Merge "Add hseog@ to OWNERS"
diff --git a/Android.bp b/Android.bp
index 75274eb..baa715e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -35,22 +35,96 @@
 }
 
 // These are subset of framework-core-sources that are needed by the
-// android.test.mock library. Ideally, the library should use public APIs only,
-// but unfortunately its API signature has some references to these private APIs.
+// android.test.mock library. The implementation of android.test.mock references
+// private members of various components to allow mocking of classes that cannot
+// be mocked without access to those internal implementation details.
 filegroup {
     name: "framework-core-sources-for-test-mock",
     srcs: [
+        "core/java/android/accounts/AccountManagerCallback.java",
+        "core/java/android/accounts/AccountManagerFuture.java",
+        "core/java/android/accounts/AccountManager.java",
+        "core/java/android/accounts/AccountsException.java",
+        "core/java/android/accounts/AuthenticatorException.java",
+        "core/java/android/accounts/OperationCanceledException.java",
+        "core/java/android/annotation/AnimatorRes.java",
+        "core/java/android/annotation/AnimRes.java",
+        "core/java/android/annotation/AnyRes.java",
+        "core/java/android/annotation/ArrayRes.java",
+        "core/java/android/annotation/AttrRes.java",
+        "core/java/android/annotation/BoolRes.java",
+        "core/java/android/annotation/BroadcastBehavior.java",
+        "core/java/android/annotation/CallbackExecutor.java",
+        "core/java/android/annotation/CallSuper.java",
+        "core/java/android/annotation/CheckResult.java",
+        "core/java/android/annotation/ColorInt.java",
+        "core/java/android/annotation/ColorRes.java",
+        "core/java/android/annotation/DimenRes.java",
+        "core/java/android/annotation/DrawableRes.java",
+        "core/java/android/annotation/FontRes.java",
+        "core/java/android/annotation/FractionRes.java",
+        "core/java/android/annotation/IntDef.java",
+        "core/java/android/annotation/IntegerRes.java",
+        "core/java/android/annotation/IntRange.java",
+        "core/java/android/annotation/LayoutRes.java",
+        "core/java/android/annotation/NonNull.java",
+        "core/java/android/annotation/Nullable.java",
+        "core/java/android/annotation/PluralsRes.java",
+        "core/java/android/annotation/RawRes.java",
+        "core/java/android/annotation/RequiresPermission.java",
+        "core/java/android/annotation/SdkConstant.java",
+        "core/java/android/annotation/Size.java",
+        "core/java/android/annotation/StringDef.java",
+        "core/java/android/annotation/StringRes.java",
+        "core/java/android/annotation/StyleableRes.java",
+        "core/java/android/annotation/StyleRes.java",
+        "core/java/android/annotation/SuppressLint.java",
+        "core/java/android/annotation/SystemApi.java",
+        "core/java/android/annotation/SystemService.java",
+        "core/java/android/annotation/TestApi.java",
+        "core/java/android/annotation/UserIdInt.java",
+        "core/java/android/annotation/XmlRes.java",
+        "core/java/android/app/Application.java",
         "core/java/android/app/IApplicationThread.aidl",
         "core/java/android/app/IServiceConnection.aidl",
+        "core/java/android/app/PackageDeleteObserver.java",
+        "core/java/android/content/ComponentCallbacks2.java",
+        "core/java/android/content/ComponentCallbacks.java",
+        "core/java/android/content/ContentInterface.java",
+        "core/java/android/content/ContentProvider.java",
+        "core/java/android/content/ContentProviderNative.java",
+        "core/java/android/content/ContentResolver.java",
+        "core/java/android/content/Context.java",
+        "core/java/android/content/ContextWrapper.java",
+        "core/java/android/content/DialogInterface.java",
         "core/java/android/content/IContentProvider.java",
-        "core/java/android/content/pm/IPackageDataObserver.aidl",
+        "core/java/android/content/Intent.java",
+        "core/java/android/content/IntentSender.java",
+        "core/java/android/content/OperationApplicationException.java",
+        "core/java/android/content/pm/ActivityInfo.java",
+        "core/java/android/content/pm/ApplicationInfo.java",
         "core/java/android/content/pm/InstantAppInfo.java",
+        "core/java/android/content/pm/IPackageDataObserver.aidl",
         "core/java/android/content/pm/KeySet.java",
         "core/java/android/content/pm/PackageManager.java",
         "core/java/android/content/pm/VerifierDeviceIdentity.java",
         "core/java/android/content/res/Resources.java",
+        "core/java/android/database/CrossProcessCursor.java",
+        "core/java/android/database/CrossProcessCursorWrapper.java",
+        "core/java/android/database/Cursor.java",
+        "core/java/android/database/CursorWrapper.java",
+        "core/java/android/os/Binder.java",
+        "core/java/android/os/Bundle.java",
+        "core/java/android/os/IBinder.java",
+        "core/java/android/os/IInterface.java",
+        "core/java/android/os/Parcelable.java",
+        "core/java/android/os/ParcelFileDescriptor.java",
+        "core/java/android/os/RemoteException.java",
         "core/java/android/os/storage/VolumeInfo.java",
+        "core/java/android/util/AndroidException.java",
         "core/java/android/view/DisplayAdjustments.java",
+        "core/java/android/view/ViewDebug.java",
+        "core/java/com/android/internal/annotations/VisibleForTesting.java",
     ],
     path: "core/java",
     visibility: ["//frameworks/base/test-mock"],
@@ -294,6 +368,17 @@
     ]
 }
 
+java_library {
+    name: "framework-updatable-stubs-module_libs_api",
+    static_libs: [
+        "framework-sdkextensions-stubs-module_libs_api",
+        "framework-tethering-stubs-module_libs_api",
+        "updatable_media_stubs",
+    ],
+    sdk_version: "module_current",
+    visibility: [":__pkg__"],
+}
+
 filegroup {
     name: "framework-all-sources",
     srcs: [
@@ -324,9 +409,11 @@
             "rs/java",
             "sax/java",
             "telecomm/java",
-            "telephony/java",
             "wifi/java",
             "wifi/aidl-export",
+
+            // TODO(b/147699819): remove this
+            "telephony/java",
         ],
     },
 }
@@ -396,7 +483,6 @@
         "app-compat-annotations",
         "ext",
         "unsupportedappusage",
-        "updatable_media_stubs",
     ],
 
     jarjar_rules: ":framework-jarjar-rules",
@@ -455,9 +541,6 @@
     name: "framework-minus-apex",
     defaults: ["framework-defaults"],
     srcs: [":framework-non-updatable-sources"],
-    libs: [
-        "framework-tethering-stubs",
-    ],
     installable: true,
     javac_shard_size: 150,
     required: [
@@ -465,6 +548,7 @@
         "libcore-platform-compat-config",
         "services-platform-compat-config",
     ],
+    libs: ["framework-updatable-stubs-module_libs_api"],
     static_libs: [
         // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
         // in favor of an API stubs dependency in java_library "framework" below.
@@ -494,9 +578,7 @@
     installable: false, // this lib is a build-only library
     static_libs: [
         "framework-minus-apex",
-        "updatable_media_stubs",
-        "framework-sdkextensions-stubs-systemapi",
-        "framework-tethering-stubs",
+        "framework-updatable-stubs-module_libs_api",
     ],
     sdk_version: "core_platform",
     apex_available: ["//apex_available:platform"],
@@ -507,52 +589,22 @@
     defaults: ["framework-defaults"],
     srcs: [":framework-all-sources"],
     installable: false,
+    static_libs: [
+        // Additional dependencies needed to build the ike API classes.
+        "ike-internals",
+    ],
     apex_available: ["//apex_available:platform"],
     visibility: [
         // DO NOT ADD ANY MORE ENTRIES TO THIS LIST
         "//external/robolectric-shadows:__subpackages__",
-        "//frameworks/base/packages/Tethering/common/TetheringLib:__subpackages__",
+        "//frameworks/base",
         "//frameworks/layoutlib:__subpackages__",
-        "//frameworks/opt/net/ike:__subpackages__",
-    ],
-}
-
-java_library {
-    name: "framework-annotation-proc",
-    defaults: ["framework-defaults"],
-    srcs: [":framework-all-sources"],
-    libs: [
-        "app-compat-annotations",
-        "unsupportedappusage",
-    ],
-    installable: false,
-    plugins: [
-        "compat-changeid-annotation-processor",
     ],
 }
 
 platform_compat_config {
-    name: "framework-platform-compat-config",
-    src: ":framework-annotation-proc",
-}
-
-// A library including just UnsupportedAppUsage.java classes.
-//
-// Provided for target so that libraries can use it without depending on
-// the whole of framework or the core platform API.
-//
-// Built for host so that the annotation processor can also use this annotation.
-java_library {
-    name: "unsupportedappusage-annotation",
-    host_supported: true,
-    srcs: [
-        "core/java/android/annotation/IntDef.java",
-    ],
-    static_libs: [
-        "art.module.api.annotations",
-    ],
-
-    sdk_version: "core_current",
+   name: "framework-platform-compat-config",
+   src: ":framework-minus-apex",
 }
 
 // A temporary build target that is conditionally included on the bootclasspath if
@@ -614,6 +666,7 @@
 filegroup {
     name: "framework-annotations",
     srcs: [
+        "core/java/android/annotation/Hide.java",
         "core/java/android/annotation/NonNull.java",
         "core/java/android/annotation/Nullable.java",
         "core/java/android/annotation/IntDef.java",
@@ -629,7 +682,7 @@
 java_library {
     name: "framework-annotations-lib",
     srcs: [ ":framework-annotations" ],
-    sdk_version: "current",
+    sdk_version: "core_current",
 }
 
 filegroup {
@@ -638,6 +691,7 @@
     srcs: [
         "core/java/android/annotation/StringDef.java",
         "core/java/android/net/annotations/PolicyDirection.java",
+        "core/java/com/android/internal/util/HexDump.java",
         "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
@@ -667,6 +721,35 @@
     ],
 }
 
+// utility classes statically linked into framework-wifi and dynamically linked
+// into wifi-service
+java_library {
+    name: "framework-wifi-util-lib",
+    sdk_version: "module_current",
+    srcs: [
+        "core/java/android/net/shared/Inet4AddressUtils.java",
+        "core/java/com/android/internal/util/Preconditions.java",
+    ],
+    libs: [
+        "framework-annotations-lib",
+        "unsupportedappusage",
+    ],
+    visibility: [
+        "//frameworks/base/wifi",
+        "//frameworks/base/services/net",
+    ],
+}
+
+filegroup {
+    name: "framework-services-net-module-wifi-shared-srcs",
+    srcs: [
+        "core/java/android/net/DhcpResults.java",
+        "core/java/android/net/shared/InetAddressUtils.java",
+        "core/java/android/net/util/IpUtils.java",
+        "core/java/android/util/LocalLog.java",
+    ],
+}
+
 // keep these files in sync with the package/Tethering/jarjar-rules.txt for the tethering module.
 filegroup {
     name: "framework-tethering-shared-srcs",
@@ -675,7 +758,6 @@
         "core/java/com/android/internal/util/IndentingPrintWriter.java",
         "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/MessageUtils.java",
-        "core/java/com/android/internal/util/Preconditions.java",
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
         "core/java/com/android/internal/util/TrafficStatsConstants.java",
@@ -731,7 +813,7 @@
         "core/proto/android/privacy.proto",
         "core/proto/android/section.proto",
     ],
-    sdk_version: "current",
+    sdk_version: "9",
     srcs: [
         "core/proto/**/*.proto",
         "libs/incident/proto/android/os/**/*.proto",
@@ -754,6 +836,7 @@
         "core/proto/android/privacy.proto",
         "core/proto/android/section.proto",
     ],
+    sdk_version: "core_current",
     // Protos have lots of MissingOverride and similar.
     errorprone: {
         javacflags: ["-XepDisableAllChecks"],
@@ -863,6 +946,7 @@
 
 aidl_interface {
     name: "libincremental_aidl",
+    unstable: true,
     srcs: [
         ":incremental_aidl",
     ],
@@ -1001,16 +1085,6 @@
     output: "framework-aidl-mappings.txt",
 }
 
-genrule {
-    name: "framework-annotation-proc-index",
-    srcs: [":framework-annotation-proc"],
-    cmd: "unzip -qp $(in) unsupportedappusage/unsupportedappusage_index.csv > $(out)",
-    out: ["unsupportedappusage_index.csv"],
-    dist: {
-        targets: ["droidcore"],
-    },
-}
-
 filegroup {
     name: "framework-cellbroadcast-shared-srcs",
     srcs: [
diff --git a/Android.mk b/Android.mk
index 09f2c40..d853248 100644
--- a/Android.mk
+++ b/Android.mk
@@ -36,13 +36,6 @@
 # always included.
 INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
 
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt)
-
 # sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
 # So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
 # $(OUT_DOCS)/offline-sdk.
@@ -67,7 +60,7 @@
 $(SDK_METADATA): $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/framework-doc-stubs-metadata.zip
 	rm -rf $(SDK_METADATA_DIR)
 	mkdir -p $(SDK_METADATA_DIR)
-	unzip -qo $< -d $(SDK_METADATA_DIR)
+	unzip -DDqo $< -d $(SDK_METADATA_DIR)
 
 .PHONY: framework-doc-stubs
 framework-doc-stubs: $(SDK_METADATA)
diff --git a/ApiDocs.bp b/ApiDocs.bp
index b7e3646..60f56de 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -65,8 +65,9 @@
         "test-base/src/**/*.java",
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
-        ":core-current-stubs-source",
-        ":core_public_api_files",
+        ":art-module-public-api-stubs-source",
+        ":conscrypt.module.public.api.stubs.source",
+        ":android_icu4j_public_api_files",
         "test-mock/src/**/*.java",
         "test-runner/src/**/*.java",
     ],
@@ -78,7 +79,7 @@
         "sdk-dir",
         "api-versions-jars-dir",
     ],
-    previous_api: ":last-released-public-api",
+    previous_api: ":android.api.public.latest",
     merge_annotations_dirs: [
         "metalava-manual",
     ],
@@ -100,7 +101,7 @@
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
-    args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) ",
+    args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
     write_sdk_values: true,
 }
 
@@ -319,7 +320,7 @@
         ":framework-doc-stubs",
     ],
     args: "-noJdkLink -links https://kotlinlang.org/api/latest/jvm/stdlib/^external/dokka/package-list " +
-    "-noStdlibLink",
+        "-noStdlibLink",
     proofread_file: "ds-dokka-proofread.txt",
     dokka_enabled: true,
 }
@@ -339,7 +340,7 @@
         targets: ["docs"],
     },
     cmd: "$(location zip2zip) -i $(location :ds-docs-kt{.docs.zip}) -o $(genDir)/ds-docs-kt-moved.zip **/*:en/reference/kotlin && " +
-         "$(location merge_zips) $(out) $(location :ds-docs-java{.docs.zip}) $(genDir)/ds-docs-kt-moved.zip",
+        "$(location merge_zips) $(out) $(location :ds-docs-java{.docs.zip}) $(genDir)/ds-docs-kt-moved.zip",
 }
 
 java_genrule {
@@ -357,10 +358,10 @@
         targets: ["docs"],
     },
     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) > /dev/null && " +
-         "$(location soong_zip) -o $(out) -C $(genDir) -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) > /dev/null && " +
+        "$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)",
 }
 
 droiddoc {
@@ -372,7 +373,6 @@
     hdf: [
         "android.whichdoc online",
     ],
-    proofread_file: "ds-static-docs-proofrerad.txt",
     args: framework_docs_only_args +
         " -staticonly " +
         " -toroot / " +
@@ -389,7 +389,6 @@
     hdf: [
         "android.whichdoc online",
     ],
-    proofread_file: "ds-ref-navtree-docs-proofrerad.txt",
     args: framework_docs_only_args +
         " -toroot / " +
         " -atLinksNavtree " +
@@ -436,4 +435,3 @@
         " -referenceonly " +
         " -title \"Android SDK - Including hidden APIs.\"",
 }
-
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 84b3625..4a77463 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -41,37 +41,43 @@
 ]
 
 stubs_defaults {
-    name: "metalava-non-updatable-api-stubs-default",
+    name: "metalava-base-api-stubs-default",
     srcs: [
         ":framework-non-updatable-sources",
         "core/java/**/*.logtags",
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
-        ":core-current-stubs-source",
-        ":core_public_api_files",
+        ":art-module-public-api-stubs-source",
+        ":android_icu4j_public_api_files",
     ],
     libs: ["framework-internal-utils"],
     installable: false,
     annotations_enabled: true,
-    previous_api: ":last-released-public-api",
+    previous_api: ":android.api.public.latest",
     merge_annotations_dirs: [
         "metalava-manual",
     ],
-    api_levels_annotations_enabled: true,
-    api_levels_annotations_dirs: [
-        "sdk-dir",
-        "api-versions-jars-dir",
-    ],
+    api_levels_annotations_enabled: false,
     filter_packages: packages_to_document,
 }
 
 stubs_defaults {
-    name: "metalava-api-stubs-default",
-    defaults: ["metalava-non-updatable-api-stubs-default"],
-    srcs: [":framework-updatable-sources"],
+    name: "metalava-full-api-stubs-default",
+    defaults: ["metalava-base-api-stubs-default"],
+    srcs: [
+        ":conscrypt.module.public.api.stubs.source",
+        ":framework-updatable-sources",
+    ],
     sdk_version: "core_platform",
 }
 
+stubs_defaults {
+    name: "metalava-non-updatable-api-stubs-default",
+    defaults: ["metalava-base-api-stubs-default"],
+    sdk_version: "core_platform",
+    libs: ["framework-all"],
+}
+
 /////////////////////////////////////////////////////////////////////
 // *-api-stubs-docs modules providing source files for the stub libraries
 /////////////////////////////////////////////////////////////////////
@@ -81,10 +87,8 @@
 // modules
 droidstubs {
     name: "api-stubs-docs",
-    defaults: ["metalava-api-stubs-default"],
-    api_filename: "public_api.txt",
-    private_api_filename: "private.txt",
-    removed_api_filename: "removed.txt",
+    defaults: ["metalava-full-api-stubs-default"],
+    removed_dex_api_filename: "removed-dex.txt",
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
@@ -95,16 +99,21 @@
             removed_api_file: "api/removed.txt",
         },
         last_released: {
-            api_file: ":last-released-public-api",
+            api_file: ":android.api.public.latest",
             removed_api_file: "api/removed.txt",
             baseline_file: ":public-api-incompatibilities-with-last-released",
         },
         api_lint: {
             enabled: true,
-            new_since: ":last-released-public-api",
+            new_since: ":android.api.public.latest",
             baseline_file: "api/lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/public/api",
+        dest: "android.txt",
+    },
     jdiff_enabled: true,
 }
 
@@ -120,12 +129,8 @@
 
 droidstubs {
     name: "system-api-stubs-docs",
-    defaults: ["metalava-api-stubs-default"],
-    api_tag_name: "SYSTEM",
-    api_filename: "system-api.txt",
-    private_api_filename: "system-private.txt",
-    private_dex_api_filename: "system-private-dex.txt",
-    removed_api_filename: "system-removed.txt",
+    defaults: ["metalava-full-api-stubs-default"],
+    removed_dex_api_filename: "system-removed-dex.txt",
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
@@ -136,25 +141,27 @@
             removed_api_file: "api/system-removed.txt",
         },
         last_released: {
-            api_file: ":last-released-system-api",
+            api_file: ":android.api.system.latest",
             removed_api_file: "api/system-removed.txt",
             baseline_file: ":system-api-incompatibilities-with-last-released"
         },
         api_lint: {
             enabled: true,
-            new_since: ":last-released-system-api",
+            new_since: ":android.api.system.latest",
             baseline_file: "api/system-lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/system/api",
+        dest: "android.txt",
+    },
     jdiff_enabled: true,
 }
 
 droidstubs {
     name: "test-api-stubs-docs",
-    defaults: ["metalava-api-stubs-default"],
-    api_tag_name: "TEST",
-    api_filename: "test-api.txt",
-    removed_api_filename: "test-removed.txt",
+    defaults: ["metalava-full-api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
@@ -169,6 +176,11 @@
             baseline_file: "api/test-lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/test/api",
+        dest: "android.txt",
+    },
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -184,25 +196,34 @@
 
 droidstubs {
     name: "module-lib-api",
-    defaults: ["metalava-api-stubs-default"],
+    defaults: ["metalava-full-api-stubs-default"],
     arg_files: ["core/res/AndroidManifest.xml"],
     args: metalava_framework_docs_args + module_libs,
+
+    // Do not generate stubs as they are not needed
+    generate_stubs: false,
+
     check_api: {
         current: {
             api_file: "api/module-lib-current.txt",
             removed_api_file: "api/module-lib-removed.txt",
         },
         last_released: {
-            api_file: ":last-released-module-lib-api",
+            api_file: ":android.api.module-lib.latest",
             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",
+            new_since: ":android.api.module-lib.latest",
             baseline_file: "api/module-lib-lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/module-lib/api",
+        dest: "android.txt",
+    },
 }
 
 
@@ -212,7 +233,7 @@
 
 droidstubs {
     name: "module-lib-api-stubs-docs",
-    defaults: ["metalava-api-stubs-default"],
+    defaults: ["metalava-non-updatable-api-stubs-default"],
     arg_files: ["core/res/AndroidManifest.xml"],
     args: metalava_framework_docs_args + priv_apps + module_libs,
 }
@@ -223,18 +244,20 @@
 /////////////////////////////////////////////////////////////////////
 
 java_defaults {
-    name: "framework-stubs-default",
+    name: "android_defaults_stubs_current",
     libs: [ "stub-annotations" ],
-    static_libs: [ "private-stub-annotations-jar" ],
-    sdk_version: "core_current",
+    static_libs: [
+        "private-stub-annotations-jar",
+
+        // License notices from art module
+        "art-notices-for-framework-stubs-jar",
+    ],
     errorprone: {
         javacflags: [
             "-XepDisableAllChecks",
         ],
     },
-    java_resources: [
-        ":notices-for-framework-stubs",
-    ],
+    sdk_version: "none",
     system_modules: "none",
     java_version: "1.8",
     compile_dex: true,
@@ -243,25 +266,26 @@
 java_library_static {
     name: "android_stubs_current",
     srcs: [ ":api-stubs-docs" ],
-    defaults: ["framework-stubs-default"],
+    defaults: ["android_defaults_stubs_current"],
 }
 
 java_library_static {
     name: "android_system_stubs_current",
     srcs: [ ":system-api-stubs-docs" ],
-    defaults: ["framework-stubs-default"],
+    defaults: ["android_defaults_stubs_current"],
 }
 
 java_library_static {
     name: "android_test_stubs_current",
     srcs: [ ":test-api-stubs-docs" ],
-    defaults: ["framework-stubs-default"],
+    defaults: ["android_defaults_stubs_current"],
 }
 
 java_library_static {
     name: "android_module_lib_stubs_current",
     srcs: [ ":module-lib-api-stubs-docs" ],
-    defaults: ["framework-stubs-default"],
+    defaults: ["android_defaults_stubs_current"],
+    libs: ["android_system_stubs_current"],
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -292,7 +316,7 @@
     installable: false,
     sdk_version: "core_platform",
     annotations_enabled: true,
-    previous_api: ":last-released-public-api",
+    previous_api: ":android.api.public.latest",
     merge_annotations_dirs: [
         "metalava-manual",
     ],
@@ -308,44 +332,6 @@
 }
 
 /////////////////////////////////////////////////////////////////////
-// Stubs for hiddenapi processing.
-/////////////////////////////////////////////////////////////////////
-
-droidstubs {
-    name: "hiddenapi-lists-docs",
-    defaults: ["metalava-api-stubs-default"],
-    arg_files: [
-        "core/res/AndroidManifest.xml",
-    ],
-    dex_api_filename: "public-dex.txt",
-    private_dex_api_filename: "private-dex.txt",
-    removed_dex_api_filename: "removed-dex.txt",
-    args: metalava_framework_docs_args +
-        " --show-unannotated " +
-        priv_apps +
-        " --show-annotation android.annotation.TestApi ",
-}
-
-droidstubs {
-    name: "hiddenapi-mappings",
-    defaults: ["metalava-api-stubs-default"],
-    srcs: [
-        ":opt-telephony-common-srcs",
-    ],
-
-    arg_files: [
-        "core/res/AndroidManifest.xml",
-    ],
-    dex_mapping_filename: "dex-mapping.txt",
-    args: metalava_framework_docs_args +
-        " --hide ReferencesHidden " +
-        " --hide UnhiddenSystemApi " +
-        " --show-unannotated " +
-        priv_apps +
-        " --show-annotation android.annotation.TestApi ",
-}
-
-/////////////////////////////////////////////////////////////////////
 // api/*-current.txt files for use by modules in other directories
 // like the CTS test
 /////////////////////////////////////////////////////////////////////
diff --git a/apex/Android.bp b/apex/Android.bp
index 051986e..3a63c805 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -26,8 +26,24 @@
     "--hide Typo " +
     "--hide UnavailableSymbol "
 
-// TODO: remove this server classes are cleaned up.
-mainline_stubs_args += "--hide-package com.android.server "
+// TODO: modularize this so not every module has the same whitelist
+framework_packages_to_document = [
+    "android",
+    "dalvik",
+    "java",
+    "javax",
+    "junit",
+    "org.apache.http",
+    "org.json",
+    "org.w3c.dom",
+    "org.xml.sax",
+    "org.xmlpull",
+]
+
+// TODO: remove the hiding when server classes are cleaned up.
+mainline_framework_stubs_args =
+    mainline_stubs_args +
+    "--hide-package com.android.server "
 
 priv_apps = " " +
     "--show-annotation android.annotation.SystemApi\\(" +
@@ -39,17 +55,116 @@
         "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
     "\\) "
 
+mainline_service_stubs_args =
+    mainline_stubs_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
+
+// Defaults for mainline module provided java_sdk_library instances.
+java_defaults {
+    name: "framework-module-defaults",
+
+    // Additional annotations used for compiling both the implementation and the
+    // stubs libraries.
+    libs: ["framework-annotations-lib"],
+
+    // Enable api lint. This will eventually become the default for java_sdk_library
+    // but it cannot yet be turned on because some usages have not been cleaned up.
+    // TODO(b/156126315) - Remove when no longer needed.
+    api_lint: {
+        enabled: true,
+    },
+
+    // The API scope specific properties.
+    public: {
+        enabled: true,
+        sdk_version: "module_current",
+    },
+    system: {
+        enabled: true,
+        sdk_version: "module_current",
+    },
+    module_lib: {
+        enabled: true,
+        sdk_version: "module_current",
+    },
+
+    // Configure framework module specific metalava options.
+    droiddoc_options: [mainline_stubs_args],
+
+    // The stub libraries must be visible to frameworks/base so they can be combined
+    // into API specific libraries.
+    stubs_library_visibility: [
+        "//frameworks/base", // Framework
+    ],
+
+    // Set the visibility of the modules creating the stubs source.
+    stubs_source_visibility: [
+        // Ignore any visibility rules specified on the java_sdk_library when
+        // setting the visibility of the stubs source modules.
+        "//visibility:override",
+
+        // Currently, the stub source is not required for anything other than building
+        // the stubs library so is private to avoid misuse.
+        "//visibility:private",
+    ],
+
+    // Collates API usages from each module for further analysis.
+    plugins: ["java_api_finder"],
+
+    // Mainline modules should only rely on 'module_lib' APIs provided by other modules
+    // and the non updatable parts of the platform.
+    sdk_version: "module_current",
+}
+
 stubs_defaults {
     name: "framework-module-stubs-defaults-publicapi",
-    args: mainline_stubs_args,
+    args: mainline_framework_stubs_args,
     installable: false,
+    sdk_version: "current",
+    filter_packages: framework_packages_to_document,
+    check_api: {
+        current: {
+            api_file: "api/current.txt",
+            removed_api_file: "api/removed.txt",
+        },
+    },
 }
 
 stubs_defaults {
     name: "framework-module-stubs-defaults-systemapi",
-    args: mainline_stubs_args + priv_apps,
-    srcs: [":framework-annotations"],
+    args: mainline_framework_stubs_args + priv_apps,
+    libs: ["framework-annotations-lib"],
     installable: false,
+    sdk_version: "system_current",
+    filter_packages: framework_packages_to_document,
+    check_api: {
+        current: {
+            api_file: "api/system-current.txt",
+            removed_api_file: "api/system-removed.txt",
+        },
+    },
+}
+
+java_defaults {
+    name: "framework-module-stubs-lib-defaults-publicapi",
+    installable: false,
+    sdk_version: "module_current",
+}
+
+java_defaults {
+    name: "framework-module-stubs-lib-defaults-systemapi",
+    installable: false,
+    sdk_version: "module_current",
+}
+
+java_defaults {
+    name: "framework-module-stubs-lib-defaults-module_libs_api",
+    installable: false,
+    sdk_version: "module_current",
 }
 
 // The defaults for module_libs comes in two parts - defaults for API checks
@@ -59,14 +174,47 @@
 
 stubs_defaults {
     name: "framework-module-api-defaults-module_libs_api",
-    args: mainline_stubs_args + module_libs,
-    srcs: [":framework-annotations"],
+    args: mainline_framework_stubs_args + module_libs,
+    libs: ["framework-annotations-lib"],
     installable: false,
+    sdk_version: "module_current",
+    filter_packages: framework_packages_to_document,
+
+    // Do not generate stubs as they are not needed
+    generate_stubs: false,
+
+    check_api: {
+        current: {
+            api_file: "api/module-lib-current.txt",
+            removed_api_file: "api/module-lib-removed.txt",
+        },
+    },
 }
 
 stubs_defaults {
     name: "framework-module-stubs-defaults-module_libs_api",
-    args: mainline_stubs_args + module_libs + priv_apps,
-    srcs: [":framework-annotations"],
+    args: mainline_framework_stubs_args + module_libs + priv_apps,
+    libs: ["framework-annotations-lib"],
     installable: false,
+    sdk_version: "module_current",
+    filter_packages: framework_packages_to_document,
+}
+
+stubs_defaults {
+    name: "service-module-stubs-srcs-defaults",
+    args: mainline_service_stubs_args,
+    installable: false,
+    filter_packages: ["com.android."],
+    check_api: {
+        current: {
+            api_file: "api/current.txt",
+            removed_api_file: "api/removed.txt",
+        },
+    },
+}
+
+// Empty for now, but a convenient place to add rules for all
+// module java_library system_server stub libs.
+java_defaults {
+    name: "service-module-stubs-defaults",
 }
diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp
index a22a948..fdb078e 100644
--- a/apex/sdkextensions/Android.bp
+++ b/apex/sdkextensions/Android.bp
@@ -28,6 +28,7 @@
 apex_defaults {
     name: "com.android.sdkext-defaults",
     updatable: true,
+    min_sdk_version: "R",
     java_libs: [ "framework-sdkextensions" ],
     prebuilts: [
         "derive_sdk.rc",
@@ -38,7 +39,7 @@
 
 sdk {
     name: "sdkextensions-sdk",
-    java_header_libs: [ "framework-sdkextensions-stubs-systemapi" ],
+    java_sdk_libs: [ "framework-sdkextensions" ],
 }
 
 apex_key {
diff --git a/apex/sdkextensions/TEST_MAPPING b/apex/sdkextensions/TEST_MAPPING
index 4e18833..3dc1b9f 100644
--- a/apex/sdkextensions/TEST_MAPPING
+++ b/apex/sdkextensions/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "CtsSdkExtensionsTestCases"
     },
     {
-      "name": "apiextensions_e2e_tests"
+      "name": "sdkextensions_e2e_tests"
     }
   ]
 }
diff --git a/apex/sdkextensions/derive_sdk/Android.bp b/apex/sdkextensions/derive_sdk/Android.bp
index cf49902..41eae09 100644
--- a/apex/sdkextensions/derive_sdk/Android.bp
+++ b/apex/sdkextensions/derive_sdk/Android.bp
@@ -20,14 +20,13 @@
     ],
     proto: {
         type: "lite",
+        static: true,
     },
-    sdk_version: "current",
+    min_sdk_version: "current",
+    shared_libs: ["liblog"],
+    // static c++/libbase for smaller size
     stl: "c++_static",
-    shared_libs: [ "liblog" ],
-    static_libs: [
-        "libbase_ndk",
-        "libprotobuf-cpp-lite-ndk",
-    ],
+    static_libs: ["libbase"],
 }
 
 cc_binary {
@@ -45,7 +44,8 @@
     compile_multilib: "prefer32",
     stem: "derive_sdk",
     apex_available: [ "test_com.android.sdkext" ],
-    visibility: [ "//frameworks/base/apex/sdkextensions/testing" ]
+    visibility: [ "//frameworks/base/apex/sdkextensions/testing" ],
+    installable: false,
 }
 
 prebuilt_etc {
diff --git a/apex/sdkextensions/derive_sdk/derive_sdk.rc b/apex/sdkextensions/derive_sdk/derive_sdk.rc
index 1b66794..18f021c 100644
--- a/apex/sdkextensions/derive_sdk/derive_sdk.rc
+++ b/apex/sdkextensions/derive_sdk/derive_sdk.rc
@@ -1,3 +1,5 @@
 service derive_sdk /apex/com.android.sdkext/bin/derive_sdk
+    user nobody
+    group nobody
     oneshot
     disabled
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 86f4ab7..b8aad7d 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -25,14 +25,18 @@
     visibility: [ "//frameworks/base" ] // For the "global" stubs.
 }
 
-java_library {
+java_sdk_library {
     name: "framework-sdkextensions",
     srcs: [ ":framework-sdkextensions-sources" ],
-    sdk_version: "system_current",
-    libs: [ "framework-annotations-lib" ],
+    defaults: ["framework-module-defaults"],
+
+    // TODO(b/155480189) - Remove naming_scheme once references have been resolved.
+    // Temporary java_sdk_library component naming scheme to use to ease the transition from separate
+    // modules to java_sdk_library.
+    naming_scheme: "framework-modules",
+
     permitted_packages: [ "android.os.ext" ],
     installable: true,
-    plugins: ["java_api_finder"],
     visibility: [
         "//frameworks/base/apex/sdkextensions",
         "//frameworks/base/apex/sdkextensions/testing",
@@ -43,72 +47,3 @@
         "test_com.android.sdkext",
     ],
 }
-
-stubs_defaults {
-    name: "framework-sdkextensions-stubs-defaults",
-    srcs: [ ":framework-sdkextensions-sources" ],
-    libs: [ "framework-annotations-lib" ],
-    sdk_version: "system_current",
-}
-
-droidstubs {
-    name: "framework-sdkextensions-stubs-srcs-publicapi",
-    defaults: [
-        "framework-module-stubs-defaults-publicapi",
-        "framework-sdkextensions-stubs-defaults",
-    ]
-}
-
-droidstubs {
-    name: "framework-sdkextensions-stubs-srcs-systemapi",
-    defaults: [
-        "framework-module-stubs-defaults-systemapi",
-        "framework-sdkextensions-stubs-defaults",
-    ]
-}
-
-droidstubs {
-    name: "framework-sdkextensions-api-module_libs_api",
-    defaults: [
-        "framework-module-api-defaults-module_libs_api",
-        "framework-sdkextensions-stubs-defaults",
-    ]
-}
-
-droidstubs {
-    name: "framework-sdkextensions-stubs-srcs-module_libs_api",
-    defaults: [
-        "framework-module-stubs-defaults-module_libs_api",
-        "framework-sdkextensions-stubs-defaults",
-    ]
-}
-
-java_library {
-    name: "framework-sdkextensions-stubs-publicapi",
-    srcs: [":framework-sdkextensions-stubs-srcs-publicapi"],
-    sdk_version: "current",
-    visibility: [
-        "//frameworks/base", // Framework
-        "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
-    ]
-}
-
-java_library {
-    name: "framework-sdkextensions-stubs-systemapi",
-    srcs: [":framework-sdkextensions-stubs-srcs-systemapi"],
-    sdk_version: "system_current",
-    visibility: [
-        "//frameworks/base", // Framework
-        "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
-    ]
-}
-
-java_library {
-    name: "framework-sdkextensions-stubs-module_libs_api",
-    srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"],
-    sdk_version: "system_current",
-    visibility: [
-        "//frameworks/base", // Framework
-        "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
-    ]
-}
diff --git a/apex/sdkextensions/framework/api/current.txt b/apex/sdkextensions/framework/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/sdkextensions/framework/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/module-lib-current.txt b/apex/sdkextensions/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/sdkextensions/framework/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/module-lib-removed.txt b/apex/sdkextensions/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/sdkextensions/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/removed.txt b/apex/sdkextensions/framework/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/sdkextensions/framework/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/system-current.txt b/apex/sdkextensions/framework/api/system-current.txt
new file mode 100644
index 0000000..bbff4c5
--- /dev/null
+++ b/apex/sdkextensions/framework/api/system-current.txt
@@ -0,0 +1,9 @@
+// Signature format: 2.0
+package android.os.ext {
+
+  public class SdkExtensions {
+    method public static int getExtensionVersion(int);
+  }
+
+}
+
diff --git a/apex/sdkextensions/framework/api/system-removed.txt b/apex/sdkextensions/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/sdkextensions/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
index 103b53e..c268ff4 100644
--- a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
@@ -62,7 +62,11 @@
         if (sdk < VERSION_CODES.R) {
             throw new IllegalArgumentException(String.valueOf(sdk) + " does not have extensions");
         }
-        return R_EXTENSION_INT;
+
+        if (sdk == VERSION_CODES.R) {
+            return R_EXTENSION_INT;
+        }
+        return 0;
     }
 
 }
diff --git a/api/current.txt b/api/current.txt
index 85771fe..8163426 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9849,7 +9849,6 @@
     field public static final String MEDIA_ROUTER_SERVICE = "media_router";
     field public static final String MEDIA_SESSION_SERVICE = "media_session";
     field public static final String MIDI_SERVICE = "midi";
-    field public static final String MMS_SERVICE = "mms";
     field public static final int MODE_APPEND = 32768; // 0x8000
     field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -11405,7 +11404,8 @@
   public class PackageInstaller {
     method public void abandonSession(int);
     method public int createSession(@NonNull android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException;
-    method @Nullable public android.content.pm.PackageInstaller.SessionInfo getActiveStagedSession();
+    method @Deprecated @Nullable public android.content.pm.PackageInstaller.SessionInfo getActiveStagedSession();
+    method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getActiveStagedSessions();
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllSessions();
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
     method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
@@ -11490,11 +11490,13 @@
     method @NonNull public String getStagedSessionErrorMessage();
     method public long getUpdatedMillis();
     method @NonNull public android.os.UserHandle getUser();
+    method public boolean hasParentSessionId();
     method public boolean isActive();
     method public boolean isCommitted();
     method public boolean isMultiPackage();
     method public boolean isSealed();
     method public boolean isStaged();
+    method public boolean isStagedSessionActive();
     method public boolean isStagedSessionApplied();
     method public boolean isStagedSessionFailed();
     method public boolean isStagedSessionReady();
@@ -19555,8 +19557,7 @@
     method public T numberFormatterSecond(android.icu.number.UnlocalizedNumberFormatter);
   }
 
-  public abstract class Precision implements java.lang.Cloneable {
-    method public Object clone();
+  public abstract class Precision {
     method public static android.icu.number.CurrencyPrecision currency(android.icu.util.Currency.CurrencyUsage);
     method public static android.icu.number.FractionPrecision fixedFraction(int);
     method public static android.icu.number.Precision fixedSignificantDigits(int);
@@ -19579,8 +19580,7 @@
     method public static android.icu.number.Scale powerOfTen(int);
   }
 
-  public class ScientificNotation extends android.icu.number.Notation implements java.lang.Cloneable {
-    method public Object clone();
+  public class ScientificNotation extends android.icu.number.Notation {
     method public android.icu.number.ScientificNotation withExponentSignDisplay(android.icu.number.NumberFormatter.SignDisplay);
     method public android.icu.number.ScientificNotation withMinExponentDigits(int);
   }
@@ -28708,7 +28708,7 @@
 
   public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
     ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
-    method public void onConnectivityReport(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
+    method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
     method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
     method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
   }
@@ -29145,9 +29145,6 @@
   public final class NetworkCapabilities implements android.os.Parcelable {
     ctor public NetworkCapabilities();
     ctor public NetworkCapabilities(android.net.NetworkCapabilities);
-    method @NonNull public android.net.NetworkCapabilities addCapability(int);
-    method @NonNull public android.net.NetworkCapabilities addTransportType(int);
-    method public void clearAll();
     method public int describeContents();
     method public int getLinkDownstreamBandwidthKbps();
     method public int getLinkUpstreamBandwidthKbps();
@@ -29157,13 +29154,6 @@
     method @Nullable public android.net.TransportInfo getTransportInfo();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
-    method @NonNull public android.net.NetworkCapabilities removeCapability(int);
-    method @NonNull public android.net.NetworkCapabilities setCapability(int, boolean);
-    method @NonNull public android.net.NetworkCapabilities setLinkDownstreamBandwidthKbps(int);
-    method @NonNull public android.net.NetworkCapabilities setLinkUpstreamBandwidthKbps(int);
-    method @NonNull public android.net.NetworkCapabilities setNetworkSpecifier(@NonNull android.net.NetworkSpecifier);
-    method @NonNull public android.net.NetworkCapabilities setOwnerUid(int);
-    method @NonNull public android.net.NetworkCapabilities setSignalStrength(int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
     field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11
@@ -29185,6 +29175,7 @@
     field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
     field public static final int NET_CAPABILITY_RCS = 8; // 0x8
     field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
+    field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19
     field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe
     field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10
     field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6
@@ -29246,6 +29237,7 @@
   }
 
   public class NetworkRequest implements android.os.Parcelable {
+    method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities);
     method public int describeContents();
     method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
     method public boolean hasCapability(int);
@@ -31171,7 +31163,7 @@
     method public boolean categoryAllowsForegroundPreference(String);
     method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public java.util.List<java.lang.String> getAidsForPreferredPaymentService();
     method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, String);
-    method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getDescriptionForPreferredPaymentService();
+    method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public CharSequence getDescriptionForPreferredPaymentService();
     method public static android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter);
     method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getRouteDestinationForPreferredPaymentService();
     method public int getSelectionModeForCategory(String);
@@ -34547,6 +34539,7 @@
     field public static final String PRODUCT;
     field @Deprecated public static final String RADIO;
     field @Deprecated public static final String SERIAL;
+    field @NonNull public static final String SKU;
     field public static final String[] SUPPORTED_32_BIT_ABIS;
     field public static final String[] SUPPORTED_64_BIT_ABIS;
     field public static final String[] SUPPORTED_ABIS;
@@ -36024,6 +36017,7 @@
     method public boolean isAllocationSupported(@NonNull java.io.FileDescriptor);
     method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
     method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
+    method public boolean isCheckpointSupported();
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(String);
     method public boolean mountObb(String, String, android.os.storage.OnObbStateChangeListener);
@@ -41310,6 +41304,7 @@
     method public int getPurposes();
     method @NonNull public String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
+    method public boolean isDevicePropertiesAttestationIncluded();
     method @NonNull public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
@@ -41331,6 +41326,7 @@
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(@NonNull java.util.Date);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateSerialNumber(@NonNull java.math.BigInteger);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateSubject(@NonNull javax.security.auth.x500.X500Principal);
+    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setDevicePropertiesAttestationIncluded(boolean);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setDigests(java.lang.String...);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setEncryptionPaddings(java.lang.String...);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setInvalidatedByBiometricEnrollment(boolean);
@@ -44069,13 +44065,9 @@
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public void onConnectionServiceFocusGained();
     method public void onConnectionServiceFocusLost();
-    method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
-    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -44565,14 +44557,26 @@
     field public static final int BAND_46 = 46; // 0x2e
     field public static final int BAND_47 = 47; // 0x2f
     field public static final int BAND_48 = 48; // 0x30
+    field public static final int BAND_49 = 49; // 0x31
     field public static final int BAND_5 = 5; // 0x5
+    field public static final int BAND_50 = 50; // 0x32
+    field public static final int BAND_51 = 51; // 0x33
+    field public static final int BAND_52 = 52; // 0x34
+    field public static final int BAND_53 = 53; // 0x35
     field public static final int BAND_6 = 6; // 0x6
     field public static final int BAND_65 = 65; // 0x41
     field public static final int BAND_66 = 66; // 0x42
     field public static final int BAND_68 = 68; // 0x44
     field public static final int BAND_7 = 7; // 0x7
     field public static final int BAND_70 = 70; // 0x46
+    field public static final int BAND_71 = 71; // 0x47
+    field public static final int BAND_72 = 72; // 0x48
+    field public static final int BAND_73 = 73; // 0x49
+    field public static final int BAND_74 = 74; // 0x4a
     field public static final int BAND_8 = 8; // 0x8
+    field public static final int BAND_85 = 85; // 0x55
+    field public static final int BAND_87 = 87; // 0x57
+    field public static final int BAND_88 = 88; // 0x58
     field public static final int BAND_9 = 9; // 0x9
   }
 
@@ -44636,7 +44640,13 @@
     field public static final int BAND_83 = 83; // 0x53
     field public static final int BAND_84 = 84; // 0x54
     field public static final int BAND_86 = 86; // 0x56
+    field public static final int BAND_89 = 89; // 0x59
     field public static final int BAND_90 = 90; // 0x5a
+    field public static final int BAND_91 = 91; // 0x5b
+    field public static final int BAND_92 = 92; // 0x5c
+    field public static final int BAND_93 = 93; // 0x5d
+    field public static final int BAND_94 = 94; // 0x5e
+    field public static final int BAND_95 = 95; // 0x5f
   }
 
   public static final class AccessNetworkConstants.UtranBand {
@@ -44682,6 +44692,38 @@
     field public static final int PRIORITY_MED = 2; // 0x2
   }
 
+  public final class BarringInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.telephony.BarringInfo.BarringServiceInfo getBarringServiceInfo(int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int BARRING_SERVICE_TYPE_CS_FALLBACK = 5; // 0x5
+    field public static final int BARRING_SERVICE_TYPE_CS_SERVICE = 0; // 0x0
+    field public static final int BARRING_SERVICE_TYPE_CS_VOICE = 2; // 0x2
+    field public static final int BARRING_SERVICE_TYPE_EMERGENCY = 8; // 0x8
+    field public static final int BARRING_SERVICE_TYPE_MMTEL_VIDEO = 7; // 0x7
+    field public static final int BARRING_SERVICE_TYPE_MMTEL_VOICE = 6; // 0x6
+    field public static final int BARRING_SERVICE_TYPE_MO_DATA = 4; // 0x4
+    field public static final int BARRING_SERVICE_TYPE_MO_SIGNALLING = 3; // 0x3
+    field public static final int BARRING_SERVICE_TYPE_PS_SERVICE = 1; // 0x1
+    field public static final int BARRING_SERVICE_TYPE_SMS = 9; // 0x9
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.BarringInfo> CREATOR;
+  }
+
+  public static final class BarringInfo.BarringServiceInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getBarringType();
+    method public int getConditionalBarringFactor();
+    method public int getConditionalBarringTimeSeconds();
+    method public boolean isBarred();
+    method public boolean isConditionallyBarred();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int BARRING_TYPE_CONDITIONAL = 1; // 0x1
+    field public static final int BARRING_TYPE_NONE = 0; // 0x0
+    field public static final int BARRING_TYPE_UNCONDITIONAL = 2; // 0x2
+    field public static final int BARRING_TYPE_UNKNOWN = -1; // 0xffffffff
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.BarringInfo.BarringServiceInfo> CREATOR;
+  }
+
   public class CarrierConfigManager {
     method @Nullable public android.os.PersistableBundle getConfig();
     method @Nullable public android.os.PersistableBundle getConfigByComponentForSubId(@NonNull String, int);
@@ -44694,12 +44736,9 @@
     field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
     field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
     field public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int";
-    field public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string";
-    field public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT = "5g_icon_display_grace_period_sec_int";
     field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array";
     field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array";
     field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array";
-    field public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long";
     field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -44892,7 +44931,6 @@
     field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
     field public static final String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL = "show_blocking_pay_phone_option_bool";
     field public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool";
-    field public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING = "show_carrier_data_icon_pattern_string";
     field public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
     field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
     field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool";
@@ -44967,7 +45005,7 @@
   }
 
   public final class CellIdentityGsm extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
     method public int getArfcn();
     method public int getBsic();
     method public int getCid();
@@ -44983,8 +45021,8 @@
   }
 
   public final class CellIdentityLte extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
-    method @NonNull public java.util.List<java.lang.Integer> getBands();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
+    method @NonNull public int[] getBands();
     method public int getBandwidth();
     method public int getCi();
     method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
@@ -45001,8 +45039,8 @@
   }
 
   public final class CellIdentityNr extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
-    method @NonNull public java.util.List<java.lang.Integer> getBands();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
+    method @NonNull public int[] getBands();
     method @Nullable public String getMccString();
     method @Nullable public String getMncString();
     method public long getNci();
@@ -45014,7 +45052,7 @@
   }
 
   public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
     method public int getCid();
     method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
     method public int getCpid();
@@ -45028,7 +45066,7 @@
   }
 
   public final class CellIdentityWcdma extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
     method public int getCid();
     method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
     method public int getLac();
@@ -45209,17 +45247,85 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ClosedSubscriberGroupInfo> CREATOR;
   }
 
-  public final class DisplayInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getNetworkType();
-    method public int getOverrideNetworkType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DisplayInfo> CREATOR;
-    field public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; // 0x2
-    field public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1; // 0x1
-    field public static final int OVERRIDE_NETWORK_TYPE_NONE = 0; // 0x0
-    field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3; // 0x3
-    field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; // 0x4
+  public final class DisconnectCause {
+    field public static final int ALREADY_DIALING = 72; // 0x48
+    field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
+    field public static final int BUSY = 4; // 0x4
+    field public static final int CALLING_DISABLED = 74; // 0x4a
+    field public static final int CALL_BARRED = 20; // 0x14
+    field public static final int CALL_PULLED = 51; // 0x33
+    field public static final int CANT_CALL_WHILE_RINGING = 73; // 0x49
+    field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
+    field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
+    field public static final int CDMA_ALREADY_ACTIVATED = 49; // 0x31
+    field public static final int CDMA_DROP = 27; // 0x1b
+    field public static final int CDMA_INTERCEPT = 28; // 0x1c
+    field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
+    field public static final int CDMA_NOT_EMERGENCY = 34; // 0x22
+    field public static final int CDMA_PREEMPTED = 33; // 0x21
+    field public static final int CDMA_REORDER = 29; // 0x1d
+    field public static final int CDMA_RETRY_ORDER = 31; // 0x1f
+    field public static final int CDMA_SO_REJECT = 30; // 0x1e
+    field public static final int CONGESTION = 5; // 0x5
+    field public static final int CS_RESTRICTED = 22; // 0x16
+    field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
+    field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
+    field public static final int DATA_DISABLED = 54; // 0x36
+    field public static final int DATA_LIMIT_REACHED = 55; // 0x37
+    field public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57; // 0x39
+    field public static final int DIALED_MMI = 39; // 0x27
+    field public static final int DIAL_LOW_BATTERY = 62; // 0x3e
+    field public static final int DIAL_MODIFIED_TO_DIAL = 48; // 0x30
+    field public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; // 0x42
+    field public static final int DIAL_MODIFIED_TO_SS = 47; // 0x2f
+    field public static final int DIAL_MODIFIED_TO_USSD = 46; // 0x2e
+    field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; // 0x45
+    field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; // 0x46
+    field public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; // 0x43
+    field public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; // 0x44
+    field public static final int EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE = 78; // 0x4e
+    field public static final int EMERGENCY_PERM_FAILURE = 64; // 0x40
+    field public static final int EMERGENCY_TEMP_FAILURE = 63; // 0x3f
+    field public static final int ERROR_UNSPECIFIED = 36; // 0x24
+    field public static final int FDN_BLOCKED = 21; // 0x15
+    field public static final int ICC_ERROR = 19; // 0x13
+    field public static final int IMEI_NOT_ACCEPTED = 58; // 0x3a
+    field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c
+    field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d
+    field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47
+    field public static final int INCOMING_MISSED = 1; // 0x1
+    field public static final int INCOMING_REJECTED = 16; // 0x10
+    field public static final int INVALID_CREDENTIALS = 10; // 0xa
+    field public static final int INVALID_NUMBER = 7; // 0x7
+    field public static final int LIMIT_EXCEEDED = 15; // 0xf
+    field public static final int LOCAL = 3; // 0x3
+    field public static final int LOST_SIGNAL = 14; // 0xe
+    field public static final int LOW_BATTERY = 61; // 0x3d
+    field public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; // 0x35
+    field public static final int MEDIA_TIMEOUT = 77; // 0x4d
+    field public static final int MMI = 6; // 0x6
+    field public static final int NORMAL = 2; // 0x2
+    field public static final int NORMAL_UNSPECIFIED = 65; // 0x41
+    field public static final int NOT_DISCONNECTED = 0; // 0x0
+    field public static final int NOT_VALID = -1; // 0xffffffff
+    field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
+    field public static final int NUMBER_UNREACHABLE = 8; // 0x8
+    field public static final int OTASP_PROVISIONING_IN_PROCESS = 76; // 0x4c
+    field public static final int OUTGOING_CANCELED = 44; // 0x2c
+    field public static final int OUTGOING_EMERGENCY_CALL_PLACED = 80; // 0x50
+    field public static final int OUTGOING_FAILURE = 43; // 0x2b
+    field public static final int OUT_OF_NETWORK = 11; // 0xb
+    field public static final int OUT_OF_SERVICE = 18; // 0x12
+    field public static final int POWER_OFF = 17; // 0x11
+    field public static final int SERVER_ERROR = 12; // 0xc
+    field public static final int SERVER_UNREACHABLE = 9; // 0x9
+    field public static final int TIMED_OUT = 13; // 0xd
+    field public static final int TOO_MANY_ONGOING_CALLS = 75; // 0x4b
+    field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
+    field public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50; // 0x32
+    field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
+    field public static final int WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 79; // 0x4f
+    field public static final int WIFI_LOST = 59; // 0x3b
   }
 
   public class IccOpenLogicalChannelResponse implements android.os.Parcelable {
@@ -45238,12 +45344,14 @@
 
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
     method public void addProgressListener(@NonNull android.telephony.mbms.DownloadRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.mbms.DownloadProgressListener);
+    method public void addServiceAnnouncementFile(@NonNull byte[]);
     method public void addStatusListener(@NonNull android.telephony.mbms.DownloadRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.mbms.DownloadStatusListener);
     method public void cancelDownload(@NonNull android.telephony.mbms.DownloadRequest);
     method public void close();
     method public static android.telephony.MbmsDownloadSession create(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.mbms.MbmsDownloadSessionCallback);
     method @Nullable public static android.telephony.MbmsDownloadSession create(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, int, @NonNull android.telephony.mbms.MbmsDownloadSessionCallback);
     method public void download(@NonNull android.telephony.mbms.DownloadRequest);
+    method public static int getMaximumServiceAnnouncementFileSize();
     method @Nullable public java.io.File getTempFileRootDirectory();
     method @NonNull public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
     method public void removeProgressListener(@NonNull android.telephony.mbms.DownloadRequest, @NonNull android.telephony.mbms.DownloadProgressListener);
@@ -45287,11 +45395,6 @@
     method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
   }
 
-  public class MmsManager {
-    method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
-    method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
-  }
-
   @Deprecated public class NeighboringCellInfo implements android.os.Parcelable {
     ctor @Deprecated public NeighboringCellInfo();
     ctor @Deprecated public NeighboringCellInfo(int, int);
@@ -45317,7 +45420,7 @@
     method @NonNull public java.util.List<java.lang.Integer> getAvailableServices();
     method @Nullable public android.telephony.CellIdentity getCellIdentity();
     method public int getDomain();
-    method public int getNrState();
+    method @Nullable public String getRegisteredPlmn();
     method public int getTransportType();
     method public boolean isRegistered();
     method public boolean isRoaming();
@@ -45444,6 +45547,7 @@
     ctor public PhoneStateListener();
     ctor public PhoneStateListener(@NonNull java.util.concurrent.Executor);
     method public void onActiveDataSubscriptionIdChanged(int);
+    method public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int);
     method public void onCallForwardingIndicatorChanged(boolean);
     method public void onCallStateChanged(int, String);
@@ -45452,7 +45556,7 @@
     method public void onDataActivity(int);
     method public void onDataConnectionStateChanged(int);
     method public void onDataConnectionStateChanged(int, int);
-    method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.DisplayInfo);
+    method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
     method public void onMessageWaitingIndicatorChanged(boolean);
     method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
@@ -45462,6 +45566,7 @@
     method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
     method public void onUserMobileDataStateChanged(boolean);
     field public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
+    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
     field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
     field public static final int LISTEN_CALL_STATE = 32; // 0x20
@@ -45484,6 +45589,7 @@
 
   public final class PreciseDataConnectionState implements android.os.Parcelable {
     method public int describeContents();
+    method @Nullable public android.telephony.data.ApnSetting getApnSetting();
     method public int getLastCauseCode();
     method @Nullable public android.net.LinkProperties getLinkProperties();
     method public int getNetworkType();
@@ -45562,7 +45668,7 @@
     method public String createAppSpecificSmsToken(android.app.PendingIntent);
     method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
     method public java.util.ArrayList<java.lang.String> divideMessage(String);
-    method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
+    method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
     method @NonNull public android.os.Bundle getCarrierConfigValues();
     method public static android.telephony.SmsManager getDefault();
     method public static int getDefaultSmsSubscriptionId();
@@ -45572,7 +45678,7 @@
     method public int getSubscriptionId();
     method public void injectSmsPdu(byte[], String, android.app.PendingIntent);
     method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
-    method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
+    method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
     method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
     method public void sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
     method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.SEND_SMS}) public void sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
@@ -45840,7 +45946,7 @@
     method public long getDataLimitBytes();
     method public long getDataUsageBytes();
     method public long getDataUsageTime();
-    method @Nullable public int[] getNetworkTypes();
+    method @NonNull public int[] getNetworkTypes();
     method @Nullable public CharSequence getSummary();
     method @Nullable public CharSequence getTitle();
     method public void writeToParcel(android.os.Parcel, int);
@@ -45858,13 +45964,27 @@
     method public android.telephony.SubscriptionPlan build();
     method public static android.telephony.SubscriptionPlan.Builder createNonrecurring(java.time.ZonedDateTime, java.time.ZonedDateTime);
     method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
+    method @NonNull public android.telephony.SubscriptionPlan.Builder resetNetworkTypes();
     method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
     method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
-    method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@Nullable int[]);
+    method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@NonNull int[]);
     method public android.telephony.SubscriptionPlan.Builder setSummary(@Nullable CharSequence);
     method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
   }
 
+  public final class TelephonyDisplayInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getNetworkType();
+    method public int getOverrideNetworkType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.TelephonyDisplayInfo> CREATOR;
+    field public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; // 0x2
+    field public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1; // 0x1
+    field public static final int OVERRIDE_NETWORK_TYPE_NONE = 0; // 0x0
+    field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3; // 0x3
+    field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; // 0x4
+  }
+
   public class TelephonyManager {
     method public boolean canChangeDtmfToneLength();
     method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
@@ -45900,6 +46020,7 @@
     method public String getMmsUserAgent();
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNai();
     method public String getNetworkCountryIso();
+    method @NonNull public String getNetworkCountryIso(int);
     method public String getNetworkOperator();
     method public String getNetworkOperatorName();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
@@ -45940,7 +46061,6 @@
     method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
     method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
     method public boolean isConcurrentVoiceAndDataSupported();
-    method public boolean isDataCapable();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
     method public boolean isEmergencyNumber(@NonNull String);
@@ -45965,6 +46085,7 @@
     method public boolean setLine1NumberForDisplay(String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(String, boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, boolean, int);
     method public boolean setOperatorBrandOverride(String);
     method public boolean setPreferredNetworkTypeToGlobal();
     method public void setPreferredOpportunisticDataSubscription(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
@@ -46010,6 +46131,7 @@
     field public static final int DATA_DISCONNECTING = 4; // 0x4
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final int DATA_UNKNOWN = -1; // 0xffffffff
+    field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
     field public static final String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
     field public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
     field public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
@@ -46019,7 +46141,6 @@
     field public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
     field public static final String EXTRA_NETWORK_COUNTRY = "android.telephony.extra.NETWORK_COUNTRY";
     field public static final String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
-    field public static final String EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED = "android.telephony.extra.NUM_OF_ACTIVE_SIM_SUPPORTED";
     field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
     field public static final String EXTRA_SPECIFIC_CARRIER_ID = "android.telephony.extra.SPECIFIC_CARRIER_ID";
     field public static final String EXTRA_SPECIFIC_CARRIER_NAME = "android.telephony.extra.SPECIFIC_CARRIER_NAME";
@@ -46030,10 +46151,6 @@
     field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
     field public static final String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
     field public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
-    field public static final int MODEM_COUNT_DUAL_MODEM = 2; // 0x2
-    field public static final int MODEM_COUNT_NO_MODEM = 0; // 0x0
-    field public static final int MODEM_COUNT_SINGLE_MODEM = 1; // 0x1
-    field public static final int MODEM_COUNT_TRI_MODEM = 3; // 0x3
     field public static final int MULTISIM_ALLOWED = 0; // 0x0
     field public static final int MULTISIM_NOT_SUPPORTED_BY_CARRIER = 2; // 0x2
     field public static final int MULTISIM_NOT_SUPPORTED_BY_HARDWARE = 1; // 0x1
@@ -46062,7 +46179,6 @@
     field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
     field public static final int PHONE_TYPE_CDMA = 2; // 0x2
     field public static final int PHONE_TYPE_GSM = 1; // 0x1
-    field public static final int PHONE_TYPE_IMS = 5; // 0x5
     field public static final int PHONE_TYPE_NONE = 0; // 0x0
     field public static final int PHONE_TYPE_SIP = 3; // 0x3
     field public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; // 0x2
@@ -46354,10 +46470,42 @@
     field public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2; // 0x2
     field public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0; // 0x0
     field public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1; // 0x1
+    field public static final int ERROR_ADDRESS_MISSING = 10011; // 0x271b
+    field public static final int ERROR_CARRIER_LOCKED = 10000; // 0x2710
+    field public static final int ERROR_CERTIFICATE_ERROR = 10012; // 0x271c
+    field public static final int ERROR_CONNECTION_ERROR = 10014; // 0x271e
+    field public static final int ERROR_DISALLOWED_BY_PPR = 10010; // 0x271a
+    field public static final int ERROR_EUICC_INSUFFICIENT_MEMORY = 10004; // 0x2714
+    field public static final int ERROR_EUICC_MISSING = 10006; // 0x2716
+    field public static final int ERROR_INCOMPATIBLE_CARRIER = 10003; // 0x2713
+    field public static final int ERROR_INSTALL_PROFILE = 10009; // 0x2719
+    field public static final int ERROR_INVALID_ACTIVATION_CODE = 10001; // 0x2711
+    field public static final int ERROR_INVALID_CONFIRMATION_CODE = 10002; // 0x2712
+    field public static final int ERROR_INVALID_RESPONSE = 10015; // 0x271f
+    field public static final int ERROR_NO_PROFILES_AVAILABLE = 10013; // 0x271d
+    field public static final int ERROR_OPERATION_BUSY = 10016; // 0x2720
+    field public static final int ERROR_SIM_MISSING = 10008; // 0x2718
+    field public static final int ERROR_TIME_OUT = 10005; // 0x2715
+    field public static final int ERROR_UNSUPPORTED_VERSION = 10007; // 0x2717
     field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
     field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
+    field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_ERROR_CODE";
+    field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_OPERATION_CODE";
+    field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE";
+    field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE";
     field public static final String EXTRA_USE_QR_SCANNER = "android.telephony.euicc.extra.USE_QR_SCANNER";
     field public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
+    field public static final int OPERATION_APDU = 8; // 0x8
+    field public static final int OPERATION_DOWNLOAD = 5; // 0x5
+    field public static final int OPERATION_EUICC_CARD = 3; // 0x3
+    field public static final int OPERATION_EUICC_GSMA = 7; // 0x7
+    field public static final int OPERATION_HTTP = 11; // 0xb
+    field public static final int OPERATION_METADATA = 6; // 0x6
+    field public static final int OPERATION_SIM_SLOT = 2; // 0x2
+    field public static final int OPERATION_SMDX = 9; // 0x9
+    field public static final int OPERATION_SMDX_SUBJECT_REASON_CODE = 10; // 0xa
+    field public static final int OPERATION_SWITCH = 4; // 0x4
+    field public static final int OPERATION_SYSTEM = 1; // 0x1
   }
 
 }
@@ -46461,6 +46609,7 @@
 
   public class ImsManager {
     method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
+    method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
     field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
     field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
     field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
@@ -46489,6 +46638,11 @@
     method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
   }
 
+  public class ImsRcsManager {
+    method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
+    field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
+  }
+
   public final class ImsReasonInfo implements android.os.Parcelable {
     ctor public ImsReasonInfo(int, int, @Nullable String);
     method public int describeContents();
@@ -46672,6 +46826,10 @@
     field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
   }
 
+  public class RcsUceAdapter {
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
+  }
+
   public interface RegistrationManager {
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -46686,8 +46844,8 @@
     ctor public RegistrationManager.RegistrationCallback();
     method public void onRegistered(int);
     method public void onRegistering(int);
-    method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
-    method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
+    method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo);
+    method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo);
   }
 
 }
@@ -46800,6 +46958,7 @@
 
   public static class MbmsErrors.DownloadErrors {
     field public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401; // 0x191
+    field public static final int ERROR_MALFORMED_SERVICE_ANNOUNCEMENT_FILE = 404; // 0x194
     field public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402; // 0x192
     field public static final int ERROR_UNKNOWN_FILE_INFO = 403; // 0x193
   }
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 1d646d4..355fa18 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -29,7 +29,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
   }
 
-  public class TetheringConstants {
+  public final 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";
@@ -72,19 +72,20 @@
     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_DISABLE_FORWARDING_ERROR = 9; // 0x9
+    field public static final int TETHER_ERROR_ENABLE_FORWARDING_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_INTERNAL_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_PROVISIONING_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_UNKNOWN_TYPE = 16; // 0x10
     field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
     field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
     field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
@@ -96,40 +97,42 @@
     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 static interface TetheringManager.StartTetheringCallback {
+    method public default void onTetheringFailed(int);
+    method public default 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 public void onOffloadStatusChanged(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);
+  public static interface TetheringManager.TetheringEventCallback {
+    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+    method public default void onError(@NonNull String, int);
+    method public default void onOffloadStatusChanged(int);
+    method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheringSupported(boolean);
+    method public default 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.TetheringInterfaceRegexps {
+    method @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+    method @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+    method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
   }
 
   public static class TetheringManager.TetheringRequest {
+    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
+    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
+    method public boolean getShouldShowEntitlementUi();
+    method public int getTetheringType();
+    method public boolean isExemptFromEntitlementCheck();
   }
 
   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);
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 7646373..4a1bf0d 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -94,7 +94,6 @@
     field public static final String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
     field public static final String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP";
     field public static final String KILL_UID = "android.permission.KILL_UID";
-    field public static final String LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = "android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH";
     field public static final String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
     field public static final String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
     field public static final String LOOP_RADIO = "android.permission.LOOP_RADIO";
@@ -134,6 +133,7 @@
     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";
+    field public static final String NETWORK_STATS_PROVIDER = "android.permission.NETWORK_STATS_PROVIDER";
     field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
     field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
     field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
@@ -186,7 +186,7 @@
     field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES";
     field public static final String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
     field public static final String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
-    field public static final String SECURE_ELEMENT_PRIVILEGED = "android.permission.SECURE_ELEMENT_PRIVILEGED";
+    field public static final String SECURE_ELEMENT_PRIVILEGED_OPERATION = "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
     field public static final String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
     field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
     field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -228,10 +228,6 @@
 
   public static final class R.array {
     field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
-    field public static final int config_restrictedPreinstalledCarrierApps = 17235975; // 0x1070007
-    field public static final int config_sms_enabled_locking_shift_tables = 17235977; // 0x1070009
-    field public static final int config_sms_enabled_single_shift_tables = 17235976; // 0x1070008
-    field public static final int simColors = 17235974; // 0x1070006
   }
 
   public static final class R.attr {
@@ -278,7 +274,6 @@
     field public static final int config_helpIntentNameKey = 17039390; // 0x104001e
     field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
     field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
-    field public static final int low_memory = 17039397; // 0x1040025
   }
 
   public static final class R.style {
@@ -317,12 +312,10 @@
     method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales();
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
     method @RequiresPermission(android.Manifest.permission.KILL_UID) public void killUid(int, String);
-    method public void registerHomeVisibilityObserver(@NonNull android.app.HomeVisibilityObserver);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
     method public void setDeviceLocales(@NonNull android.os.LocaleList);
     method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
-    method public void unregisterHomeVisibilityObserver(@NonNull android.app.HomeVisibilityObserver);
   }
 
   public static interface ActivityManager.OnUidImportanceListener {
@@ -509,11 +502,6 @@
     field public static final String ACTION_DOWNLOAD_COMPLETED = "android.intent.action.DOWNLOAD_COMPLETED";
   }
 
-  public abstract class HomeVisibilityObserver {
-    ctor public HomeVisibilityObserver();
-    method public abstract void onHomeVisibilityChanged(boolean);
-  }
-
   public abstract class InstantAppResolverService extends android.app.Service {
     ctor public InstantAppResolverService();
     method public final void attachBaseContext(android.content.Context);
@@ -997,8 +985,8 @@
 
   public final class CompatChanges {
     method public static boolean isChangeEnabled(long);
-    method public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle);
-    method public static boolean isChangeEnabled(long, int);
+    method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle);
+    method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int);
   }
 
 }
@@ -1238,7 +1226,8 @@
   }
 
   public class NetworkStatsManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.net.netstats.provider.NetworkStatsProviderCallback registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.AbstractNetworkStatsProvider);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider);
   }
 
   public static final class UsageEvents.Event {
@@ -1289,16 +1278,8 @@
 package android.bluetooth {
 
   public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void disableOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void enableOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice);
-    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothDevice getActiveDevice();
-    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int getOptionalCodecsEnabled(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setCodecConfigPreference(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothCodecConfig);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setOptionalCodecsEnabled(@Nullable android.bluetooth.BluetoothDevice, int);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int supportsOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; // 0x0
     field public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; // 0x0
     field public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; // 0x1
@@ -1309,28 +1290,23 @@
 
   public final class BluetoothA2dpSink implements android.bluetooth.BluetoothProfile {
     method public void finalize();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAudioPlaying(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAudioPlaying(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
   }
 
   public final class BluetoothAdapter {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice);
     method public boolean disableBLE();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice);
     method public boolean enableBLE();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public long getDiscoveryEndMillis();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public long getDiscoveryEndMillis();
     method public boolean isBleScanAlwaysAvailable();
     method public boolean isLeEnabled();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeActiveDevice(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setActiveDevice(@NonNull android.bluetooth.BluetoothDevice, int);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setScanMode(int, long);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setScanMode(int);
     field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
     field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
     field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
@@ -1342,70 +1318,17 @@
     method public void onMetadataChanged(@NonNull android.bluetooth.BluetoothDevice, int, @Nullable byte[]);
   }
 
-  public final class BluetoothCodecConfig implements android.os.Parcelable {
-    ctor public BluetoothCodecConfig(int, int, int, int, int, long, long, long, long);
-    ctor public BluetoothCodecConfig(int);
-    method public int getBitsPerSample();
-    method @NonNull public String getCodecName();
-    method public int getCodecPriority();
-    method public long getCodecSpecific1();
-    method public int getCodecType();
-    method public int getSampleRate();
-    method public boolean isMandatoryCodec();
-    field public static final int BITS_PER_SAMPLE_16 = 1; // 0x1
-    field public static final int BITS_PER_SAMPLE_24 = 2; // 0x2
-    field public static final int BITS_PER_SAMPLE_32 = 4; // 0x4
-    field public static final int BITS_PER_SAMPLE_NONE = 0; // 0x0
-    field public static final int CHANNEL_MODE_MONO = 1; // 0x1
-    field public static final int CHANNEL_MODE_NONE = 0; // 0x0
-    field public static final int CHANNEL_MODE_STEREO = 2; // 0x2
-    field public static final int CODEC_PRIORITY_DEFAULT = 0; // 0x0
-    field public static final int CODEC_PRIORITY_DISABLED = -1; // 0xffffffff
-    field public static final int CODEC_PRIORITY_HIGHEST = 1000000; // 0xf4240
-    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothCodecConfig> CREATOR;
-    field public static final int SAMPLE_RATE_176400 = 16; // 0x10
-    field public static final int SAMPLE_RATE_192000 = 32; // 0x20
-    field public static final int SAMPLE_RATE_44100 = 1; // 0x1
-    field public static final int SAMPLE_RATE_48000 = 2; // 0x2
-    field public static final int SAMPLE_RATE_88200 = 4; // 0x4
-    field public static final int SAMPLE_RATE_96000 = 8; // 0x8
-    field public static final int SAMPLE_RATE_NONE = 0; // 0x0
-    field public static final int SOURCE_CODEC_TYPE_AAC = 1; // 0x1
-    field public static final int SOURCE_CODEC_TYPE_APTX = 2; // 0x2
-    field public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; // 0x3
-    field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240
-    field public static final int SOURCE_CODEC_TYPE_LDAC = 4; // 0x4
-    field public static final int SOURCE_CODEC_TYPE_MAX = 5; // 0x5
-    field public static final int SOURCE_CODEC_TYPE_SBC = 0; // 0x0
-  }
-
-  public final class BluetoothCodecStatus implements android.os.Parcelable {
-    ctor public BluetoothCodecStatus(@Nullable android.bluetooth.BluetoothCodecConfig, @Nullable android.bluetooth.BluetoothCodecConfig[], @Nullable android.bluetooth.BluetoothCodecConfig[]);
-    method @Nullable public android.bluetooth.BluetoothCodecConfig getCodecConfig();
-    method @Nullable public android.bluetooth.BluetoothCodecConfig[] getCodecsLocalCapabilities();
-    method @Nullable public android.bluetooth.BluetoothCodecConfig[] getCodecsSelectableCapabilities();
-    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothCodecStatus> CREATOR;
-    field public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS";
-  }
-
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelPairing();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getBatteryLevel();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getMessageAccessPermission();
     method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getPhonebookAccessPermission();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getSimAccessPermission();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isBondingInitiatedLocally();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getSimAccessPermission();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeBond();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setAlias(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMessageAccessPermission(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSilenceMode(boolean);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSimAccessPermission(int);
     field public static final int ACCESS_ALLOWED = 1; // 0x1
@@ -1435,17 +1358,15 @@
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice);
-    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothDevice getActiveDevice();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
   }
 
   public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
-    method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH) public java.util.List<android.bluetooth.BluetoothDevice> getActiveDevices();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public long getHiSyncId(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public long getHiSyncId(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
   }
 
   public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
@@ -1454,9 +1375,9 @@
 
   public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile {
     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 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);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) 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";
   }
 
@@ -1464,14 +1385,14 @@
     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);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
   }
 
-  public final class BluetoothPan implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+  public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
     method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isTetheringOn();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setBluetoothTethering(boolean);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
@@ -1485,7 +1406,7 @@
   }
 
   public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
   }
@@ -1607,9 +1528,7 @@
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
     field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
     field public static final String NETD_SERVICE = "netd";
-    field public static final String NETWORK_POLICY_SERVICE = "netpolicy";
     field public static final String NETWORK_SCORE_SERVICE = "network_score";
-    field public static final String NETWORK_STACK_SERVICE = "network_stack";
     field public static final String OEM_LOCK_SERVICE = "oem_lock";
     field public static final String PERMISSION_SERVICE = "permission";
     field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
@@ -1668,7 +1587,6 @@
     field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
     field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
-    field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
     field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
     field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
     field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -1973,7 +1891,6 @@
     field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
     field public static final int MATCH_ANY_USER = 4194304; // 0x400000
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
-    field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
     field public static final int MATCH_INSTANT = 8388608; // 0x800000
     field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
     field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
@@ -4421,7 +4338,7 @@
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, 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(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
     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);
@@ -4472,10 +4389,10 @@
 
   public class InvalidPacketException extends java.lang.Exception {
     ctor public InvalidPacketException(int);
+    method public int getError();
     field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
     field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
     field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
-    field public final int error;
   }
 
   public final class IpConfiguration implements android.os.Parcelable {
@@ -4529,12 +4446,12 @@
   }
 
   public class KeepalivePacketData {
-    ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException;
+    ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException;
+    method @NonNull public java.net.InetAddress getDstAddress();
+    method public int getDstPort();
     method @NonNull public byte[] getPacket();
-    field @NonNull public final java.net.InetAddress dstAddress;
-    field public final int dstPort;
-    field @NonNull public final java.net.InetAddress srcAddress;
-    field public final int srcPort;
+    method @NonNull public java.net.InetAddress getSrcAddress();
+    method public int getSrcPort();
   }
 
   public class LinkAddress implements android.os.Parcelable {
@@ -4555,6 +4472,7 @@
 
   public final class LinkProperties implements android.os.Parcelable {
     ctor public LinkProperties(@Nullable android.net.LinkProperties);
+    ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
     method public boolean addDnsServer(@NonNull java.net.InetAddress);
     method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
     method public boolean addPcscfServer(@NonNull java.net.InetAddress);
@@ -4577,7 +4495,6 @@
     method public boolean isIpv6Provisioned();
     method public boolean isProvisioned();
     method public boolean isReachable(@NonNull java.net.InetAddress);
-    method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy();
     method public boolean removeDnsServer(@NonNull java.net.InetAddress);
     method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
     method public boolean removeRoute(@NonNull android.net.RouteInfo);
@@ -4593,7 +4510,6 @@
   public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
     ctor public MatchAllNetworkSpecifier();
     method public int describeContents();
-    method public boolean satisfiedBy(android.net.NetworkSpecifier);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR;
   }
@@ -4607,79 +4523,83 @@
 
   public class Network implements android.os.Parcelable {
     ctor public Network(@NonNull android.net.Network);
+    method public int getNetId();
     method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
-    field public final int netId;
   }
 
   public abstract class NetworkAgent {
     ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
     method @Nullable public android.net.Network getNetwork();
+    method public void markConnected();
     method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
     method public void onAutomaticReconnectDisabled();
-    method public void onBandwidthUpdateRequested();
     method public void onNetworkUnwanted();
     method public void onRemoveKeepalivePacketFilter(int);
     method public void onSaveAcceptUnvalidated(boolean);
     method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
-    method public void onStartSocketKeepalive(int, int, @NonNull android.net.KeepalivePacketData);
+    method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData);
     method public void onStopSocketKeepalive(int);
-    method public void onValidationStatus(int, @Nullable String);
+    method public void onValidationStatus(int, @Nullable android.net.Uri);
     method @NonNull public android.net.Network register();
-    method public void sendLinkProperties(@NonNull android.net.LinkProperties);
-    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 final void sendLinkProperties(@NonNull android.net.LinkProperties);
+    method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+    method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
+    method public final void sendSocketKeepaliveEvent(int, int);
     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 public final int providerId;
   }
 
   public final class NetworkAgentConfig implements android.os.Parcelable {
     method public int describeContents();
     method public int getLegacyType();
     method @NonNull public String getLegacyTypeName();
-    method @Nullable public String getSubscriberId();
     method public boolean isExplicitlySelected();
-    method public boolean isNat64DetectionEnabled();
     method public boolean isPartialConnectivityAcceptable();
-    method public boolean isProvisioningNotificationEnabled();
     method public boolean isUnvalidatedConnectivityAcceptable();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
   }
 
-  public static class NetworkAgentConfig.Builder {
+  public static final class NetworkAgentConfig.Builder {
     ctor public NetworkAgentConfig.Builder();
     method @NonNull public android.net.NetworkAgentConfig build();
-    method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection();
-    method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification();
     method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
     method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
     method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
     method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
     method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean);
   }
 
   public final class NetworkCapabilities implements android.os.Parcelable {
-    method public boolean deduceRestrictedCapability();
-    method @NonNull public java.util.List<java.lang.Integer> getAdministratorUids();
-    method @Nullable public String getSSID();
+    method @NonNull public int[] getAdministratorUids();
+    method @Nullable public String getSsid();
     method @NonNull public int[] getTransportTypes();
     method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
-    method @NonNull public android.net.NetworkCapabilities setAdministratorUids(@NonNull java.util.List<java.lang.Integer>);
-    method @NonNull public android.net.NetworkCapabilities setRequestorPackageName(@NonNull String);
-    method @NonNull public android.net.NetworkCapabilities setRequestorUid(int);
-    method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String);
-    method @NonNull public android.net.NetworkCapabilities setTransportInfo(@NonNull android.net.TransportInfo);
     field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
     field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
   }
 
+  public static class NetworkCapabilities.Builder {
+    ctor public NetworkCapabilities.Builder();
+    ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
+    method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
+    method @NonNull public android.net.NetworkCapabilities build();
+    method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
+    method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+    method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
+  }
+
   public class NetworkKey implements android.os.Parcelable {
     ctor public NetworkKey(android.net.WifiKey);
     method @Nullable public static android.net.NetworkKey createFromScanResult(@Nullable android.net.wifi.ScanResult);
@@ -4691,30 +4611,12 @@
     field public final android.net.WifiKey wifiKey;
   }
 
-  public class NetworkPolicyManager {
-    method @NonNull public android.telephony.SubscriptionPlan[] getSubscriptionPlans(int, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
-    method public void setSubscriptionOverride(int, int, int, long, @NonNull String);
-    method public void setSubscriptionPlans(int, @NonNull android.telephony.SubscriptionPlan[], @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
-    field public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 2; // 0x2
-    field public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1; // 0x1
-  }
-
-  public static class NetworkPolicyManager.SubscriptionCallback {
-    ctor public NetworkPolicyManager.SubscriptionCallback();
-    method public void onSubscriptionOverride(int, int, int);
-    method public void onSubscriptionPlansChanged(int, @NonNull android.telephony.SubscriptionPlan[]);
-  }
-
   public class NetworkProvider {
     ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
-    method @Nullable public android.os.Messenger getMessenger();
-    method @NonNull public String getName();
     method public int getProviderId();
-    method public void onNetworkRequested(@NonNull android.net.NetworkRequest, int, int);
-    method public void onRequestWithdrawn(@NonNull android.net.NetworkRequest);
+    method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest);
+    method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int);
     field public static final int ID_NONE = -1; // 0xffffffff
   }
 
@@ -4727,7 +4629,6 @@
   public class NetworkRequest implements android.os.Parcelable {
     method @Nullable public String getRequestorPackageName();
     method public int getRequestorUid();
-    method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities);
   }
 
   public static class NetworkRequest.Builder {
@@ -4761,25 +4662,25 @@
   }
 
   public abstract class NetworkSpecifier {
+    method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkSpecifier);
     method @Nullable public android.net.NetworkSpecifier redact();
-    method public abstract boolean satisfiedBy(@Nullable android.net.NetworkSpecifier);
   }
 
   public class NetworkStack {
+    method @Nullable public static android.os.IBinder getService();
     field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
   }
 
   public final class NetworkStats implements android.os.Parcelable {
     ctor public NetworkStats(long, int);
     method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats);
-    method @NonNull public android.net.NetworkStats addValues(@NonNull android.net.NetworkStats.Entry);
+    method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry);
     method public int describeContents();
     method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR;
     field public static final int DEFAULT_NETWORK_NO = 0; // 0x0
     field public static final int DEFAULT_NETWORK_YES = 1; // 0x1
-    field @Nullable public static final String IFACE_ALL;
     field public static final String IFACE_VT = "vt_data0";
     field public static final int METERED_NO = 0; // 0x0
     field public static final int METERED_YES = 1; // 0x1
@@ -4865,10 +4766,6 @@
     method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
   }
 
-  public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
-    method public boolean satisfiedBy(android.net.NetworkSpecifier);
-  }
-
   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();
@@ -4891,7 +4788,6 @@
     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.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
     method @RequiresPermission(anyOf={android.Manifest.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.Manifest.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.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
@@ -4908,19 +4804,20 @@
     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_DISABLE_FORWARDING_ERROR = 9; // 0x9
+    field public static final int TETHER_ERROR_ENABLE_FORWARDING_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_INTERNAL_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_PROVISIONING_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_UNKNOWN_TYPE = 16; // 0x10
     field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
     field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
     field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
@@ -4932,40 +4829,35 @@
     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 static interface TetheringManager.StartTetheringCallback {
+    method public default void onTetheringFailed(int);
+    method public default 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 public void onOffloadStatusChanged(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 interface TetheringManager.TetheringEventCallback {
+    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+    method public default void onError(@NonNull String, int);
+    method public default void onOffloadStatusChanged(int);
+    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheringSupported(boolean);
+    method public default void onUpstreamChanged(@Nullable android.net.Network);
   }
 
   public static class TetheringManager.TetheringRequest {
+    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
+    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
+    method public boolean getShouldShowEntitlementUi();
+    method public int getTetheringType();
+    method public boolean isExemptFromEntitlementCheck();
   }
 
   public static class TetheringManager.TetheringRequest.Builder {
     ctor public TetheringManager.TetheringRequest.Builder(int);
     method @NonNull public android.net.TetheringManager.TetheringRequest build();
     method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
   }
 
   public class TrafficStats {
@@ -5179,21 +5071,17 @@
 
 package android.net.netstats.provider {
 
-  public abstract class AbstractNetworkStatsProvider {
-    ctor public AbstractNetworkStatsProvider();
-    method public abstract void requestStatsUpdate(int);
-    method public abstract void setAlert(long);
-    method public abstract void setLimit(@NonNull String, long);
+  public abstract class NetworkStatsProvider {
+    ctor public NetworkStatsProvider();
+    method public void notifyAlertReached();
+    method public void notifyLimitReached();
+    method public void notifyStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
+    method public abstract void onRequestStatsUpdate(int);
+    method public abstract void onSetAlert(long);
+    method public abstract void onSetLimit(@NonNull String, long);
     field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff
   }
 
-  public class NetworkStatsProviderCallback {
-    method public void onAlertReached();
-    method public void onLimitReached();
-    method public void onStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
-    method public void unregister();
-  }
-
 }
 
 package android.net.sip {
@@ -5568,10 +5456,6 @@
     field public int numUsage;
   }
 
-  public final class WifiNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
-    method public boolean satisfiedBy(android.net.NetworkSpecifier);
-  }
-
   public class WifiScanner {
     method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
     method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
@@ -5736,10 +5620,6 @@
     method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]);
   }
 
-  public final class WifiAwareNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
-    method public boolean satisfiedBy(android.net.NetworkSpecifier);
-  }
-
   public static final class WifiAwareNetworkSpecifier.Builder {
     method @NonNull public android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder setPmk(@NonNull byte[]);
   }
@@ -5982,13 +5862,15 @@
     field public static final String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
     field public static final String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
     field public static final String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
-    field public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
+    field @RequiresPermission("android.permission.UPDATE_CONFIG") public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
     field public static final String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
     field public static final String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
     field public static final String ACTION_UPDATE_NETWORK_WATCHLIST = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
     field public static final String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
     field public static final String ACTION_UPDATE_SMART_SELECTION = "android.intent.action.UPDATE_SMART_SELECTION";
     field public static final String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES";
+    field public static final String EXTRA_REQUIRED_HASH = "android.os.extra.REQUIRED_HASH";
+    field public static final String EXTRA_VERSION = "android.os.extra.VERSION";
   }
 
   public class Environment {
@@ -6644,18 +6526,6 @@
 
 package android.provider {
 
-  public class BlockedNumberContract {
-    field public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
-    field public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
-    field public static final String RES_BLOCK_STATUS = "block_status";
-    field public static final int STATUS_BLOCKED_IN_LIST = 1; // 0x1
-    field public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5; // 0x5
-    field public static final int STATUS_BLOCKED_PAYPHONE = 4; // 0x4
-    field public static final int STATUS_BLOCKED_RESTRICTED = 2; // 0x2
-    field public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3; // 0x3
-    field public static final int STATUS_NOT_BLOCKED = 0; // 0x0
-  }
-
   public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
@@ -6911,7 +6781,6 @@
     field public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
     field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
     field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
-    field public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
     field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
     field public static final String DOZE_ALWAYS_ON = "doze_always_on";
     field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
@@ -6937,32 +6806,6 @@
     field public static final int VOLUME_HUSH_VIBRATE = 1; // 0x1
   }
 
-  public static interface Telephony.CarrierColumns extends android.provider.BaseColumns {
-    field @NonNull public static final android.net.Uri CONTENT_URI;
-    field public static final String EXPIRATION_TIME = "expiration_time";
-    field public static final String KEY_IDENTIFIER = "key_identifier";
-    field public static final String KEY_TYPE = "key_type";
-    field public static final String LAST_MODIFIED = "last_modified";
-    field public static final String MCC = "mcc";
-    field public static final String MNC = "mnc";
-    field public static final String MVNO_MATCH_DATA = "mvno_match_data";
-    field public static final String MVNO_TYPE = "mvno_type";
-    field public static final String PUBLIC_KEY = "public_key";
-  }
-
-  public static final class Telephony.CarrierId.All implements android.provider.BaseColumns {
-    field public static final String APN = "apn";
-    field @NonNull public static final android.net.Uri CONTENT_URI;
-    field public static final String GID1 = "gid1";
-    field public static final String GID2 = "gid2";
-    field public static final String ICCID_PREFIX = "iccid_prefix";
-    field public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern";
-    field public static final String MCCMNC = "mccmnc";
-    field public static final String PLMN = "plmn";
-    field public static final String PRIVILEGE_ACCESS_RULE = "privilege_access_rule";
-    field public static final String SPN = "spn";
-  }
-
   public static final class Telephony.Carriers implements android.provider.BaseColumns {
     field public static final String APN_SET_ID = "apn_set_id";
     field public static final int CARRIER_EDITED = 4; // 0x4
@@ -7033,76 +6876,6 @@
     field @NonNull public static final String ENABLE_TEST_ALERT_PREF = "enable_test_alerts";
   }
 
-  public static final class Telephony.SimInfo {
-    field public static final String ACCESS_RULES = "access_rules";
-    field public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = "access_rules_from_carrier_configs";
-    field public static final String ALLOWED_NETWORK_TYPES = "allowed_network_types";
-    field public static final String CARD_ID = "card_id";
-    field public static final String CARRIER_ID = "carrier_id";
-    field public static final String CARRIER_NAME = "carrier_name";
-    field public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
-    field public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
-    field public static final String CB_ALERT_SPEECH = "enable_alert_speech";
-    field public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
-    field public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
-    field public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
-    field public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts";
-    field public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
-    field public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
-    field public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
-    field public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
-    field public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
-    field public static final String COLOR = "color";
-    field @NonNull public static final android.net.Uri CONTENT_URI;
-    field public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules";
-    field public static final String DATA_ROAMING = "data_roaming";
-    field public static final int DATA_ROAMING_DEFAULT = 0; // 0x0
-    field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
-    field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
-    field public static final String DISPLAY_NAME = "display_name";
-    field public static final String EHPLMNS = "ehplmns";
-    field public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
-    field public static final String GROUP_OWNER = "group_owner";
-    field public static final String GROUP_UUID = "group_uuid";
-    field public static final String HPLMNS = "hplmns";
-    field public static final String ICC_ID = "icc_id";
-    field public static final String IMSI = "imsi";
-    field public static final String IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
-    field public static final String ISO_COUNTRY_CODE = "iso_country_code";
-    field public static final String IS_EMBEDDED = "is_embedded";
-    field public static final String IS_OPPORTUNISTIC = "is_opportunistic";
-    field public static final String IS_REMOVABLE = "is_removable";
-    field public static final String MCC = "mcc";
-    field public static final String MCC_STRING = "mcc_string";
-    field public static final String MNC = "mnc";
-    field public static final String MNC_STRING = "mnc_string";
-    field public static final String NAME_SOURCE = "name_source";
-    field public static final int NAME_SOURCE_CARRIER = 3; // 0x3
-    field public static final int NAME_SOURCE_DEFAULT = 0; // 0x0
-    field public static final int NAME_SOURCE_SIM_PNN = 4; // 0x4
-    field public static final int NAME_SOURCE_SIM_SPN = 1; // 0x1
-    field public static final int NAME_SOURCE_USER_INPUT = 2; // 0x2
-    field public static final String NUMBER = "number";
-    field public static final String PROFILE_CLASS = "profile_class";
-    field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
-    field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
-    field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1
-    field public static final int PROFILE_CLASS_TESTING = 0; // 0x0
-    field public static final int PROFILE_CLASS_UNSET = -1; // 0xffffffff
-    field public static final int SIM_NOT_INSERTED = -1; // 0xffffffff
-    field public static final String SIM_SLOT_INDEX = "sim_id";
-    field public static final String SUBSCRIPTION_TYPE = "subscription_type";
-    field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
-    field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
-    field public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled";
-    field public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
-    field public static final String VT_IMS_ENABLED = "vt_ims_enabled";
-    field public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
-    field public static final String WFC_IMS_MODE = "wfc_ims_mode";
-    field public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
-    field public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
-  }
-
   public static final class Telephony.Sms.Intents {
     field public static final String ACTION_SMS_EMERGENCY_CB_RECEIVED = "android.provider.action.SMS_EMERGENCY_CB_RECEIVED";
   }
@@ -7128,7 +6901,7 @@
 package android.se.omapi {
 
   public final class Reader {
-    method @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED) public boolean reset();
+    method @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION) public boolean reset();
   }
 
 }
@@ -7508,6 +7281,7 @@
   public abstract class EuiccService extends android.app.Service {
     ctor public EuiccService();
     method public void dump(@NonNull java.io.PrintWriter);
+    method public int encodeSmdxSubjectAndReasonCode(@NonNull String, @NonNull String);
     method @CallSuper public android.os.IBinder onBind(android.content.Intent);
     method public abstract int onDeleteSubscription(int, String);
     method public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
@@ -8208,6 +7982,11 @@
     field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0
   }
 
+  public final class BarringInfo implements android.os.Parcelable {
+    ctor public BarringInfo();
+    method @NonNull public android.telephony.BarringInfo createLocationInfoSanitizedCopy();
+  }
+
   public final class CallAttributes implements android.os.Parcelable {
     ctor public CallAttributes(@NonNull android.telephony.PreciseCallState, int, @NonNull android.telephony.CallQuality);
     method public int describeContents();
@@ -8218,27 +7997,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
   }
 
-  public final class CallForwardingInfo implements android.os.Parcelable {
-    ctor public CallForwardingInfo(int, int, @Nullable String, int);
-    method public int describeContents();
-    method @Nullable public String getNumber();
-    method public int getReason();
-    method public int getStatus();
-    method public int getTimeoutSeconds();
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
-    field public static final int REASON_ALL = 4; // 0x4
-    field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
-    field public static final int REASON_BUSY = 1; // 0x1
-    field public static final int REASON_NOT_REACHABLE = 3; // 0x3
-    field public static final int REASON_NO_REPLY = 2; // 0x2
-    field public static final int REASON_UNCONDITIONAL = 0; // 0x0
-    field public static final int STATUS_ACTIVE = 1; // 0x1
-    field public static final int STATUS_FDN_CHECK_FAILURE = 2; // 0x2
-    field public static final int STATUS_INACTIVE = 0; // 0x0
-    field public static final int STATUS_NOT_SUPPORTED = 4; // 0x4
-    field public static final int STATUS_UNKNOWN_ERROR = 3; // 0x3
-  }
-
   public final class CallQuality implements android.os.Parcelable {
     ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
     ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
@@ -8726,89 +8484,10 @@
   public final class DataSpecificRegistrationInfo implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
-    method public boolean isUsingCarrierAggregation();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR;
   }
 
-  public final class DisconnectCause {
-    field public static final int ALREADY_DIALING = 72; // 0x48
-    field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
-    field public static final int BUSY = 4; // 0x4
-    field public static final int CALLING_DISABLED = 74; // 0x4a
-    field public static final int CALL_BARRED = 20; // 0x14
-    field public static final int CALL_PULLED = 51; // 0x33
-    field public static final int CANT_CALL_WHILE_RINGING = 73; // 0x49
-    field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
-    field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
-    field public static final int CDMA_ALREADY_ACTIVATED = 49; // 0x31
-    field public static final int CDMA_DROP = 27; // 0x1b
-    field public static final int CDMA_INTERCEPT = 28; // 0x1c
-    field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
-    field public static final int CDMA_NOT_EMERGENCY = 34; // 0x22
-    field public static final int CDMA_PREEMPTED = 33; // 0x21
-    field public static final int CDMA_REORDER = 29; // 0x1d
-    field public static final int CDMA_RETRY_ORDER = 31; // 0x1f
-    field public static final int CDMA_SO_REJECT = 30; // 0x1e
-    field public static final int CONGESTION = 5; // 0x5
-    field public static final int CS_RESTRICTED = 22; // 0x16
-    field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
-    field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
-    field public static final int DATA_DISABLED = 54; // 0x36
-    field public static final int DATA_LIMIT_REACHED = 55; // 0x37
-    field public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57; // 0x39
-    field public static final int DIALED_MMI = 39; // 0x27
-    field public static final int DIAL_LOW_BATTERY = 62; // 0x3e
-    field public static final int DIAL_MODIFIED_TO_DIAL = 48; // 0x30
-    field public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66; // 0x42
-    field public static final int DIAL_MODIFIED_TO_SS = 47; // 0x2f
-    field public static final int DIAL_MODIFIED_TO_USSD = 46; // 0x2e
-    field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69; // 0x45
-    field public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70; // 0x46
-    field public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67; // 0x43
-    field public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68; // 0x44
-    field public static final int EMERGENCY_PERM_FAILURE = 64; // 0x40
-    field public static final int EMERGENCY_TEMP_FAILURE = 63; // 0x3f
-    field public static final int ERROR_UNSPECIFIED = 36; // 0x24
-    field public static final int FDN_BLOCKED = 21; // 0x15
-    field public static final int ICC_ERROR = 19; // 0x13
-    field public static final int IMEI_NOT_ACCEPTED = 58; // 0x3a
-    field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c
-    field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d
-    field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47
-    field public static final int INCOMING_MISSED = 1; // 0x1
-    field public static final int INCOMING_REJECTED = 16; // 0x10
-    field public static final int INVALID_CREDENTIALS = 10; // 0xa
-    field public static final int INVALID_NUMBER = 7; // 0x7
-    field public static final int LIMIT_EXCEEDED = 15; // 0xf
-    field public static final int LOCAL = 3; // 0x3
-    field public static final int LOST_SIGNAL = 14; // 0xe
-    field public static final int LOW_BATTERY = 61; // 0x3d
-    field public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 53; // 0x35
-    field public static final int MMI = 6; // 0x6
-    field public static final int NORMAL = 2; // 0x2
-    field public static final int NORMAL_UNSPECIFIED = 65; // 0x41
-    field public static final int NOT_DISCONNECTED = 0; // 0x0
-    field public static final int NOT_VALID = -1; // 0xffffffff
-    field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
-    field public static final int NUMBER_UNREACHABLE = 8; // 0x8
-    field public static final int OTASP_PROVISIONING_IN_PROCESS = 76; // 0x4c
-    field public static final int OUTGOING_CANCELED = 44; // 0x2c
-    field public static final int OUTGOING_EMERGENCY_CALL_PLACED = 80; // 0x50
-    field public static final int OUTGOING_FAILURE = 43; // 0x2b
-    field public static final int OUT_OF_NETWORK = 11; // 0xb
-    field public static final int OUT_OF_SERVICE = 18; // 0x12
-    field public static final int POWER_OFF = 17; // 0x11
-    field public static final int SERVER_ERROR = 12; // 0xc
-    field public static final int SERVER_UNREACHABLE = 9; // 0x9
-    field public static final int TIMED_OUT = 13; // 0xd
-    field public static final int TOO_MANY_ONGOING_CALLS = 75; // 0x4b
-    field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
-    field public static final int VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED = 50; // 0x32
-    field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
-    field public static final int WIFI_LOST = 59; // 0x3b
-  }
-
   public final class ImsiEncryptionInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getKeyIdentifier();
@@ -8883,6 +8562,7 @@
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setCellIdentity(@Nullable android.telephony.CellIdentity);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setDomain(int);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setEmergencyOnly(boolean);
+    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegisteredPlmn(@Nullable String);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegistrationState(int);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRejectCause(int);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int);
@@ -8947,7 +8627,6 @@
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
-    field @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 512; // 0x200
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
@@ -8957,19 +8636,6 @@
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
   }
 
-  public final class PinResult implements android.os.Parcelable {
-    ctor public PinResult(int, int);
-    method public int describeContents();
-    method public int getAttemptsRemaining();
-    method @NonNull public static android.telephony.PinResult getDefaultFailedResult();
-    method public int getType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PinResult> CREATOR;
-    field public static final int PIN_RESULT_TYPE_FAILURE = 2; // 0x2
-    field public static final int PIN_RESULT_TYPE_INCORRECT = 1; // 0x1
-    field public static final int PIN_RESULT_TYPE_SUCCESS = 0; // 0x0
-  }
-
   public final class PreciseCallState implements android.os.Parcelable {
     ctor public PreciseCallState(int, int, int, int, int);
     method public int describeContents();
@@ -8991,7 +8657,6 @@
   }
 
   public final class PreciseDataConnectionState implements android.os.Parcelable {
-    ctor public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int, @Nullable android.telephony.data.ApnSetting);
     method @Deprecated @NonNull public String getDataConnectionApn();
     method @Deprecated public int getDataConnectionApnTypeBitMask();
     method @Deprecated public int getDataConnectionFailCause();
@@ -9098,15 +8763,11 @@
   }
 
   public class ServiceState implements android.os.Parcelable {
-    method @NonNull public android.telephony.ServiceState createLocationInfoSanitizedCopy(boolean);
     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);
-    method public int getNrFrequencyRange();
     method @Nullable public String getOperatorAlphaLongRaw();
     method @Nullable public String getOperatorAlphaShortRaw();
     method @NonNull public static android.telephony.ServiceState newFromBundle(@NonNull android.os.Bundle);
@@ -9250,7 +8911,8 @@
     method public boolean enableCellBroadcastRange(int, int, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public java.util.List<android.telephony.SmsMessage> getMessagesFromIcc();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
-    method public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
+    method @Deprecated public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
+    method public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String, @Nullable String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
   }
 
@@ -9268,7 +8930,7 @@
 
   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 public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull 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);
@@ -9283,10 +8945,10 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(boolean, int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean);
     field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
     field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
-    field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
+    field @Deprecated public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
     field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
     field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1
     field public static final int PROFILE_CLASS_TESTING = 0; // 0x0
@@ -9327,7 +8989,6 @@
   public class TelephonyManager {
     method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int changeIccLockPassword(@NonNull String, @NonNull String);
     method public int checkCarrierPrivilegesForPackage(String);
     method public int checkCarrierPrivilegesForPackageAnyPhone(String);
     method public void dial(String);
@@ -9338,8 +8999,6 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CallForwardingInfo getCallForwarding(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCallWaitingStatus();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -9351,7 +9010,6 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
     method public String getCdmaPrlVersion();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
     method public int getCurrentPhoneType();
     method public int getCurrentPhoneType(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
@@ -9361,14 +9019,12 @@
     method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
-    method public int getEmergencyNumberDbVersion();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
     method public int getMaxNumberOfSimultaneouslyActiveSims();
     method public static long getMaxNumberVerificationTimeoutMillis();
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
     method public int getSimApplicationState();
@@ -9390,15 +9046,14 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
     method public boolean isCurrentSimOperator(@NonNull String, int, @Nullable String);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
     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();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isManualNetworkSelectionAllowed();
     method public boolean isModemEnabledForSlot(int);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
@@ -9415,30 +9070,22 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
-    method public void requestModemActivityInfo(@NonNull android.os.ResultReceiver);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int);
+    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
     method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAlwaysReportSignalStrength(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallForwarding(@NonNull android.telephony.CallForwardingInfo);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaRoamingMode(int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaSubscriptionMode(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIccLockEnabled(boolean, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
@@ -9452,15 +9099,13 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoiceActivationState(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void shutdownAllRadios();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPin(String);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult supplyPinReportPinResult(@NonNull String);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPinReportResult(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean supplyPuk(String, String);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult supplyPukReportPinResult(@NonNull String, @NonNull String);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int[] supplyPukReportResult(String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
+    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
     method public void updateServiceLocation();
-    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String);
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED";
     field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
     field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
@@ -9470,60 +9115,32 @@
     field public static final String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
     field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
     field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
-    field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
     field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
     field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
     field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
-    field public static final int CALL_WAITING_STATUS_ACTIVE = 1; // 0x1
-    field public static final int CALL_WAITING_STATUS_INACTIVE = 2; // 0x2
-    field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
-    field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
-    field public static final int CARD_POWER_DOWN = 0; // 0x0
-    field public static final int CARD_POWER_UP = 1; // 0x1
-    field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
-    field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
-    field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
-    field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
-    field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff
     field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
     field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
     field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
     field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
     field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
     field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
-    field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
     field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
-    field public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
-    field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; // 0x4
-    field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA = 1; // 0x1
-    field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE = 0; // 0x0
-    field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS = 3; // 0x3
-    field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE = 2; // 0x2
     field public static final String EXTRA_ERROR_CODE = "errorCode";
     field public static final String EXTRA_PCO_ID = "pcoId";
     field public static final String EXTRA_PCO_VALUE = "pcoValue";
     field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
     field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
-    field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
     field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
-    field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
-    field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
-    field public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES";
-    field public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
-    field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; // 0x1
-    field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; // 0x0
     field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
-    field public static final String EXTRA_SPN = "android.telephony.extra.SPN";
     field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
     field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
     field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
     field public static final int KEY_TYPE_EPDG = 1; // 0x1
     field public static final int KEY_TYPE_WLAN = 2; // 0x2
-    field public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
     field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
     field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
     field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
@@ -9544,7 +9161,6 @@
     field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
     field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
     field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
-    field public static final int PHONE_TYPE_THIRD_PARTY = 4; // 0x4
     field public static final int RADIO_POWER_OFF = 0; // 0x0
     field public static final int RADIO_POWER_ON = 1; // 0x1
     field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -9624,23 +9240,6 @@
 
 package android.telephony.data {
 
-  public class ApnSetting implements android.os.Parcelable {
-    method @NonNull public static String getApnTypesStringFromBitmask(int);
-    field public static final String TYPE_ALL_STRING = "*";
-    field public static final String TYPE_CBS_STRING = "cbs";
-    field public static final String TYPE_DEFAULT_STRING = "default";
-    field public static final String TYPE_DUN_STRING = "dun";
-    field public static final String TYPE_EMERGENCY_STRING = "emergency";
-    field public static final String TYPE_FOTA_STRING = "fota";
-    field public static final String TYPE_HIPRI_STRING = "hipri";
-    field public static final String TYPE_IA_STRING = "ia";
-    field public static final String TYPE_IMS_STRING = "ims";
-    field public static final String TYPE_MCX_STRING = "mcx";
-    field public static final String TYPE_MMS_STRING = "mms";
-    field public static final String TYPE_SUPL_STRING = "supl";
-    field public static final String TYPE_XCAP_STRING = "xcap";
-  }
-
   public final class DataCallResponse implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.net.LinkAddress> getAddresses();
@@ -9966,7 +9565,7 @@
     method public int getEmergencyServiceCategories();
     method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
     method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
-    method @Nullable public android.os.Bundle getProprietaryCallExtras();
+    method @NonNull public android.os.Bundle getProprietaryCallExtras();
     method public int getRestrictCause();
     method public int getServiceType();
     method public static int getVideoStateFromCallType(int);
@@ -10126,11 +9725,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
-  public class ImsManager {
-    method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
-    field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
-  }
-
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
     method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -10155,10 +9749,6 @@
     ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
   }
 
-  public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
-    method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
-  }
-
   public final class ImsReasonInfo implements android.os.Parcelable {
     field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
   }
@@ -10400,82 +9990,12 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
-    field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
-    field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
-    field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
-    field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
-    field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
-    field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
-    field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
-    field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
-    field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
-    field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
-    field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
-    field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
-    field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
-    field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
-    field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
-    field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
-    field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
-    field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
-    field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
-    field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
-    field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
-    field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
-    field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
-    field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
-    field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
-    field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
-    field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
-    field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
-    field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
-    field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
-    field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
-    field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
-    field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
-    field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
-    field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
-    field public static final int KEY_RTT_ENABLED = 66; // 0x42
-    field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
-    field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
-    field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
-    field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
-    field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
-    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
-    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
-    field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
-    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
-    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
-    field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
-    field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
-    field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
-    field public static final int KEY_SMS_FORMAT = 13; // 0xd
-    field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
-    field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
-    field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
-    field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
-    field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
-    field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
-    field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
-    field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
-    field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
     field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
     field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
-    field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
-    field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
-    field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
-    field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
-    field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
-    field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
-    field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
     field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
     field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
-    field public static final int SMS_FORMAT_3GPP = 1; // 0x1
-    field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
     field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
     field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
-    field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
-    field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
   }
 
   public static class ProvisioningManager.Callback {
@@ -10484,58 +10004,7 @@
     method public void onProvisioningStringChanged(int, @NonNull String);
   }
 
-  public final class RcsContactUceCapability implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
-    method @NonNull public android.net.Uri getContactUri();
-    method @Nullable public android.net.Uri getServiceUri(long);
-    method public boolean isCapable(long);
-    method public boolean isCapable(@NonNull String);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
-    field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000
-    field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000
-    field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
-    field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
-    field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
-    field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
-    field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
-    field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
-    field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
-    field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
-    field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
-    field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
-    field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
-    field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
-    field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
-    field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
-    field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
-    field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
-    field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
-    field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
-    field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
-    field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
-    field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
-    field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
-    field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
-    field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
-    field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
-    field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
-    field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
-    field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
-  }
-
-  public static class RcsContactUceCapability.Builder {
-    ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability build();
-  }
-
   public class RcsUceAdapter {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
   }
 
@@ -10787,7 +10256,6 @@
     method public int transact(android.os.Bundle);
     method public int updateCallBarring(int, int, String[]);
     method public int updateCallBarringForServiceClass(int, int, String[], int);
-    method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
     method public int updateCallForward(int, int, String, int, int);
     method public int updateCallWaiting(boolean, int);
     method public int updateClip(boolean);
@@ -10852,6 +10320,7 @@
   public class MbmsDownloadServiceBase extends android.os.Binder implements android.os.IInterface {
     ctor public MbmsDownloadServiceBase();
     method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+    method public int addServiceAnnouncementFile(int, @NonNull byte[]);
     method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
     method public android.os.IBinder asBinder();
     method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index d2b3a64..306b8af 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -165,12 +165,6 @@
 PublicTypedef: android.content.integrity.Rule.Effect: Don't expose @IntDef: @Effect must be hidden.
 
 
-ResourceValueFieldName: android.R.array#config_sms_enabled_locking_shift_tables:
-    Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_locking_shift_tables`
-ResourceValueFieldName: android.R.array#config_sms_enabled_single_shift_tables:
-    Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_single_shift_tables`
-
-
 SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
     
 SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
diff --git a/api/test-current.txt b/api/test-current.txt
index 0c7db06b..9747647 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -657,7 +657,6 @@
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
     field public static final String ETHERNET_SERVICE = "ethernet";
-    field public static final String NETWORK_STACK_SERVICE = "network_stack";
     field public static final String PERMISSION_SERVICE = "permission";
     field public static final String ROLLBACK_SERVICE = "rollback";
     field public static final String STATUS_BAR_SERVICE = "statusbar";
@@ -1201,6 +1200,13 @@
     ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String);
   }
 
+  public class AudioSystem {
+    method public static float getMasterBalance();
+    method public static final int getNumStreamTypes();
+    method public static int setMasterBalance(float);
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+  }
+
   public static final class AudioTrack.MetricsConstants {
     field public static final String ATTRIBUTES = "android.media.audiotrack.attributes";
     field public static final String CHANNEL_MASK = "android.media.audiotrack.channelMask";
@@ -1434,6 +1440,7 @@
   }
 
   public class ConnectivityManager {
+    method @RequiresPermission(anyOf={"android.permission.MANAGE_TEST_NETWORKS", android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
@@ -1441,6 +1448,7 @@
 
   public class EthernetManager {
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+    method public void setIncludeTestInterfaces(boolean);
   }
 
   public static interface EthernetManager.TetheredInterfaceCallback {
@@ -1477,6 +1485,7 @@
 
   public final class LinkProperties implements android.os.Parcelable {
     ctor public LinkProperties(@Nullable android.net.LinkProperties);
+    ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
     method public boolean addDnsServer(@NonNull java.net.InetAddress);
     method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
     method @Nullable public android.net.Uri getCaptivePortalApiUrl();
@@ -1491,7 +1500,6 @@
     method public boolean isIpv6Provisioned();
     method public boolean isProvisioned();
     method public boolean isReachable(@NonNull java.net.InetAddress);
-    method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy();
     method public boolean removeDnsServer(@NonNull java.net.InetAddress);
     method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
     method public boolean removeRoute(@NonNull android.net.RouteInfo);
@@ -1506,17 +1514,42 @@
 
   public class Network implements android.os.Parcelable {
     ctor public Network(@NonNull android.net.Network);
+    method public int getNetId();
     method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
   }
 
   public final class NetworkCapabilities implements android.os.Parcelable {
+    method @NonNull public int[] getAdministratorUids();
     method public int[] getCapabilities();
+    method @Nullable public String getSsid();
     method @NonNull public int[] getTransportTypes();
     method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
     field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
+  public static class NetworkCapabilities.Builder {
+    ctor public NetworkCapabilities.Builder();
+    ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
+    method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
+    method @NonNull public android.net.NetworkCapabilities build();
+    method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
+    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
+    method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
+    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setOwnerUid(int);
+    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
+    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setRequestorUid(int);
+    method @NonNull @RequiresPermission("android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP") public android.net.NetworkCapabilities.Builder setSignalStrength(int);
+    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+    method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
+  }
+
   public class NetworkStack {
+    method @Nullable public static android.os.IBinder getService();
+    method public static void setServiceForTest(@Nullable android.os.IBinder);
     field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
   }
 
@@ -1590,7 +1623,6 @@
     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 @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 @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
@@ -1607,19 +1639,20 @@
     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_DISABLE_FORWARDING_ERROR = 9; // 0x9
+    field public static final int TETHER_ERROR_ENABLE_FORWARDING_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_INTERNAL_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_PROVISIONING_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_UNKNOWN_TYPE = 16; // 0x10
     field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
     field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
     field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
@@ -1631,40 +1664,35 @@
     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 static interface TetheringManager.StartTetheringCallback {
+    method public default void onTetheringFailed(int);
+    method public default 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 public void onOffloadStatusChanged(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 interface TetheringManager.TetheringEventCallback {
+    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+    method public default void onError(@NonNull String, int);
+    method public default void onOffloadStatusChanged(int);
+    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheringSupported(boolean);
+    method public default void onUpstreamChanged(@Nullable android.net.Network);
   }
 
   public static class TetheringManager.TetheringRequest {
+    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
+    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
+    method public boolean getShouldShowEntitlementUi();
+    method public int getTetheringType();
+    method public boolean isExemptFromEntitlementCheck();
   }
 
   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);
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
   }
 
   public class TrafficStats {
@@ -3095,6 +3123,15 @@
     field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0
   }
 
+  public final class BarringInfo implements android.os.Parcelable {
+    ctor public BarringInfo();
+    ctor public BarringInfo(@Nullable android.telephony.CellIdentity, @NonNull android.util.SparseArray<android.telephony.BarringInfo.BarringServiceInfo>);
+  }
+
+  public static final class BarringInfo.BarringServiceInfo implements android.os.Parcelable {
+    ctor public BarringInfo.BarringServiceInfo(int, boolean, int, int);
+  }
+
   public final class CallQuality implements android.os.Parcelable {
     ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
     ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
@@ -3181,6 +3218,7 @@
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setCellIdentity(@Nullable android.telephony.CellIdentity);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setDomain(int);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setEmergencyOnly(boolean);
+    method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegisteredPlmn(@Nullable String);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegistrationState(int);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRejectCause(int);
     method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int);
@@ -3219,7 +3257,8 @@
 
   public final class SmsManager {
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int checkSmsShortCodeDestination(String, String);
-    method public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
+    method @Deprecated public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
+    method public void sendMultipartTextMessage(@NonNull String, @NonNull String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String, @Nullable String);
     field public static final int SMS_CATEGORY_FREE_SHORT_CODE = 1; // 0x1
     field public static final int SMS_CATEGORY_NOT_SHORT_CODE = 0; // 0x0
     field public static final int SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3; // 0x3
@@ -3244,17 +3283,17 @@
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
     method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
-    method public int getEmergencyNumberDbVersion();
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getEmergencyNumberDbVersion();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
-    method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int);
     method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
     method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
+    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void resetOtaEmergencyNumberDbFilePath();
     method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
     method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
-    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String);
+    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -3318,7 +3357,7 @@
     method public int getEmergencyServiceCategories();
     method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
     method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
-    method @Nullable public android.os.Bundle getProprietaryCallExtras();
+    method @NonNull public android.os.Bundle getProprietaryCallExtras();
     method public int getRestrictCause();
     method public int getServiceType();
     method public static int getVideoStateFromCallType(int);
@@ -3479,11 +3518,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
-  public class ImsManager {
-    method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
-    field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
-  }
-
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
     method @Deprecated @NonNull @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -3508,10 +3542,6 @@
     ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
   }
 
-  public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
-    method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
-  }
-
   public class ImsService extends android.app.Service {
     ctor public ImsService();
     method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
@@ -3749,82 +3779,12 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
-    field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
-    field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
-    field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
-    field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
-    field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
-    field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
-    field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
-    field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
-    field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
-    field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
-    field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
-    field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
-    field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
-    field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
-    field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
-    field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
-    field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
-    field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
-    field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
-    field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
-    field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
-    field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
-    field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
-    field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
-    field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
-    field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
-    field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
-    field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
-    field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
-    field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
-    field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
-    field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
-    field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
-    field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
-    field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
-    field public static final int KEY_RTT_ENABLED = 66; // 0x42
-    field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
-    field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
-    field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
-    field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
-    field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
-    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
-    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
-    field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
-    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
-    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
-    field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
-    field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
-    field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
-    field public static final int KEY_SMS_FORMAT = 13; // 0xd
-    field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
-    field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
-    field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
-    field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
-    field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
-    field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
-    field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
-    field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
-    field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
     field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
     field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
-    field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
-    field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
-    field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
-    field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
-    field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
-    field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
-    field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
     field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
     field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
-    field public static final int SMS_FORMAT_3GPP = 1; // 0x1
-    field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
     field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
     field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
-    field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
-    field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
   }
 
   public static class ProvisioningManager.Callback {
@@ -3833,58 +3793,7 @@
     method public void onProvisioningStringChanged(int, @NonNull String);
   }
 
-  public final class RcsContactUceCapability implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
-    method @NonNull public android.net.Uri getContactUri();
-    method @Nullable public android.net.Uri getServiceUri(long);
-    method public boolean isCapable(long);
-    method public boolean isCapable(@NonNull String);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
-    field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000
-    field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000
-    field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
-    field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
-    field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
-    field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
-    field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
-    field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
-    field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
-    field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
-    field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
-    field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
-    field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
-    field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
-    field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
-    field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
-    field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
-    field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
-    field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
-    field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
-    field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
-    field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
-    field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
-    field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
-    field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
-    field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
-    field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
-    field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
-    field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
-    field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
-  }
-
-  public static class RcsContactUceCapability.Builder {
-    ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability build();
-  }
-
   public class RcsUceAdapter {
-    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
   }
 
@@ -4136,7 +4045,6 @@
     method public int transact(android.os.Bundle);
     method public int updateCallBarring(int, int, String[]);
     method public int updateCallBarringForServiceClass(int, int, String[], int);
-    method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
     method public int updateCallForward(int, int, String, int, int);
     method public int updateCallWaiting(boolean, int);
     method public int updateClip(boolean);
@@ -4191,6 +4099,7 @@
   public class MbmsDownloadServiceBase extends android.os.Binder implements android.os.IInterface {
     ctor public MbmsDownloadServiceBase();
     method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+    method public int addServiceAnnouncementFile(int, @NonNull byte[]);
     method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
     method public android.os.IBinder asBinder();
     method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index 8be95e4..07221f9 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -5,11 +5,13 @@
 
     multilib: {
         lib32: {
-            version_script: ":art_sigchain_version_script32.txt",
+            // TODO(b/142944043): Remove version script when libsigchain is a DSO.
+            version_script: "version-script32.txt",
             suffix: "32",
         },
         lib64: {
-            version_script: ":art_sigchain_version_script64.txt",
+            // TODO(b/142944043): Remove version script when libsigchain is a DSO.
+            version_script: "version-script64.txt",
             suffix: "64",
         },
     },
diff --git a/cmds/app_process/version-script32.txt b/cmds/app_process/version-script32.txt
new file mode 100644
index 0000000..70810e0
--- /dev/null
+++ b/cmds/app_process/version-script32.txt
@@ -0,0 +1,15 @@
+{
+global:
+  EnsureFrontOfChain;
+  AddSpecialSignalHandlerFn;
+  RemoveSpecialSignalHandlerFn;
+  SkipAddSignalHandler;
+  bsd_signal;
+  sigaction;
+  sigaction64;
+  signal;
+  sigprocmask;
+  sigprocmask64;
+local:
+  *;
+};
diff --git a/cmds/app_process/version-script64.txt b/cmds/app_process/version-script64.txt
new file mode 100644
index 0000000..7bcd76b
--- /dev/null
+++ b/cmds/app_process/version-script64.txt
@@ -0,0 +1,14 @@
+{
+global:
+  EnsureFrontOfChain;
+  AddSpecialSignalHandlerFn;
+  RemoveSpecialSignalHandlerFn;
+  SkipAddSignalHandler;
+  sigaction;
+  sigaction64;
+  signal;
+  sigprocmask;
+  sigprocmask64;
+local:
+  *;
+};
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 3e5877b..c60d08b 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -28,6 +28,8 @@
     name: "bootanimation",
     defaults: ["bootanimation_defaults"],
 
+    header_libs: ["jni_headers"],
+
     shared_libs: [
         "libOpenSLES",
         "libbootanimation",
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index 9f4f314..ad4de0a 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -5,4 +5,4 @@
     disabled
     oneshot
     ioprio rt 0
-    writepid /dev/stune/top-app/tasks
+    task_profiles MaxPerformance
diff --git a/cmds/device_config/Android.bp b/cmds/device_config/Android.bp
new file mode 100644
index 0000000..67e014a
--- /dev/null
+++ b/cmds/device_config/Android.bp
@@ -0,0 +1,7 @@
+// Copyright 2018 The Android Open Source Project
+//
+
+sh_binary {
+    name: "device_config",
+    src: "device_config",
+}
diff --git a/cmds/device_config/Android.mk b/cmds/device_config/Android.mk
deleted file mode 100644
index 4041e01..0000000
--- a/cmds/device_config/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright 2018 The Android Open Source Project
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := device_config
-LOCAL_SRC_FILES := device_config
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_PREBUILT)
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index bb8d927..f482191 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -50,7 +50,7 @@
   std::string overlay_apk_path;
   std::string idmap_path;
   std::vector<std::string> policies;
-  bool ignore_overlayable;
+  bool ignore_overlayable = false;
 
   const CommandLineOptions opts =
       CommandLineOptions("idmap2 create")
diff --git a/cmds/idmap2/idmap2/Dump.cpp b/cmds/idmap2/idmap2/Dump.cpp
index 8716bf3..47f442a 100644
--- a/cmds/idmap2/idmap2/Dump.cpp
+++ b/cmds/idmap2/idmap2/Dump.cpp
@@ -39,7 +39,7 @@
 Result<Unit> Dump(const std::vector<std::string>& args) {
   SYSTRACE << "Dump " << args;
   std::string idmap_path;
-  bool verbose;
+  bool verbose = false;
 
   const CommandLineOptions opts =
       CommandLineOptions("idmap2 dump")
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 8a48f4b..499eb99 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -131,7 +131,6 @@
   ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020000 string/str1"), std::string::npos);
   ASSERT_NE(result->stdout.find("0x7f02000e -> 0x7f020001 string/str3"), std::string::npos);
   ASSERT_NE(result->stdout.find("0x7f02000f -> 0x7f020002 string/str4"), std::string::npos);
-  ASSERT_EQ(result->stdout.find("00000210:     007f  target package id"), std::string::npos);
 
   // clang-format off
   result = ExecuteBinary({"idmap2",
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index f476fcf..1cc761f 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -512,8 +512,8 @@
     // Open log buffer and getting logs since last retrieved time if any.
     unique_ptr<logger_list, void (*)(logger_list*)> loggers(
             gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end()
-                    ? android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0)
-                    : android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                    ? android_logger_list_alloc(ANDROID_LOG_NONBLOCK, 0, 0)
+                    : android_logger_list_alloc_time(ANDROID_LOG_NONBLOCK,
                                                      gLastLogsRetrieved[mLogID], 0),
             android_logger_list_free);
 
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 72a8bea..24fbf21 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -382,7 +382,7 @@
 // ====  java proto device library (for test only)  ==============================
 java_library {
     name: "statsdprotolite",
-    sdk_version: "core_platform",
+    sdk_version: "core_current",
     proto: {
         type: "lite",
         include_dirs: ["external/protobuf/src"],
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 97fe7cd..52e9385 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -149,8 +149,10 @@
         HardwareFailed hardware_failed = 72;
         PhysicalDropDetected physical_drop_detected = 73;
         ChargeCyclesReported charge_cycles_reported = 74;
-        MobileConnectionStateChanged mobile_connection_state_changed = 75;
-        MobileRadioTechnologyChanged mobile_radio_technology_changed = 76;
+        MobileConnectionStateChanged mobile_connection_state_changed =
+                75 [(log_from_module) = "telephony"];
+        MobileRadioTechnologyChanged mobile_radio_technology_changed =
+                76 [(log_from_module) = "telephony"];
         UsbDeviceAttached usb_device_attached = 77;
         AppCrashOccurred app_crash_occurred = 78;
         ANROccurred anr_occurred = 79;
@@ -1531,7 +1533,7 @@
  * Logged from:
  *    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java
  *
- * Next Tag: 5
+ * Next Tag: 6
  */
 message BluetoothConnectionStateChanged {
     // The state of the connection.
@@ -1553,6 +1555,15 @@
     // Size: 32 byte
     // Default: null or empty if the device identifier is not known
     optional bytes new_obfuscated_id = 4 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 5;
 }
 
 /**
@@ -1561,7 +1572,7 @@
  * Logged from:
  *    packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java
  *
- * Next Tag: 3
+ * Next Tag: 4
  */
 message BluetoothAclConnectionStateChanged {
     // An identifier that can be used to match events for this device.
@@ -1574,6 +1585,15 @@
     // The state of the connection.
     // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED.
     optional android.bluetooth.ConnectionStateEnum state = 2;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 3;
 }
 
 /**
@@ -1583,7 +1603,7 @@
  *    packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
  *    packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetClientStateMachine.java
  *
- * Next Tag: 4
+ * Next Tag: 5
  */
 message BluetoothScoConnectionStateChanged {
     // An identifier that can be used to match events for this device.
@@ -1599,6 +1619,15 @@
     // Codec used for this SCO connection
     // Default: UNKNOWN
     optional android.bluetooth.hfp.ScoCodec codec = 3;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 4;
 }
 
 /**
@@ -1620,6 +1649,15 @@
     // Size: 32 byte
     // Default: null or empty if there is no active device for this profile
     optional bytes obfuscated_id = 2 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 3;
 }
 
 // Logs when there is an event affecting Bluetooth device's link layer connection.
@@ -1703,6 +1741,15 @@
     // HCI reason code associated with this event
     // Default: STATUS_UNKNOWN
     optional android.bluetooth.hci.StatusEnum reason_code = 9;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 10;
 }
 
 /**
@@ -1758,6 +1805,15 @@
     // Current audio coding mode
     // Default: AUDIO_CODING_MODE_UNKNOWN
     optional android.bluetooth.a2dp.AudioCodingModeEnum audio_coding_mode = 3;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 4;
 }
 
 /**
@@ -1796,6 +1852,15 @@
     optional int64 codec_specific_2 = 8;
     optional int64 codec_specific_3 = 9;
     optional int64 codec_specific_4 = 10;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 11;
 }
 
 /**
@@ -1838,6 +1903,15 @@
     optional int64 codec_specific_2 = 8;
     optional int64 codec_specific_3 = 9;
     optional int64 codec_specific_4 = 10;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 11;
 }
 
 /**
@@ -1861,6 +1935,15 @@
     // Number of bytes of PCM data that could not be read from the source
     // Default: 0
     optional int32 num_missing_pcm_bytes = 3;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 4;
 }
 
 /**
@@ -1891,6 +1974,15 @@
     // Number of encoded bytes dropped in this event
     // Default: 0
     optional int32 num_dropped_encoded_bytes = 5;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 6;
 }
 
 /**
@@ -1922,6 +2014,15 @@
     //   Units: dBm
     // Invalid when an out of range value is reported
     optional int32 rssi = 4;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 5;
 }
 
 /**
@@ -1949,6 +2050,15 @@
     // Range: uint16_t, 0-0xFFFF
     // Default: 0xFFFFF
     optional int32 failed_contact_counter = 4;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 5;
 }
 
 /**
@@ -1976,6 +2086,15 @@
     // Units: dBm
     // Invalid when an out of range value is reported
     optional int32 transmit_power_level = 4;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 5;
 }
 
 /**
@@ -2105,6 +2224,15 @@
     optional string hardware_version = 6;
     // Software version of this device
     optional string software_version = 7;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 8;
 }
 
 /**
@@ -2158,6 +2286,15 @@
     optional int32 attribute_id = 3;
     // Attribute value for the particular attribute
     optional bytes attribute_value = 4 [(android.os.statsd.log_mode) = MODE_BYTES];
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 5;
 }
 
 /**
@@ -2190,6 +2327,15 @@
     // Unbond Reason
     // Default: UNBOND_REASON_UNKNOWN
     optional android.bluetooth.UnbondReasonEnum unbond_reason = 6;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 7;
 }
 
 /**
@@ -2225,6 +2371,15 @@
     // A status value related to this specific event
     // Default: 0
     optional int64 event_value = 7;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 8;
 }
 
 /**
@@ -2250,6 +2405,15 @@
     // SMP failure reason code
     // Default: PAIRING_FAIL_REASON_DEFAULT
     optional android.bluetooth.smp.PairingFailReasonEnum smp_fail_reason = 4;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 5;
 }
 
 /**
@@ -2288,6 +2452,15 @@
     optional int32 server_port = 8;
     // Whether this is a server listener socket
     optional android.bluetooth.SocketRoleEnum is_server = 9;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 10;
 }
 
 /**
@@ -2311,6 +2484,15 @@
     // Also defined in: https://developer.android.com/reference/android/bluetooth/BluetoothClass
     // Default: 0
     optional int32 class_of_device = 2;
+    // An identifier that can be used to match events for this device.
+    // The incremental identifier is locally generated and guaranteed not derived
+    // from any globally unique hardware id.
+    // For paired devices, it stays consistent between Bluetooth toggling for the
+    // same remote device.
+    // For unpaired devices, it stays consistent within the same Bluetooth adapter
+    // session for the same remote device.
+    // Default: 0 if the device's metric id is unknown.
+    optional int32 metric_id = 3;
 }
 
 /**
@@ -5702,11 +5884,11 @@
  * Logs when a data stall event occurs.
  *
  * Log from:
- *     frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+ *     packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
  */
 message DataStallEvent {
     // Data stall evaluation type.
-    // See frameworks/base/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+    // See packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
     // Refer to the definition of DATA_STALL_EVALUATION_TYPE_*.
     optional int32 evaluation_type = 1;
     // See definition in data_stall_event.proto.
@@ -5719,6 +5901,10 @@
     optional com.android.server.connectivity.CellularData cell_info = 5 [(log_mode) = MODE_BYTES];
     // See definition in data_stall_event.proto.
     optional com.android.server.connectivity.DnsEvent dns_event = 6 [(log_mode) = MODE_BYTES];
+    // The tcp packets fail rate from the latest tcp polling.
+    optional int32 tcp_fail_rate = 7;
+    // Number of packets sent since the last received packet.
+    optional int32 tcp_sent_since_last_recv = 8;
 }
 
 /*
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index 3180b77..957ebfb 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -24,6 +24,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.sysprop.InitProperties;
 
 public class PowerCommand extends Svc.Command {
     private static final int FORCE_SUSPEND_DELAY_DEFAULT_MILLIS = 0;
@@ -103,6 +104,8 @@
                         pm.reboot(false, mode, true);
                     } catch (RemoteException e) {
                         maybeLogRemoteException("Failed to reboot.");
+                    } catch (Exception e) {
+                        System.err.println("Failed to reboot: " + e.getMessage());
                     }
                     return;
                 } else if ("shutdown".equals(args[1])) {
@@ -138,7 +141,9 @@
     // if it is already in shutdown flow.
     private void maybeLogRemoteException(String msg) {
         String powerProp = SystemProperties.get("sys.powerctl");
-        if (powerProp.isEmpty()) {
+        // Also check if userspace reboot is ongoing, since in case of userspace reboot value of the
+        // sys.powerctl property will be reset.
+        if (powerProp.isEmpty() && !InitProperties.userspace_reboot_in_progress().orElse(false)) {
             System.err.println(msg);
         }
     }
diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp
index 3a26063..c33d31f 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -28,9 +28,6 @@
     installable: false,
     args: "-stubpackages com.android.uiautomator.core:" +
           "com.android.uiautomator.testrunner",
-    api_tag_name: "UIAUTOMATOR",
-    api_filename: "uiautomator_api.txt",
-    removed_api_filename: "uiautomator_removed_api.txt",
 
     check_api: {
         current: {
diff --git a/config/OWNERS b/config/OWNERS
index 53f80e6..3d4924d 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -1,5 +1,5 @@
 # compat-team@ for changes to hiddenapi files
-per-file hiddenapi-* = andreionea@google.com, atrost@google.com, mathewi@google.com, satayev@google.com
+per-file hiddenapi-* = andreionea@google.com, mathewi@google.com, satayev@google.com
 
 # Escalations:
-per-file hiddenapi-* = bdc@google.com, narayan@google.com
\ No newline at end of file
+per-file hiddenapi-* = bdc@google.com, narayan@google.com
diff --git a/config/preloaded-classes b/config/preloaded-classes
index eb3879f..b05d02c 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -7438,6 +7438,7 @@
 sun.nio.fs.NativeBuffer$Deallocator
 sun.nio.fs.NativeBuffer
 sun.nio.fs.NativeBuffers
+sun.nio.fs.UnixChannelFactory
 sun.nio.fs.UnixChannelFactory$Flags
 sun.nio.fs.UnixConstants
 sun.nio.fs.UnixException
diff --git a/config/preloaded-classes-blacklist b/config/preloaded-classes-blacklist
index 7cfde8a..5e54559 100644
--- a/config/preloaded-classes-blacklist
+++ b/config/preloaded-classes-blacklist
@@ -4,4 +4,4 @@
 android.os.FileObserver
 android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
 android.widget.Magnifier
-sun.nio.fs.UnixChannelFactory
+
diff --git a/core/java/android/annotation/CallbackExecutor.java b/core/java/android/annotation/CallbackExecutor.java
index 5671a3d..4258f73 100644
--- a/core/java/android/annotation/CallbackExecutor.java
+++ b/core/java/android/annotation/CallbackExecutor.java
@@ -19,9 +19,6 @@
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.content.Context;
-import android.os.AsyncTask;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 import java.util.concurrent.Executor;
@@ -30,9 +27,10 @@
  * @paramDoc Callback and listener events are dispatched through this
  *           {@link Executor}, providing an easy way to control which thread is
  *           used. To dispatch events through the main thread of your
- *           application, you can use {@link Context#getMainExecutor()}. To
- *           dispatch events through a shared thread pool, you can use
- *           {@link AsyncTask#THREAD_POOL_EXECUTOR}.
+ *           application, you can use
+ *           {@link android.content.Context#getMainExecutor() Context.getMainExecutor()}.
+ *           To dispatch events through a shared thread pool, you can use
+ *           {@link android.os.AsyncTask#THREAD_POOL_EXECUTOR AsyncTask#THREAD_POOL_EXECUTOR}.
  * @hide
  */
 @Retention(SOURCE)
diff --git a/core/java/android/annotation/OWNERS b/core/java/android/annotation/OWNERS
index 8aceb56..e1ef544 100644
--- a/core/java/android/annotation/OWNERS
+++ b/core/java/android/annotation/OWNERS
@@ -1,3 +1,3 @@
 tnorbye@google.com
 aurimas@google.com
-per-file UnsupportedAppUsage.java = mathewi@google.com, dbrazdil@google.com, atrost@google.com, andreionea@google.com
+per-file UnsupportedAppUsage.java = mathewi@google.com, satayev@google.com, andreionea@google.com
diff --git a/core/java/android/annotation/RequiresPermission.java b/core/java/android/annotation/RequiresPermission.java
index e5c0654..1d89e31 100644
--- a/core/java/android/annotation/RequiresPermission.java
+++ b/core/java/android/annotation/RequiresPermission.java
@@ -15,9 +15,6 @@
  */
 package android.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
 import static java.lang.annotation.ElementType.FIELD;
@@ -25,6 +22,9 @@
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that the annotated element requires (or may require) one or more permissions.
  * <p/>
@@ -55,7 +55,8 @@
  * <p>
  * When specified on a parameter, the annotation indicates that the method requires
  * a permission which depends on the value of the parameter. For example, consider
- * {@link android.app.Activity#startActivity(android.content.Intent)}:
+ * {@link android.app.Activity#startActivity(android.content.Intent)
+ * Activity#startActivity(Intent)}:
  * <pre>{@code
  *   public void startActivity(@RequiresPermission Intent intent) { ... }
  * }</pre>
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b6f61a2..4f1d7f2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3618,7 +3618,6 @@
      * To receive this callback, you must return true from onKeyDown for the current
      * event stream.
      *
-     * @see KeyEvent.Callback#onKeyLongPress()
      * @see KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
      */
     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e4c07db..85cd21d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2082,8 +2082,7 @@
      * has access to it.
      *
      * @see ActivityOptions#setLaunchDisplayId(int)
-     * @see android.view.Display.FLAG_PRIVATE
-     * @see android.view.Display.TYPE_VIRTUAL
+     * @see android.view.Display#FLAG_PRIVATE
      *
      * @param context Source context, from which an activity will be started.
      * @param displayId Target display id.
@@ -3871,6 +3870,28 @@
     }
 
     /**
+     * Updates mcc mnc configuration and applies changes to the entire system.
+     *
+     * @param mcc mcc configuration to update.
+     * @param mnc mnc configuration to update.
+     * @throws RemoteException; IllegalArgumentException if mcc or mnc is null;
+     * @return Returns {@code true} if the configuration was updated successfully;
+     *         {@code false} otherwise.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION)
+    public boolean updateMccMncConfiguration(@NonNull String mcc, @NonNull String mnc) {
+        if (mcc == null || mnc == null) {
+            throw new IllegalArgumentException("mcc or mnc cannot be null.");
+        }
+        try {
+            return getService().updateMccMncConfiguration(mcc, mnc);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Logs out current current foreground user by switching to the system user and stopping the
      * user being switched from.
      * @hide
@@ -4334,9 +4355,9 @@
 
     /**
      * Register with {@link HomeVisibilityObserver} with ActivityManager.
+     * TODO: b/144351078 expose as SystemApi
      * @hide
      */
-    @SystemApi
     public void registerHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) {
         Preconditions.checkNotNull(observer);
         try {
@@ -4351,9 +4372,9 @@
 
     /**
      * Unregister with {@link HomeVisibilityObserver} with ActivityManager.
+     * TODO: b/144351078 expose as SystemApi
      * @hide
      */
-    @SystemApi
     public void unregisterHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) {
         Preconditions.checkNotNull(observer);
         try {
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index b669d77..647f630 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -37,7 +37,6 @@
 
 import libcore.timezone.ZoneInfoDb;
 
-import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -998,12 +997,7 @@
 
         // Reject this timezone if it isn't an Olson zone we recognize.
         if (mTargetSdkVersion >= Build.VERSION_CODES.M) {
-            boolean hasTimeZone = false;
-            try {
-                hasTimeZone = ZoneInfoDb.getInstance().hasTimeZone(timeZone);
-            } catch (IOException ignored) {
-            }
-
+            boolean hasTimeZone = ZoneInfoDb.getInstance().hasTimeZone(timeZone);
             if (!hasTimeZone) {
                 throw new IllegalArgumentException("Timezone: " + timeZone + " is not an Olson ID");
             }
diff --git a/core/java/android/app/HomeVisibilityObserver.java b/core/java/android/app/HomeVisibilityObserver.java
index f3465f8..8422c6f 100644
--- a/core/java/android/app/HomeVisibilityObserver.java
+++ b/core/java/android/app/HomeVisibilityObserver.java
@@ -16,7 +16,6 @@
 
 package android.app;
 
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -28,9 +27,9 @@
  * An observer / callback to create and register by
  * {@link ActivityManager#registerHomeVisibilityObserver} so that it's triggered when
  * visibility of home page changes.
+ * TODO: b/144351078 expose as SystemApi
  * @hide
  */
-@SystemApi
 public abstract class HomeVisibilityObserver {
     private Context mContext;
     private ActivityManager mActivityManager;
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index feaddda..0feed73 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -188,6 +188,16 @@
      */
     @UnsupportedAppUsage
     boolean updateConfiguration(in Configuration values);
+    /**
+     * Updates mcc mnc configuration and applies changes to the entire system.
+     *
+     * @param mcc mcc configuration to update.
+     * @param mnc mnc configuration to update.
+     * @throws RemoteException; IllegalArgumentException if mcc or mnc is null.
+     * @return Returns {@code true} if the configuration was updated;
+     *         {@code false} otherwise.
+     */
+    boolean updateMccMncConfiguration(in String mcc, in String mnc);
     boolean stopServiceToken(in ComponentName className, in IBinder token, int startId);
     @UnsupportedAppUsage
     void setProcessLimit(int max);
@@ -289,7 +299,8 @@
     void handleApplicationStrictModeViolation(in IBinder app, int penaltyMask,
             in StrictMode.ViolationInfo crashInfo);
     boolean isTopActivityImmersive();
-    void crashApplication(int uid, int initialPid, in String packageName, int userId, in String message);
+    void crashApplication(int uid, int initialPid, in String packageName, int userId,
+            in String message, boolean force);
     @UnsupportedAppUsage
     String getProviderMimeType(in Uri uri, int userId);
     // Cause the specified process to dump the specified heap.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index cff6411..2049b34 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -729,7 +729,7 @@
          * a non-null value if the intent needs to be intercepted.
          *
          * <p> Whenever a new activity is started, this method will be called on instances created
-         * using {@link #Instrumentation.ActivityMonitor()} to check if there is a match. In case
+         * using {@link #ActivityMonitor()} to check if there is a match. In case
          * of a match, the activity start will be blocked and the returned result will be used.
          *
          * @param intent The intent used for starting the activity.
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 2a72d43..71b2773 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -797,6 +797,11 @@
         makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
 
         String libraryPermittedPath = mDataDir;
+        if (mActivityThread == null) {
+            // In a zygote context where mActivityThread is null we can't access the app data dir
+            // and including this in libraryPermittedPath would cause SELinux denials.
+            libraryPermittedPath = "";
+        }
 
         if (isBundledApp) {
             // For bundled apps, add the base directory of the app (e.g.,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index cefec44..f2b2635 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3437,7 +3437,7 @@
         }
 
         /**
-         * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
+         * @deprecated use {@link #Builder(Context, String)}
          * instead. All posted Notifications must specify a NotificationChannel Id.
          */
         @Deprecated
@@ -7071,7 +7071,7 @@
          * Should be unique amongst all individuals in the conversation, and should be
          * consistent during re-posts of the notification.
          *
-         * @see Message#Notification.MessagingStyle.Message(CharSequence, long, CharSequence)
+         * @see Message#Message(CharSequence, long, CharSequence)
          *
          * @return this object for method chaining
          *
@@ -7091,7 +7091,7 @@
          * Should be <code>null</code> for messages by the current user, in which case
          * the platform will insert the user set in {@code MessagingStyle(Person)}.
          *
-         * @see Message#Notification.MessagingStyle.Message(CharSequence, long, CharSequence)
+         * @see Message#Message(CharSequence, long, CharSequence)
          *
          * @return this object for method chaining
          */
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index e25558f..925586f 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -294,7 +294,7 @@
      * </p>
      * </p>
      *
-     * @see {@link #addAutomaticZenRule(AutomaticZenRule)}
+     * @see #addAutomaticZenRule(AutomaticZenRule)
      */
     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_AUTOMATIC_ZEN_RULE =
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 7a736d6..4d972b1 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -347,14 +347,6 @@
             }
         });
 
-        registerService(Context.NETWORK_STACK_SERVICE, IBinder.class,
-                new StaticServiceFetcher<IBinder>() {
-                    @Override
-                    public IBinder createService() {
-                        return ServiceManager.getService(Context.NETWORK_STACK_SERVICE);
-                    }
-                });
-
         registerService(Context.TETHERING_SERVICE, TetheringManager.class,
                 new CachedServiceFetcher<TetheringManager>() {
             @Override
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 3633064..62c7329 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -70,19 +70,26 @@
      * also monitor this Intent in order to be informed of mode changes or
      * prevent the normal car UI from being displayed by setting the result
      * of the broadcast to {@link Activity#RESULT_CANCELED}.
+     * <p>
+     * This intent is broadcast when {@link #getCurrentModeType()} transitions to
+     * {@link Configuration#UI_MODE_TYPE_CAR} from some other ui mode.
      */
     public static String ACTION_ENTER_CAR_MODE = "android.app.action.ENTER_CAR_MODE";
 
     /**
-     * Broadcast sent when the device's UI has switched to car mode, either by being placed in a car
-     * dock or explicit action of the user.
+     * Broadcast sent when an app has entered car mode using either {@link #enableCarMode(int)} or
+     * {@link #enableCarMode(int, int)}.
      * <p>
-     * In addition to the behavior for {@link #ACTION_ENTER_CAR_MODE}, this broadcast includes the
-     * package name of the app which requested to enter car mode in the
-     * {@link #EXTRA_CALLING_PACKAGE}.  If an app requested to enter car mode using
-     * {@link #enableCarMode(int, int)} and specified a priority this will be specified in the
+     * Unlike {@link #ACTION_ENTER_CAR_MODE}, which is only sent when the global car mode state
+     * (i.e. {@link #getCurrentModeType()}) transitions to {@link Configuration#UI_MODE_TYPE_CAR},
+     * this intent is sent any time an app declares it has entered car mode.  Thus, this intent is
+     * intended for use by a component which needs to know not only when the global car mode state
+     * changed, but also when the highest priority app declaring car mode has changed.
+     * <p>
+     * This broadcast includes the package name of the app which requested to enter car mode in
+     * {@link #EXTRA_CALLING_PACKAGE}.  The priority the app entered car mode at is specified in
      * {@link #EXTRA_PRIORITY}.
-     *
+     * <p>
      * This is primarily intended to be received by other components of the Android OS.
      * <p>
      * Receiver requires permission: {@link android.Manifest.permission.HANDLE_CAR_MODE_CHANGES}
@@ -96,17 +103,25 @@
      * Broadcast sent when the device's UI has switch away from car mode back
      * to normal mode.  Typically used by a car mode app, to dismiss itself
      * when the user exits car mode.
+     * <p>
+     * This intent is broadcast when {@link #getCurrentModeType()} transitions from
+     * {@link Configuration#UI_MODE_TYPE_CAR} to some other ui mode.
      */
     public static String ACTION_EXIT_CAR_MODE = "android.app.action.EXIT_CAR_MODE";
 
     /**
-     * Broadcast sent when the device's UI has switched away from car mode back to normal mode.
-     * Typically used by a car mode app, to dismiss itself when the user exits car mode.
+     * Broadcast sent when an app has exited car mode using {@link #disableCarMode(int)}.
      * <p>
-     * In addition to the behavior for {@link #ACTION_EXIT_CAR_MODE}, this broadcast includes the
-     * package name of the app which requested to exit car mode in {@link #EXTRA_CALLING_PACKAGE}.
-     * If an app requested to enter car mode using {@link #enableCarMode(int, int)} and specified a
-     * priority this will be specified in the {@link #EXTRA_PRIORITY} when exiting car mode.
+     * Unlike {@link #ACTION_EXIT_CAR_MODE}, which is only sent when the global car mode state
+     * (i.e. {@link #getCurrentModeType()}) transitions to a non-car mode state such as
+     * {@link Configuration#UI_MODE_TYPE_NORMAL}, this intent is sent any time an app declares it
+     * has exited car mode.  Thus, this intent is intended for use by a component which needs to
+     * know not only when the global car mode state changed, but also when the highest priority app
+     * declaring car mode has changed.
+     * <p>
+     * This broadcast includes the package name of the app which requested to exit car mode in
+     * {@link #EXTRA_CALLING_PACKAGE}.  The priority the app originally entered car mode at is
+     * specified in {@link #EXTRA_PRIORITY}.
      * <p>
      * If {@link #DISABLE_CAR_MODE_ALL_PRIORITIES} is used when disabling car mode (i.e. this is
      * initiated by the user via the persistent car mode notification), this broadcast is sent once
@@ -251,9 +266,7 @@
      * An app may request to enter car mode when the system is already in car mode.  The app may
      * specify a "priority" when entering car mode.  The device will remain in car mode
      * (i.e. {@link #getCurrentModeType()} is {@link Configuration#UI_MODE_TYPE_CAR}) as long as
-     * there is a priority level at which car mode have been enabled.  For example assume app A
-     * enters car mode at priority level 100, and then app B enters car mode at the default priority
-     * (0).  If app A exits car mode, the device will remain in car mode until app B exits car mode.
+     * there is a priority level at which car mode have been enabled.
      * <p>
      * Specifying a priority level when entering car mode is important in cases where multiple apps
      * on a device implement a car-mode {@link android.telecom.InCallService} (see
@@ -266,18 +279,28 @@
      * correct conditions exist for that app to be in car mode.  The device maker should ensure that
      * where multiple apps exist on the device which can potentially enter car mode, appropriate
      * priorities are used to ensure that calls delivered by the
-     * {@link android.telecom.InCallService} API are delivered to the highest priority app.
-     * If app A and app B can both potentially enable car mode, and it is desired that app B is the
-     * one which should receive call information, the priority for app B should be higher than the
-     * one for app A.
+     * {@link android.telecom.InCallService} API are sent to the highest priority app given the
+     * desired behavior of the car mode experience on the device.
      * <p>
-     * When an app uses a priority to enable car mode, they can disable car mode at the specified
+     * If app A and app B both meet their own criteria to enable car mode, and it is desired that
+     * app B should be the one which should receive call information in that scenario, the priority
+     * for app B should be higher than the one for app A.  The higher priority of app B compared to
+     * A means it will be bound to during calls and app A will not.  When app B no longer meets its
+     * criteria for providing a car mode experience it uses {@link #disableCarMode(int)} to disable
+     * car mode at its priority level.  The system will then unbind from app B and bind to app A as
+     * it has the next highest priority.
+     * <p>
+     * When an app enables car mode at a certain priority, it can disable car mode at the specified
      * priority level using {@link #disableCarMode(int)}.  An app may only enable car mode at a
      * single priority.
      * <p>
-     * Public apps are assumed to enter/exit car mode at {@link #DEFAULT_PRIORITY}.
+     * Public apps are assumed to enter/exit car mode at the lowest priority,
+     * {@link #DEFAULT_PRIORITY}.
      *
-     * @param priority The declared priority for the caller.
+     * @param priority The declared priority for the caller, where {@link #DEFAULT_PRIORITY} (0) is
+     *                 the lowest priority and higher numbers represent a higher priority.
+     *                 The priorities apps declare when entering car mode is determined by the
+     *                 device manufacturer based on the desired car mode experience.
      * @param flags Car mode flags.
      * @hide
      */
@@ -322,11 +345,11 @@
     /**
      * The default priority used for entering car mode.
      * <p>
-     * Callers of the {@link UiModeManager#enableCarMode(int)} priority will be assigned the
-     * default priority.
+     * Callers of the {@link #enableCarMode(int)} priority will be assigned the default priority.
+     * This is considered the lowest possible priority for enabling car mode.
      * <p>
      * System apps can specify a priority other than the default priority when using
-     * {@link UiModeManager#enableCarMode(int, int)} to enable car mode.
+     * {@link #enableCarMode(int, int)} to enable car mode.
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 5afd82f..dee013c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5311,6 +5311,10 @@
         throwIfParentInstance("isAlwaysOnVpnLockdownEnabled");
         if (mService != null) {
             try {
+                // Starting from Android R, the caller can pass the permission check in
+                // DevicePolicyManagerService if it holds android.permission.MAINLINE_NETWORK_STACK.
+                // Note that the android.permission.MAINLINE_NETWORK_STACK is a signature permission
+                // which is used by the NetworkStack mainline module.
                 return mService.isAlwaysOnVpnLockdownEnabled(admin);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index dc815b6..067a35f 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -803,7 +803,7 @@
      * has a work profile that was restored from another work profile with serial number
      * {@code ancestralSerialNumber}.
      *
-     * @see UserManager#getSerialNumberForUser(UserHandle)
+     * @see android.os.UserManager#getSerialNumberForUser(UserHandle)
      */
     @Nullable
     public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index e289a27..6689507 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -17,6 +17,7 @@
 package android.app.compat;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.compat.Compatibility;
 import android.content.Context;
@@ -59,14 +60,13 @@
      * <p> Note that this involves a binder call to the system server (unless running in the system
      * server). If the binder call fails, a {@code RuntimeException} will be thrown.
      *
-     * <p> Caller must have android.permission.READ_COMPAT_CHANGE_CONFIG permission. If it
-     * doesn't, a {@code RuntimeException} will be thrown.
-     *
      * @param changeId    The ID of the compatibility change in question.
      * @param packageName The package name of the app in question.
      * @param user        The user that the operation is done for.
      * @return {@code true} if the change is enabled for the current app.
      */
+    @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+            android.Manifest.permission.LOG_COMPAT_CHANGE})
     public static boolean isChangeEnabled(long changeId, @NonNull String packageName,
             @NonNull UserHandle user) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
@@ -89,9 +89,6 @@
      * <p> Note that this involves a binder call to the system server (unless running in the system
      * server). If the binder call fails, {@code RuntimeException}  will be thrown.
      *
-     * <p> Caller must have android.permission.READ_COMPAT_CHANGE_CONFIG permission. If it
-     * doesn't, a {@code RuntimeException} will be thrown.
-     *
      * <p> Returns {@code true} if there are no installed packages for the required UID, or if the
      * change is enabled for ALL of the installed packages associated with the provided UID. Please
      * use a more specific API if you want a different behaviour for multi-package UIDs.
@@ -100,6 +97,8 @@
      * @param uid      The UID of the app in question.
      * @return {@code true} if the change is enabled for the current app.
      */
+    @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+            android.Manifest.permission.LOG_COMPAT_CHANGE})
     public static boolean isChangeEnabled(long changeId, int uid) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java
index 08b1c2b..ec9c574 100644
--- a/core/java/android/app/job/JobScheduler.java
+++ b/core/java/android/app/job/JobScheduler.java
@@ -41,7 +41,7 @@
  * system will execute this job on your application's {@link android.app.job.JobService}.
  * You identify the service component that implements the logic for your job when you
  * construct the JobInfo using
- * {@link android.app.job.JobInfo.Builder#JobInfo.Builder(int,android.content.ComponentName)}.
+ * {@link android.app.job.JobInfo.Builder#Builder(int,android.content.ComponentName)}.
  * </p>
  * <p>
  * The framework will be intelligent about when it executes jobs, and attempt to batch
@@ -147,7 +147,7 @@
      * method is ignored.
      *
      * @param jobId unique identifier for the job to be canceled, as supplied to
-     *     {@link JobInfo.Builder#JobInfo.Builder(int, android.content.ComponentName)
+     *     {@link JobInfo.Builder#Builder(int, android.content.ComponentName)
      *     JobInfo.Builder(int, android.content.ComponentName)}.
      */
     public abstract void cancel(int jobId);
diff --git a/core/java/android/app/role/OWNERS b/core/java/android/app/role/OWNERS
new file mode 100644
index 0000000..b94d988
--- /dev/null
+++ b/core/java/android/app/role/OWNERS
@@ -0,0 +1,6 @@
+svetoslavganov@google.com
+moltmann@google.com
+zhanghai@google.com
+evanseverson@google.com
+eugenesusla@google.com
+ntmyren@google.com
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 2eb9459..4e5443a 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -102,8 +102,6 @@
 
     /**
      * The name of the emergency role
-     *
-     * @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
      */
     public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY";
 
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
index 22e2efb..002c663 100644
--- a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
@@ -30,7 +30,7 @@
 import java.util.Objects;
 
 /**
- * A time signal from a manual (user provided) source.
+ * A time zone suggestion from a manual (user provided) source.
  *
  * <p>{@code zoneId} contains the suggested time zone ID, e.g. "America/Los_Angeles".
  *
@@ -124,7 +124,7 @@
 
     @Override
     public String toString() {
-        return "ManualTimeSuggestion{"
+        return "ManualTimeZoneSuggestion{"
                 + "mZoneId=" + mZoneId
                 + ", mDebugInfo=" + mDebugInfo
                 + '}';
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 20761ad..34a7586 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -41,6 +41,15 @@
     }
 
     /**
+     * Suggests the current time zone, determined using the user's manually entered information, to
+     * the detector. The detector may ignore the signal based on system settings.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE)
+    void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion);
+
+    /**
      * Suggests the current time zone, determined using telephony signals, to the detector. The
      * detector may ignore the signal based on system settings, whether better information is
      * available, and so on.
@@ -49,13 +58,4 @@
      */
     @RequiresPermission(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE)
     void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion);
-
-    /**
-     * Suggests the current time zone, determined for the user's manually information, to the
-     * detector. The detector may ignore the signal based on system settings.
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE)
-    void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion);
 }
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
index 0ada885..54cf1f3 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
@@ -40,18 +40,6 @@
     }
 
     @Override
-    public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) {
-        if (DEBUG) {
-            Log.d(TAG, "suggestTelephonyTimeZone called: " + timeZoneSuggestion);
-        }
-        try {
-            mITimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @Override
     public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
         if (DEBUG) {
             Log.d(TAG, "suggestManualTimeZone called: " + timeZoneSuggestion);
@@ -62,4 +50,16 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    @Override
+    public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) {
+        if (DEBUG) {
+            Log.d(TAG, "suggestTelephonyTimeZone called: " + timeZoneSuggestion);
+        }
+        try {
+            mITimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 5b98188..d6e7762 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -29,10 +29,10 @@
 import android.net.DataUsageRequest;
 import android.net.INetworkStatsService;
 import android.net.NetworkIdentity;
+import android.net.NetworkStack;
 import android.net.NetworkTemplate;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
-import android.net.netstats.provider.NetworkStatsProviderWrapper;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
@@ -527,32 +527,53 @@
 
     /**
      * Registers a custom provider of {@link android.net.NetworkStats} to provide network statistics
-     * to the system. To unregister, invoke {@link NetworkStatsProviderCallback#unregister()}.
+     * to the system. To unregister, invoke {@link #unregisterNetworkStatsProvider}.
      * Note that no de-duplication of statistics between providers is performed, so each provider
-     * must only report network traffic that is not being reported by any other provider.
+     * must only report network traffic that is not being reported by any other provider. Also note
+     * that the provider cannot be re-registered after unregistering.
      *
      * @param tag a human readable identifier of the custom network stats provider. This is only
      *            used for debugging.
-     * @param provider the subclass of {@link AbstractNetworkStatsProvider} that needs to be
+     * @param provider the subclass of {@link NetworkStatsProvider} that needs to be
      *                 registered to the system.
-     * @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the
-     *         system or unregister the provider.
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
-    @NonNull public NetworkStatsProviderCallback registerNetworkStatsProvider(
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STATS_PROVIDER,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+    @NonNull public void registerNetworkStatsProvider(
             @NonNull String tag,
-            @NonNull AbstractNetworkStatsProvider provider) {
+            @NonNull NetworkStatsProvider provider) {
         try {
-            final NetworkStatsProviderWrapper wrapper = new NetworkStatsProviderWrapper(provider);
-            return new NetworkStatsProviderCallback(
-                    mService.registerNetworkStatsProvider(tag, wrapper));
+            if (provider.getProviderCallbackBinder() != null) {
+                throw new IllegalArgumentException("provider is already registered");
+            }
+            final INetworkStatsProviderCallback cbBinder =
+                    mService.registerNetworkStatsProvider(tag, provider.getProviderBinder());
+            provider.setProviderCallbackBinder(cbBinder);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
-        // Unreachable code, but compiler doesn't know about it.
-        return null;
+    }
+
+    /**
+     * Unregisters an instance of {@link NetworkStatsProvider}.
+     *
+     * @param provider the subclass of {@link NetworkStatsProvider} that needs to be
+     *                 unregistered to the system.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STATS_PROVIDER,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+    @NonNull public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) {
+        try {
+            provider.getProviderCallbackBinderOrThrow().unregister();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
     }
 
     private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index d8c653c6..5374d6d 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -433,7 +433,7 @@
      * is active
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     @Nullable
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public BluetoothDevice getActiveDevice() {
@@ -462,7 +462,7 @@
      * @return true if priority is set, false on error
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -481,7 +481,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -517,7 +517,18 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
+        try {
+            final IBluetoothA2dp service = getService();
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device));
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return BluetoothProfile.PRIORITY_OFF;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.PRIORITY_OFF;
+        }
     }
 
     /**
@@ -532,7 +543,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         try {
@@ -640,11 +651,12 @@
      * @return the current codec status
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     @Nullable
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) {
+    public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
         if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
+        verifyDeviceNotNull(device, "getCodecStatus");
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled()) {
@@ -668,11 +680,16 @@
      * @param codecConfig the codec configuration preference
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void setCodecConfigPreference(@Nullable BluetoothDevice device,
-                                         @Nullable BluetoothCodecConfig codecConfig) {
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public void setCodecConfigPreference(@NonNull BluetoothDevice device,
+                                         @NonNull BluetoothCodecConfig codecConfig) {
         if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
+        verifyDeviceNotNull(device, "setCodecConfigPreference");
+        if (codecConfig == null) {
+            Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
+            throw new IllegalArgumentException("codecConfig cannot be null");
+        }
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled()) {
@@ -693,10 +710,11 @@
      * active A2DP Bluetooth device.
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void enableOptionalCodecs(@Nullable BluetoothDevice device) {
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public void enableOptionalCodecs(@NonNull BluetoothDevice device) {
         if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
+        verifyDeviceNotNull(device, "enableOptionalCodecs");
         enableDisableOptionalCodecs(device, true);
     }
 
@@ -707,10 +725,11 @@
      * active A2DP Bluetooth device.
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void disableOptionalCodecs(@Nullable BluetoothDevice device) {
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public void disableOptionalCodecs(@NonNull BluetoothDevice device) {
         if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
+        verifyDeviceNotNull(device, "disableOptionalCodecs");
         enableDisableOptionalCodecs(device, false);
     }
 
@@ -747,10 +766,11 @@
      * OPTIONAL_CODECS_SUPPORTED.
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     @OptionalCodecsSupportStatus
-    public int supportsOptionalCodecs(@Nullable BluetoothDevice device) {
+    public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
+        verifyDeviceNotNull(device, "isOptionalCodecsSupported");
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled() && isValidDevice(device)) {
@@ -772,10 +792,11 @@
      * OPTIONAL_CODECS_PREF_DISABLED.
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     @OptionalCodecsPreferenceStatus
-    public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) {
+    public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
+        verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled() && isValidDevice(device)) {
@@ -798,10 +819,11 @@
      * OPTIONAL_CODECS_PREF_DISABLED.
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device,
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
             @OptionalCodecsPreferenceStatus int value) {
+        verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
         try {
             if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
                     && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
@@ -854,6 +876,13 @@
         return false;
     }
 
+    private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
+        if (device == null) {
+            Log.e(TAG, methodName + ": device param is null");
+            throw new IllegalArgumentException("Device cannot be null");
+        }
+    }
+
     private boolean isValidDevice(BluetoothDevice device) {
         if (device == null) return false;
 
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index ab49230..53f87e6 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -17,7 +17,7 @@
 package android.bluetooth;
 
 import android.Manifest;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -120,7 +120,7 @@
      * @return false on immediate error, true otherwise
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
         final IBluetoothA2dpSink service = getService();
@@ -277,7 +277,7 @@
      * @return true if priority is set, false on error
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -297,7 +297,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothA2dpSink service = getService();
@@ -327,7 +327,7 @@
      * @return priority of the device
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -346,7 +346,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -371,7 +371,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public boolean isAudioPlaying(@Nullable BluetoothDevice device) {
+    public boolean isAudioPlaying(@NonNull BluetoothDevice device) {
         final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 0a9dbb6..1508a65 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -916,23 +916,11 @@
         if (!isBleScanAlwaysAvailable()) {
             return false;
         }
-
-        int state = getLeState();
-        if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) {
-            String packageName = ActivityThread.currentPackageName();
-            if (DBG) {
-                Log.d(TAG, "disableBLE(): de-registering " + packageName);
-            }
-            try {
-                mManagerService.updateBleAppCount(mToken, false, packageName);
-            } catch (RemoteException e) {
-                Log.e(TAG, "", e);
-            }
-            return true;
-        }
-
-        if (DBG) {
-            Log.d(TAG, "disableBLE(): Already disabled");
+        String packageName = ActivityThread.currentPackageName();
+        try {
+            return mManagerService.disableBle(packageName, mToken);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
         }
         return false;
     }
@@ -973,20 +961,9 @@
         if (!isBleScanAlwaysAvailable()) {
             return false;
         }
-
+        String packageName = ActivityThread.currentPackageName();
         try {
-            String packageName = ActivityThread.currentPackageName();
-            mManagerService.updateBleAppCount(mToken, true, packageName);
-            if (isLeEnabled()) {
-                if (DBG) {
-                    Log.d(TAG, "enableBLE(): Bluetooth already enabled");
-                }
-                return true;
-            }
-            if (DBG) {
-                Log.d(TAG, "enableBLE(): Calling enable");
-            }
-            return mManagerService.enable(packageName);
+            return mManagerService.enableBle(packageName, mToken);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
@@ -1214,7 +1191,7 @@
      * @return true to indicate that the config file was successfully cleared
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean factoryReset() {
         try {
@@ -1506,8 +1483,9 @@
      * @return true if the scan mode was set, false otherwise
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which "
+            + "shows UI that confirms the user wants to go into discoverable mode.")
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean setScanMode(@ScanMode int mode, long durationMillis) {
         if (getState() != STATE_ON) {
             return false;
@@ -1555,8 +1533,8 @@
      * @return true if the scan mode was set, false otherwise
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean setScanMode(@ScanMode int mode) {
         if (getState() != STATE_ON) {
             return false;
@@ -1620,7 +1598,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public long getDiscoveryEndMillis() {
         try {
             mServiceLock.readLock().lock();
@@ -1872,7 +1850,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) {
         try {
@@ -1901,7 +1878,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) {
         try {
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index 93e76fa..d2a1535 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,7 +33,6 @@
  *
  * {@hide}
  */
-@SystemApi
 public final class BluetoothCodecConfig implements Parcelable {
     // Add an entry for each source codec here.
     // NOTE: The values should be same as those listed in the following file:
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index b6e7739..1e394b8 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -17,7 +17,6 @@
 package android.bluetooth;
 
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,7 +31,6 @@
  *
  * {@hide}
  */
-@SystemApi
 public final class BluetoothCodecStatus implements Parcelable {
     /**
      * Extra for the codec configuration intents of the individual profiles.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 2a3f2be..a2cf7d9 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -35,8 +35,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.lang.annotation.Retention;
@@ -1103,8 +1101,8 @@
      * @return true on success, false on error
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean setAlias(@NonNull String alias) {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1145,8 +1143,8 @@
      * not have any battery reporting service, or return value is invalid
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getBatteryLevel() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1236,8 +1234,8 @@
      *
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean isBondingInitiatedLocally() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1531,7 +1529,7 @@
      * @return true pin has been set false for error
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean setPin(@NonNull String pin) {
         byte[] pinBytes = convertPinToBytes(pin);
@@ -1568,8 +1566,8 @@
      *
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean cancelPairing() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1600,8 +1598,8 @@
      * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public @AccessPermission int getPhonebookAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1708,8 +1706,8 @@
      * @return Whether the message access is allowed to this device.
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public @AccessPermission int getMessageAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1758,7 +1756,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public @AccessPermission int getSimAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -2013,7 +2011,7 @@
      * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin.
      * @hide
      */
-    @VisibleForTesting
+    @UnsupportedAppUsage
     public static byte[] convertPinToBytes(String pin) {
         if (pin == null) {
             return null;
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 1ba2bb5..6ce05f9 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -572,7 +572,22 @@
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
+        final IBluetoothHeadset service = mService;
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF
+                    && priority != BluetoothProfile.PRIORITY_ON) {
+                return false;
+            }
+            try {
+                return service.setPriority(
+                        device, BluetoothAdapter.priorityToConnectionPolicy(priority));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
     /**
@@ -588,7 +603,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -624,7 +639,17 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
-        return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
+        final IBluetoothHeadset service = mService;
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return BluetoothAdapter.connectionPolicyToPriority(service.getPriority(device));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.PRIORITY_OFF;
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.PRIORITY_OFF;
     }
 
     /**
@@ -639,7 +664,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothHeadset service = mService;
@@ -1126,16 +1151,13 @@
     /**
      * Get the connected device that is active.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
-     * permission.
-     *
      * @return the connected device that is active or null if no device
      * is active.
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     @Nullable
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public BluetoothDevice getActiveDevice() {
         if (VDBG) {
             Log.d(TAG, "getActiveDevice");
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index fbda9e9..85e0e08 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -442,6 +443,8 @@
      * @param device a remote device we want connect to
      * @return <code>true</code> if command has been issued successfully; <code>false</code>
      * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public boolean connect(BluetoothDevice device) {
@@ -466,6 +469,8 @@
      * @param device a remote device we want disconnect
      * @return <code>true</code> if command has been issued successfully; <code>false</code>
      * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
@@ -564,7 +569,7 @@
      * @return true if priority is set, false on error
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -583,8 +588,8 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(BluetoothDevice device,
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothHeadsetClient service =
@@ -634,7 +639,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothHeadsetClient service =
                 getService();
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 38498bc..fa62a02 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -162,13 +162,11 @@
      * the state. Users can get the connection state of the profile
      * from this intent.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     * permission.
-     *
      * @param device Remote Bluetooth Device
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
         final IBluetoothHearingAid service = getService();
@@ -202,13 +200,11 @@
      * {@link #STATE_DISCONNECTING} can be used to distinguish between the
      * two scenarios.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     * permission.
-     *
      * @param device Remote Bluetooth Device
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         final IBluetoothHearingAid service = getService();
@@ -327,15 +323,12 @@
     /**
      * Get the connected physical Hearing Aid devices that are active
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
-     * permission.
-     *
      * @return the list of active devices. The first element is the left active
      * device; the second element is the right active device. If either or both side
      * is not active, it will be null on that position. Returns empty list on error.
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public @NonNull List<BluetoothDevice> getActiveDevices() {
         if (VDBG) log("getActiveDevices()");
@@ -363,7 +356,7 @@
      * @return true if priority is set, false on error
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -382,10 +375,11 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+        verifyDeviceNotNull(device, "setConnectionPolicy");
         final IBluetoothHearingAid service = getService();
         try {
             if (service != null && isEnabled()
@@ -414,7 +408,7 @@
      * @return priority of the device
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -432,9 +426,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
+        verifyDeviceNotNull(device, "getConnectionPolicy");
         final IBluetoothHearingAid service = getService();
         try {
             if (service != null && isEnabled()
@@ -496,18 +491,22 @@
     }
 
     /**
-     * Get the CustomerId of the device.
+     * Get the HiSyncId (unique hearing aid device identifier) of the device.
+     *
+     * <a href=https://source.android.com/devices/bluetooth/asha#hisyncid>HiSyncId documentation
+     * can be found here</a>
      *
      * @param device Bluetooth device
-     * @return the CustomerId of the device
+     * @return the HiSyncId of the device
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public long getHiSyncId(@Nullable BluetoothDevice device) {
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public long getHiSyncId(@NonNull BluetoothDevice device) {
         if (VDBG) {
-            log("getCustomerId(" + device + ")");
+            log("getHiSyncId(" + device + ")");
         }
+        verifyDeviceNotNull(device, "getConnectionPolicy");
         final IBluetoothHearingAid service = getService();
         try {
             if (service == null) {
@@ -581,6 +580,13 @@
         return false;
     }
 
+    private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
+        if (device == null) {
+            Log.e(TAG, methodName + ": device param is null");
+            throw new IllegalArgumentException("Device cannot be null");
+        }
+    }
+
     private boolean isValidDevice(BluetoothDevice device) {
         if (device == null) return false;
 
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index e9e1f68..9561d93 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -263,13 +263,11 @@
      * the state. Users can get the connection state of the profile
      * from this intent.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     * permission.
-     *
      * @param device Remote Bluetooth Device
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
         final IBluetoothHidHost service = getService();
@@ -303,13 +301,11 @@
      * {@link #STATE_DISCONNECTING} can be used to distinguish between the
      * two scenarios.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     * permission.
-     *
      * @param device Remote Bluetooth Device
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         final IBluetoothHidHost service = getService();
@@ -327,7 +323,10 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @hide
      */
+    @SystemApi
     @Override
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @NonNull List<BluetoothDevice> getConnectedDevices() {
@@ -368,7 +367,10 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @hide
      */
+    @SystemApi
     @Override
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public int getConnectionState(@NonNull BluetoothDevice device) {
@@ -400,7 +402,7 @@
      * @return true if priority is set, false on error
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -419,7 +421,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -453,7 +455,7 @@
      * @return priority of the device
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -471,7 +473,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         if (device == null) {
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index cc2b615..14a71c4 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -18,7 +18,6 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -328,7 +327,7 @@
      * @return true if priority is set, false on error
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -347,8 +346,8 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothMap service = getService();
@@ -378,7 +377,7 @@
      * @return priority of the device
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -396,8 +395,8 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothMap service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 8d2aadd..19240dc 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.PendingIntent;
@@ -140,7 +141,10 @@
     /**
      * Initiate connection. Initiation of outgoing connections is not
      * supported for MAP server.
+     *
+     * @hide
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean connect(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
         final IBluetoothMapClient service = getService();
@@ -162,7 +166,10 @@
      *
      * @param device Remote Bluetooth Device
      * @return false on error, true otherwise
+     *
+     * @hide
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "disconnect(" + device + ")");
         final IBluetoothMapClient service = getService();
@@ -251,7 +258,7 @@
      * @return true if priority is set, false on error
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -270,8 +277,8 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(BluetoothDevice device,
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothMapClient service = getService();
@@ -301,7 +308,7 @@
      * @return priority of the device
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
         return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -319,8 +326,8 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
         final IBluetoothMapClient service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 7af770e..bfc28fa 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -19,7 +19,6 @@
 import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -30,7 +29,6 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.util.CloseGuard;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -51,11 +49,10 @@
  * @hide
  */
 @SystemApi
-public final class BluetoothPan implements BluetoothProfile, AutoCloseable {
+public final class BluetoothPan implements BluetoothProfile {
     private static final String TAG = "BluetoothPan";
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
-    private CloseGuard mCloseGuard;
 
     /**
      * Intent used to broadcast the change in connection state of the Pan
@@ -168,16 +165,13 @@
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mContext = context;
         mProfileConnector.connect(context, listener);
-        mCloseGuard = new CloseGuard();
-        mCloseGuard.open("close");
     }
 
     /**
      * Closes the connection to the service and unregisters callbacks
-     *
-     * @hide
      */
-    public void close() {
+    @UnsupportedAppUsage
+    void close() {
         if (VDBG) log("close()");
         mProfileConnector.disconnect();
     }
@@ -188,9 +182,6 @@
 
     /** @hide */
     protected void finalize() {
-        if (mCloseGuard != null) {
-            mCloseGuard.warnIfOpen();
-        }
         close();
     }
 
@@ -204,9 +195,6 @@
      * the state. Users can get the connection state of the profile
      * from this intent.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     * permission.
-     *
      * @param device Remote Bluetooth Device
      * @return false on immediate error, true otherwise
      * @hide
@@ -245,9 +233,6 @@
      * {@link #STATE_DISCONNECTING} can be used to distinguish between the
      * two scenarios.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     * permission.
-     *
      * @param device Remote Bluetooth Device
      * @return false on immediate error, true otherwise
      * @hide
@@ -353,7 +338,7 @@
     @SystemApi
     @Override
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public int getConnectionState(@Nullable BluetoothDevice device) {
+    public int getConnectionState(@NonNull BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
         final IBluetoothPan service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -382,7 +367,7 @@
         final IBluetoothPan service = getService();
         if (service != null && isEnabled()) {
             try {
-                service.setBluetoothTethering(value, pkgName);
+                service.setBluetoothTethering(value, pkgName, null);
             } catch (RemoteException e) {
                 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
             }
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 277a5a8..d58a893 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.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.SuppressLint;
@@ -239,7 +238,7 @@
     @SystemApi
     @Override
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
-    public @BtProfileState int getConnectionState(@Nullable BluetoothDevice device) {
+    public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
         log("getConnectionState: device=" + device);
         try {
             final IBluetoothPbap service = mService;
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 9563c68..d3452ff 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
@@ -101,7 +102,10 @@
      * @param device a remote device we want connect to
      * @return <code>true</code> if command has been issued successfully; <code>false</code>
      * otherwise;
+     *
+     * @hide
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean connect(BluetoothDevice device) {
         if (DBG) {
             log("connect(" + device + ") for PBAP Client.");
@@ -126,7 +130,10 @@
      *
      * @param device Remote Bluetooth Device
      * @return false on error, true otherwise
+     *
+     * @hide
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) {
             log("disconnect(" + device + ")" + new Exception());
@@ -251,7 +258,7 @@
      * @return true if priority is set, false on error
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -270,8 +277,8 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(BluetoothDevice device,
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) {
             log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -305,7 +312,7 @@
      * @return priority of the device
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -323,8 +330,8 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) {
             log("getConnectionPolicy(" + device + ")");
         }
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index bfc3a4d..6e03481 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -310,7 +310,7 @@
      * @return true if priority is set, false on error
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -329,7 +329,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -360,7 +360,7 @@
      * @return priority of the device
      * @hide
      */
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -378,7 +378,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothSap service = getService();
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 9a17346..2888fbd 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -110,8 +110,9 @@
      * off to save power. Scanning is resumed when screen is turned on again. To avoid this, use
      * {@link #startScan(List, ScanSettings, ScanCallback)} with desired {@link ScanFilter}.
      * <p>
-     * An app must hold
-     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+     * An app must have
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
+     * in order to get results. An App targeting Android Q or later must have
      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
      * in order to get results.
      *
@@ -129,8 +130,9 @@
      * resumed when screen is turned on again. To avoid this, do filetered scanning by
      * using proper {@link ScanFilter}.
      * <p>
-     * An app must hold
-     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+     * An app must have
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
+     * in order to get results. An App targeting Android Q or later must have
      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
      * in order to get results.
      *
@@ -150,8 +152,9 @@
      * the PendingIntent. Use this method of scanning if your process is not always running and it
      * should be started when scan results are available.
      * <p>
-     * An app must hold
-     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+     * An app must have
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} permission
+     * in order to get results. An App targeting Android Q or later must have
      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
      * in order to get results.
      * <p>
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c19c284..6e0ce3f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3321,7 +3321,7 @@
             TELEPHONY_SUBSCRIPTION_SERVICE,
             CARRIER_CONFIG_SERVICE,
             EUICC_SERVICE,
-            MMS_SERVICE,
+            //@hide: MMS_SERVICE,
             TELECOM_SERVICE,
             CLIPBOARD_SERVICE,
             INPUT_METHOD_SERVICE,
@@ -3519,7 +3519,6 @@
      * @see android.telephony.CarrierConfigManager
      * @see #EUICC_SERVICE
      * @see android.telephony.euicc.EuiccManager
-     * @see #MMS_SERVICE
      * @see android.telephony.MmsManager
      * @see #INPUT_METHOD_SERVICE
      * @see android.view.inputmethod.InputMethodManager
@@ -3864,15 +3863,13 @@
      * @hide
      * @see NetworkStackClient
      */
-    @SystemApi
-    @TestApi
     public static final String NETWORK_STACK_SERVICE = "network_stack";
 
     /**
-     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
-     * {@link ITetheringConnector} IBinder for communicating with the tethering service
+     * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.TetheringManager}
+     * for managing tethering functions.
      * @hide
-     * @see TetheringClient
+     * @see android.net.TetheringManager
      */
     @SystemApi
     public static final String TETHERING_SERVICE = "tethering";
@@ -3947,8 +3944,6 @@
      */
     public static final String NETWORK_STATS_SERVICE = "netstats";
     /** {@hide} */
-    @SystemApi
-    @SuppressLint("ServiceName")
     public static final String NETWORK_POLICY_SERVICE = "netpolicy";
     /** {@hide} */
     public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
@@ -4186,6 +4181,7 @@
      *
      * @see #getSystemService(String)
      * @see android.telephony.MmsManager
+     * @hide
      */
     public static final String MMS_SERVICE = "mms";
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ba91014..467aa15 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3622,9 +3622,7 @@
      * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @SystemApi
+    @UnsupportedAppUsage
     public static final String ACTION_USER_SWITCHED =
             "android.intent.action.USER_SWITCHED";
 
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 2217807..2914e4c 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -68,6 +68,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
 
 /**
  * Offers the ability to install, upgrade, and remove applications on the
@@ -486,35 +487,30 @@
     }
 
     /**
-     * Returns an active staged session, or {@code null} if there is none.
+     * Returns first active staged session, or {@code null} if there is none.
      *
-     * <p>Staged session is active iff:
-     * <ul>
-     *     <li>It is committed, i.e. {@link SessionInfo#isCommitted()} is {@code true}, and
-     *     <li>it is not applied, i.e. {@link SessionInfo#isStagedSessionApplied()} is {@code
-     *     false}, and
-     *     <li>it is not failed, i.e. {@link SessionInfo#isStagedSessionFailed()} is {@code false}.
-     * </ul>
+     * <p>For more information on what sessions are considered active see
+     * {@link SessionInfo#isStagedSessionActive()}.
      *
-     * <p>In case of a multi-apk session, reasoning above is applied to the parent session, since
-     * that is the one that should been {@link Session#commit committed}.
+     * @deprecated Use {@link #getActiveStagedSessions} as there can be more than one active staged
+     * session
      */
+    @Deprecated
     public @Nullable SessionInfo getActiveStagedSession() {
-        final List<SessionInfo> stagedSessions = getStagedSessions();
-        for (SessionInfo s : stagedSessions) {
-            if (s.isStagedSessionApplied() || s.isStagedSessionFailed()) {
-                // Finalized session.
-                continue;
-            }
-            if (s.getParentSessionId() != SessionInfo.INVALID_ID) {
-                // Child session.
-                continue;
-            }
-            if (s.isCommitted()) {
-                return s;
-            }
-        }
-        return null;
+        List<SessionInfo> activeSessions = getActiveStagedSessions();
+        return activeSessions.isEmpty() ? null : activeSessions.get(0);
+    }
+
+    /**
+     * Returns list of active staged sessions. Returns empty list if there is none.
+     *
+     * <p>For more information on what sessions are considered active see
+     *      * {@link SessionInfo#isStagedSessionActive()}.
+     */
+    public @NonNull List<SessionInfo> getActiveStagedSessions() {
+        return getStagedSessions().stream()
+                .filter(s -> s.isStagedSessionActive())
+                .collect(Collectors.toList());
     }
 
     /**
@@ -2234,13 +2230,36 @@
         }
 
         /**
-         * Returns true if this session is a staged session which will be applied at next reboot.
+         * Returns true if this session is a staged session.
          */
         public boolean isStaged() {
             return isStaged;
         }
 
         /**
+         * Returns {@code true} if this session is an active staged session.
+         *
+         * We consider a session active if it has been committed and it is either pending
+         * verification, or will be applied at next reboot.
+         *
+         * <p>Staged session is active iff:
+         * <ul>
+         *     <li>It is committed, i.e. {@link SessionInfo#isCommitted()} is {@code true}, and
+         *     <li>it is not applied, i.e. {@link SessionInfo#isStagedSessionApplied()} is {@code
+         *     false}, and
+         *     <li>it is not failed, i.e. {@link SessionInfo#isStagedSessionFailed()} is
+         *     {@code false}.
+         * </ul>
+         *
+         * <p>In case of a multi-package session, reasoning above is applied to the parent session,
+         * since that is the one that should have been {@link Session#commit committed}.
+         */
+        public boolean isStagedSessionActive() {
+            return isStaged && isCommitted && !isStagedSessionApplied && !isStagedSessionFailed
+                    && !hasParentSessionId();
+        }
+
+        /**
          * Returns the parent multi-package session ID if this session belongs to one,
          * {@link #INVALID_ID} otherwise.
          */
@@ -2249,6 +2268,13 @@
         }
 
         /**
+         * Returns true if session has a valid parent session, otherwise false.
+         */
+        public boolean hasParentSessionId() {
+            return parentSessionId != INVALID_ID;
+        }
+
+        /**
          * Returns the set of session IDs that will be committed when this session is commited if
          * this session is a multi-package session.
          */
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2db661d..a2a5469 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -548,7 +548,6 @@
      * Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app.
      * @hide
      */
-    @SystemApi
     public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS =  0x20000000;
 
     /**
@@ -1424,7 +1423,7 @@
 
     /**
      * Installation failed return code: a new staged session was attempted to be committed while
-     * there is already one in-progress.
+     * there is already one in-progress or new session has package that is already staged.
      *
      * @hide
      */
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index a70eff9..72df399 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1157,8 +1157,11 @@
             }
         }
 
-        if (mObject != 0) {
-            nativeDestroy(mObject);
+        synchronized (this) {
+            if (mObject != 0) {
+                nativeDestroy(mObject);
+                mObject = 0;
+            }
         }
     }
 
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index d5dadbf..7e8bb08 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -16,8 +16,6 @@
 
 package android.hardware.hdmi;
 
-import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,7 +29,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
-import android.os.SystemProperties;
+import android.sysprop.HdmiProperties;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -316,8 +314,7 @@
         mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK);
         mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
         mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
-        mIsSwitchDevice = SystemProperties.getBoolean(
-            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+        mIsSwitchDevice = HdmiProperties.is_switch().orElse(false);
     }
 
     private static boolean hasDeviceType(int[] types, int type) {
diff --git a/core/java/android/hardware/usb/OWNERS b/core/java/android/hardware/usb/OWNERS
new file mode 100644
index 0000000..8ee72b5
--- /dev/null
+++ b/core/java/android/hardware/usb/OWNERS
@@ -0,0 +1,6 @@
+badhri@google.com
+elaurent@google.com
+moltmann@google.com
+albertccwang@google.com
+jameswei@google.com
+howardyen@google.com
\ No newline at end of file
diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS
new file mode 100644
index 0000000..4447197
--- /dev/null
+++ b/core/java/android/inputmethodservice/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include ../../../../services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index d0091440..275e38c 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -136,7 +136,7 @@
          * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID},
          * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}.
          *
-         * @see android.net.NetworkCapabilities#CAPABILITY_VALIDATED
+         * @see android.net.NetworkCapabilities#NET_CAPABILITY_VALIDATED
          */
         @NetworkValidationResult
         public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
@@ -233,8 +233,8 @@
          * Constructor for ConnectivityReport.
          *
          * <p>Apps should obtain instances through {@link
-         * ConnectivityDiagnosticsCallback#onConnectivityReport} instead of instantiating their own
-         * instances (unless for testing purposes).
+         * ConnectivityDiagnosticsCallback#onConnectivityReportAvailable} instead of instantiating
+         * their own instances (unless for testing purposes).
          *
          * @param network The Network for which this ConnectivityReport applies
          * @param reportTimestamp The timestamp for the report
@@ -368,7 +368,14 @@
 
     /** Class that includes information for a suspected data stall on a specific Network */
     public static final class DataStallReport implements Parcelable {
+        /**
+         * Indicates that the Data Stall was detected using DNS events.
+         */
         public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+
+        /**
+         * Indicates that the Data Stall was detected using TCP metrics.
+         */
         public static final int DETECTION_METHOD_TCP_METRICS = 2;
 
         /** @hide */
@@ -430,7 +437,7 @@
          */
         private long mReportTimestamp;
 
-        /** The detection method used to identify the suspected data stall */
+        /** A bitmask of the detection methods used to identify the suspected data stall */
         @DetectionMethod private final int mDetectionMethod;
 
         /** LinkProperties available on the Network at the reported timestamp */
@@ -492,9 +499,9 @@
         }
 
         /**
-         * Returns the detection method used to identify this suspected data stall.
+         * Returns the bitmask of detection methods used to identify this suspected data stall.
          *
-         * @return The detection method used to identify the suspected data stall
+         * @return The bitmask of detection methods used to identify the suspected data stall
          */
         public int getDetectionMethod() {
             return mDetectionMethod;
@@ -615,10 +622,10 @@
 
         /** @hide */
         @VisibleForTesting
-        public void onConnectivityReport(@NonNull ConnectivityReport report) {
+        public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
             Binder.withCleanCallingIdentity(() -> {
                 mExecutor.execute(() -> {
-                    mCb.onConnectivityReport(report);
+                    mCb.onConnectivityReportAvailable(report);
                 });
             });
         }
@@ -652,14 +659,15 @@
     public abstract static class ConnectivityDiagnosticsCallback {
         /**
          * Called when the platform completes a data connectivity check. This will also be invoked
-         * upon registration with the latest report.
+         * immediately upon registration for each network matching the request with the latest
+         * report, if a report has already been generated for that network.
          *
          * <p>The Network specified in the ConnectivityReport may not be active any more when this
          * method is invoked.
          *
          * @param report The ConnectivityReport containing information about a connectivity check
          */
-        public void onConnectivityReport(@NonNull ConnectivityReport report) {}
+        public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {}
 
         /**
          * Called when the platform suspects a data stall on some Network.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index d8a97de..6b71360 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -48,6 +48,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -145,16 +146,6 @@
     public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
 
     /**
-     * A temporary hack until SUPL system can get off the legacy APIS.
-     * They do too many network requests and the long list of apps listening
-     * and waking due to the CONNECTIVITY_ACTION broadcast makes it expensive.
-     * Use this broadcast intent instead for SUPL requests.
-     * @hide
-     */
-    public static final String CONNECTIVITY_ACTION_SUPL =
-            "android.net.conn.CONNECTIVITY_CHANGE_SUPL";
-
-    /**
      * The device has connected to a network that has presented a captive
      * portal, which is blocking Internet connectivity. The user was presented
      * with a notification that network sign in is required,
@@ -715,6 +706,36 @@
     @Deprecated
     public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
 
+    /**
+     * @deprecated Use {@link NetworkCapabilities} instead.
+     * @hide
+     */
+    @Deprecated
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "TYPE_" }, value = {
+                TYPE_NONE,
+                TYPE_MOBILE,
+                TYPE_WIFI,
+                TYPE_MOBILE_MMS,
+                TYPE_MOBILE_SUPL,
+                TYPE_MOBILE_DUN,
+                TYPE_MOBILE_HIPRI,
+                TYPE_WIMAX,
+                TYPE_BLUETOOTH,
+                TYPE_DUMMY,
+                TYPE_ETHERNET,
+                TYPE_MOBILE_FOTA,
+                TYPE_MOBILE_IMS,
+                TYPE_MOBILE_CBS,
+                TYPE_WIFI_P2P,
+                TYPE_MOBILE_IA,
+                TYPE_MOBILE_EMERGENCY,
+                TYPE_PROXY,
+                TYPE_VPN,
+                TYPE_TEST
+    })
+    public @interface LegacyNetworkType {}
+
     // Deprecated constants for return values of startUsingNetworkFeature. They used to live
     // in com.android.internal.telephony.PhoneConstants until they were made inaccessible.
     private static final int DEPRECATED_PHONE_CONSTANT_APN_ALREADY_ACTIVE = 0;
@@ -1517,84 +1538,6 @@
         return null;
     }
 
-    /**
-     * Guess what the network request was trying to say so that the resulting
-     * network is accessible via the legacy (deprecated) API such as
-     * requestRouteToHost.
-     *
-     * This means we should try to be fairly precise about transport and
-     * capability but ignore things such as networkSpecifier.
-     * If the request has more than one transport or capability it doesn't
-     * match the old legacy requests (they selected only single transport/capability)
-     * so this function cannot map the request to a single legacy type and
-     * the resulting network will not be available to the legacy APIs.
-     *
-     * This code is only called from the requestNetwork API (L and above).
-     *
-     * Setting a legacy type causes CONNECTIVITY_ACTION broadcasts, which are expensive
-     * because they wake up lots of apps - see http://b/23350688 . So we currently only
-     * do this for SUPL requests, which are the only ones that we know need it. If
-     * omitting these broadcasts causes unacceptable app breakage, then for backwards
-     * compatibility we can send them:
-     *
-     * if (targetSdkVersion < Build.VERSION_CODES.M) &&        // legacy API unsupported >= M
-     *     targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP))  // requestNetwork not present < L
-     *
-     * TODO - This should be removed when the legacy APIs are removed.
-     */
-    private int inferLegacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
-        if (netCap == null) {
-            return TYPE_NONE;
-        }
-
-        if (!netCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
-            return TYPE_NONE;
-        }
-
-        // Do this only for SUPL, until GnssLocationProvider is fixed. http://b/25876485 .
-        if (!netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
-            // NOTE: if this causes app breakage, we should not just comment out this early return;
-            // instead, we should make this early return conditional on the requesting app's target
-            // SDK version, as described in the comment above.
-            return TYPE_NONE;
-        }
-
-        String type = null;
-        int result = TYPE_NONE;
-
-        if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
-            type = "enableCBS";
-            result = TYPE_MOBILE_CBS;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
-            type = "enableIMS";
-            result = TYPE_MOBILE_IMS;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
-            type = "enableFOTA";
-            result = TYPE_MOBILE_FOTA;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
-            type = "enableDUN";
-            result = TYPE_MOBILE_DUN;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
-            type = "enableSUPL";
-            result = TYPE_MOBILE_SUPL;
-        // back out this hack for mms as they no longer need this and it's causing
-        // device slowdowns - b/23350688 (note, supl still needs this)
-        //} else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
-        //    type = "enableMMS";
-        //    result = TYPE_MOBILE_MMS;
-        } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-            type = "enableHIPRI";
-            result = TYPE_MOBILE_HIPRI;
-        }
-        if (type != null) {
-            NetworkCapabilities testCap = networkCapabilitiesForFeature(TYPE_MOBILE, type);
-            if (testCap.equalsNetCapabilities(netCap) && testCap.equalsTransportTypes(netCap)) {
-                return result;
-            }
-        }
-        return TYPE_NONE;
-    }
-
     private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
         if (netCap == null) return TYPE_NONE;
         if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
@@ -2102,13 +2045,22 @@
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
         checkLegacyRoutingApiAccess();
         try {
-            return mService.requestRouteToHostAddress(networkType, hostAddress.getAddress());
+            return mService.requestRouteToHostAddress(networkType, hostAddress.getAddress(),
+                    mContext.getOpPackageName(), getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
+     * @return the context's attribution tag
+     */
+    // TODO: Remove method and replace with direct call once R code is pushed to AOSP
+    private @Nullable String getAttributionTag() {
+        return null;
+    }
+
+    /**
      * Returns the value of the setting for background data usage. If false,
      * applications should not use the network if the application is not in the
      * foreground. Developers should respect this setting, and check the value
@@ -2298,14 +2250,30 @@
      * services.jar, possibly in com.android.server.net. */
 
     /** {@hide} */
-    public static final void enforceChangePermission(Context context) {
+    public static final void enforceChangePermission(Context context,
+            String callingPkg, String callingAttributionTag) {
         int uid = Binder.getCallingUid();
-        Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
-                .getPackageNameForUid(context, uid), true /* throwException */);
+        checkAndNoteChangeNetworkStateOperation(context, uid, callingPkg,
+                callingAttributionTag, true /* throwException */);
+    }
+
+    /**
+     * Check if the package is a allowed to change the network state. This also accounts that such
+     * an access happened.
+     *
+     * @return {@code true} iff the package is allowed to change the network state.
+     */
+    // TODO: Remove method and replace with direct call once R code is pushed to AOSP
+    private static boolean checkAndNoteChangeNetworkStateOperation(@NonNull Context context,
+            int uid, @NonNull String callingPackage, @Nullable String callingAttributionTag,
+            boolean throwException) {
+        return Settings.checkAndNoteChangeNetworkStateOperation(context, uid, callingPackage,
+                throwException);
     }
 
     /** {@hide} */
-    public static final void enforceTetherChangePermission(Context context, String callingPkg) {
+    public static final void enforceTetherChangePermission(Context context, String callingPkg,
+            String callingAttributionTag) {
         Preconditions.checkNotNull(context, "Context cannot be null");
         Preconditions.checkNotNull(callingPkg, "callingPkg cannot be null");
 
@@ -2319,12 +2287,26 @@
             int uid = Binder.getCallingUid();
             // If callingPkg's uid is not same as Binder.getCallingUid(),
             // AppOpsService throws SecurityException.
-            Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPkg,
-                    true /* throwException */);
+            checkAndNoteWriteSettingsOperation(context, uid, callingPkg,
+                    callingAttributionTag, true /* throwException */);
         }
     }
 
     /**
+     * Check if the package is a allowed to write settings. This also accounts that such an access
+     * happened.
+     *
+     * @return {@code true} iff the package is allowed to write settings.
+     */
+    // TODO: Remove method and replace with direct call once R code is pushed to AOSP
+    private static boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
+            @NonNull String callingPackage, @Nullable String callingAttributionTag,
+            boolean throwException) {
+        return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
+                throwException);
+    }
+
+    /**
      * @deprecated - use getSystemService. This is a kludge to support static access in certain
      *               situations where a Context pointer is unavailable.
      * @hide
@@ -2576,13 +2558,13 @@
             }
 
             @Override
-            public void onTetheringFailed(final int resultCode) {
+            public void onTetheringFailed(final int error) {
                 callback.onTetheringFailed();
             }
         };
 
         final TetheringRequest request = new TetheringRequest.Builder(type)
-                .setSilentProvisioning(!showProvisioningUi).build();
+                .setShouldShowEntitlementUi(showProvisioningUi).build();
 
         mTetheringManager.startTethering(request, executor, tetheringCallback);
     }
@@ -2802,11 +2784,12 @@
     public static final int TETHER_ERROR_UNAVAIL_IFACE =
             TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
     /**
-     * @deprecated Use {@link TetheringManager#TETHER_ERROR_MASTER_ERROR}.
+     * @deprecated Use {@link TetheringManager#TETHER_ERROR_INTERNAL_ERROR}.
      * {@hide}
      */
     @Deprecated
-    public static final int TETHER_ERROR_MASTER_ERROR = TetheringManager.TETHER_ERROR_MASTER_ERROR;
+    public static final int TETHER_ERROR_MASTER_ERROR =
+            TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
     /**
      * @deprecated Use {@link TetheringManager#TETHER_ERROR_TETHER_IFACE_ERROR}.
      * {@hide}
@@ -2822,19 +2805,19 @@
     public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR =
             TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
     /**
-     * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENABLE_NAT_ERROR}.
+     * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENABLE_FORWARDING_ERROR}.
      * {@hide}
      */
     @Deprecated
     public static final int TETHER_ERROR_ENABLE_NAT_ERROR =
-            TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+            TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
     /**
-     * @deprecated Use {@link TetheringManager#TETHER_ERROR_DISABLE_NAT_ERROR}.
+     * @deprecated Use {@link TetheringManager#TETHER_ERROR_DISABLE_FORWARDING_ERROR}.
      * {@hide}
      */
     @Deprecated
     public static final int TETHER_ERROR_DISABLE_NAT_ERROR =
-            TetheringManager.TETHER_ERROR_DISABLE_NAT_ERROR;
+            TetheringManager.TETHER_ERROR_DISABLE_FORWARDING_ERROR;
     /**
      * @deprecated Use {@link TetheringManager#TETHER_ERROR_IFACE_CFG_ERROR}.
      * {@hide}
@@ -2843,13 +2826,13 @@
     public static final int TETHER_ERROR_IFACE_CFG_ERROR =
             TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
     /**
-     * @deprecated Use {@link TetheringManager#TETHER_ERROR_PROVISION_FAILED}.
+     * @deprecated Use {@link TetheringManager#TETHER_ERROR_PROVISIONING_FAILED}.
      * {@hide}
      */
     @SystemApi
     @Deprecated
     public static final int TETHER_ERROR_PROVISION_FAILED =
-            TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+            TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
     /**
      * @deprecated Use {@link TetheringManager#TETHER_ERROR_DHCPSERVER_ERROR}.
      * {@hide}
@@ -2881,7 +2864,14 @@
     @UnsupportedAppUsage
     @Deprecated
     public int getLastTetherError(String iface) {
-        return mTetheringManager.getLastTetherError(iface);
+        int error = mTetheringManager.getLastTetherError(iface);
+        if (error == TetheringManager.TETHER_ERROR_UNKNOWN_TYPE) {
+            // TETHER_ERROR_UNKNOWN_TYPE was introduced with TetheringManager and has never been
+            // returned by ConnectivityManager. Convert it to the legacy TETHER_ERROR_UNKNOWN_IFACE
+            // instead.
+            error = TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
+        }
+        return error;
     }
 
     /** @hide */
@@ -3756,7 +3746,8 @@
                             need, messenger, binder, callingPackageName);
                 } else {
                     request = mService.requestNetwork(
-                            need, messenger, timeoutMs, binder, legacyType, callingPackageName);
+                            need, messenger, timeoutMs, binder, legacyType, callingPackageName,
+                            getAttributionTag());
                 }
                 if (request != null) {
                     sCallbacks.put(request, callback);
@@ -3774,29 +3765,29 @@
     /**
      * Helper function to request a network with a particular legacy type.
      *
-     * @deprecated This is temporarily public for tethering to backwards compatibility that uses
-     * the NetworkRequest API to request networks with legacy type and relies on
-     * CONNECTIVITY_ACTION broadcasts instead of NetworkCallbacks. New caller should use
+     * This API is only for use in internal system code that requests networks with legacy type and
+     * relies on CONNECTIVITY_ACTION broadcasts instead of NetworkCallbacks. New caller should use
      * {@link #requestNetwork(NetworkRequest, NetworkCallback, Handler)} instead.
      *
-     * TODO: update said system code to rely on NetworkCallbacks and make this method private.
-
      * @param request {@link NetworkRequest} describing this request.
-     * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
-     *                        the callback must not be shared - it uniquely specifies this request.
      * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
      *                  before {@link NetworkCallback#onUnavailable()} is called. The timeout must
      *                  be a positive value (i.e. >0).
      * @param legacyType to specify the network type(#TYPE_*).
      * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+     * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+     *                        the callback must not be shared - it uniquely specifies this request.
      *
      * @hide
      */
     @SystemApi
-    @Deprecated
+    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
     public void requestNetwork(@NonNull NetworkRequest request,
-            @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
-            @NonNull Handler handler) {
+            int timeoutMs, int legacyType, @NonNull Handler handler,
+            @NonNull NetworkCallback networkCallback) {
+        if (legacyType == TYPE_NONE) {
+            throw new IllegalArgumentException("TYPE_NONE is meaningless legacy type");
+        }
         CallbackHandler cbHandler = new CallbackHandler(handler);
         NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler);
@@ -3894,9 +3885,9 @@
      */
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
-        int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
-        requestNetwork(request, networkCallback, 0, legacyType, cbHandler);
+        NetworkCapabilities nc = request.networkCapabilities;
+        sendRequestForNetwork(nc, networkCallback, 0, REQUEST, TYPE_NONE, cbHandler);
     }
 
     /**
@@ -3929,8 +3920,9 @@
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, int timeoutMs) {
         checkTimeout(timeoutMs);
-        int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
-        requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
+        NetworkCapabilities nc = request.networkCapabilities;
+        sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, TYPE_NONE,
+                getDefaultHandler());
     }
 
     /**
@@ -3955,9 +3947,9 @@
     public void requestNetwork(@NonNull NetworkRequest request,
             @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
         checkTimeout(timeoutMs);
-        int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
         CallbackHandler cbHandler = new CallbackHandler(handler);
-        requestNetwork(request, networkCallback, timeoutMs, legacyType, cbHandler);
+        NetworkCapabilities nc = request.networkCapabilities;
+        sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, TYPE_NONE, cbHandler);
     }
 
     /**
@@ -4031,7 +4023,8 @@
         checkPendingIntentNotNull(operation);
         try {
             mService.pendingRequestForNetwork(
-                    request.networkCapabilities, operation, mContext.getOpPackageName());
+                    request.networkCapabilities, operation, mContext.getOpPackageName(),
+                    getAttributionTag());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -4743,4 +4736,28 @@
             Log.d(TAG, "StackLog:" + sb.toString());
         }
     }
+
+    /**
+     * Simulates a Data Stall for the specified Network.
+     *
+     * <p>The caller must be the owner of the specified Network.
+     *
+     * @param detectionMethod The detection method used to identify the Data Stall.
+     * @param timestampMillis The timestamp at which the stall 'occurred', in milliseconds.
+     * @param network The Network for which a Data Stall is being simluated.
+     * @param extras The PersistableBundle of extras included in the Data Stall notification.
+     * @throws SecurityException if the caller is not the owner of the given network.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_TEST_NETWORKS,
+            android.Manifest.permission.NETWORK_STACK})
+    public void simulateDataStall(int detectionMethod, long timestampMillis,
+            @NonNull Network network, @NonNull PersistableBundle extras) {
+        try {
+            mService.simulateDataStall(detectionMethod, timestampMillis, network, extras);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index 83b5f63..d975017 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -200,6 +200,21 @@
     }
 
     /**
+     * Whether to treat interfaces created by {@link TestNetworkManager#createTapInterface}
+     * as Ethernet interfaces. The effects of this method apply to any test interfaces that are
+     * already present on the system.
+     * @hide
+     */
+    @TestApi
+    public void setIncludeTestInterfaces(boolean include) {
+        try {
+            mService.setIncludeTestInterfaces(include);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * A request for a tethered interface.
      */
     public static class TetheredInterfaceRequest {
diff --git a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
index 3a161bf..82b64a9 100644
--- a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
+++ b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
@@ -22,7 +22,7 @@
 
 /** @hide */
 oneway interface IConnectivityDiagnosticsCallback {
-    void onConnectivityReport(in ConnectivityDiagnosticsManager.ConnectivityReport report);
+    void onConnectivityReportAvailable(in ConnectivityDiagnosticsManager.ConnectivityReport report);
     void onDataStallSuspected(in ConnectivityDiagnosticsManager.DataStallReport report);
     void onNetworkConnectivityReported(in Network n, boolean hasConnectivity);
 }
\ No newline at end of file
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1434560..d7f178c 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.net.ConnectionInfo;
+import android.net.ConnectivityDiagnosticsManager;
 import android.net.IConnectivityDiagnosticsCallback;
 import android.net.LinkProperties;
 import android.net.Network;
@@ -33,6 +34,7 @@
 import android.os.IBinder;
 import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.os.ResultReceiver;
 
 import com.android.internal.net.LegacyVpnInfo;
@@ -77,7 +79,8 @@
     NetworkQuotaInfo getActiveNetworkQuotaInfo();
     boolean isActiveNetworkMetered();
 
-    boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
+    boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress,
+            String callingPackageName, String callingAttributionTag);
 
     @UnsupportedAppUsage(maxTargetSdk = 29,
             publicAlternatives = "Use {@code TetheringManager#getLastTetherError} as alternative")
@@ -168,10 +171,10 @@
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
             in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
-            String callingPackageName);
+            String callingPackageName, String callingAttributionTag);
 
     NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
-            in PendingIntent operation, String callingPackageName);
+            in PendingIntent operation, String callingPackageName, String callingAttributionTag);
 
     void releasePendingNetworkRequest(in PendingIntent operation);
 
@@ -227,4 +230,7 @@
     void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback);
 
     IBinder startOrGetTestNetworkService();
+
+    void simulateDataStall(int detectionMethod, long timestampMillis, in Network network,
+                in PersistableBundle extras);
 }
diff --git a/core/java/android/net/IEthernetManager.aidl b/core/java/android/net/IEthernetManager.aidl
index ccc6e35..e058e5a 100644
--- a/core/java/android/net/IEthernetManager.aidl
+++ b/core/java/android/net/IEthernetManager.aidl
@@ -33,6 +33,7 @@
     boolean isAvailable(String iface);
     void addListener(in IEthernetServiceListener listener);
     void removeListener(in IEthernetServiceListener listener);
+    void setIncludeTestInterfaces(boolean include);
     void requestTetheredInterface(in ITetheredInterfaceCallback callback);
     void releaseTetheredInterface(in ITetheredInterfaceCallback callback);
 }
diff --git a/core/java/android/net/ITestNetworkManager.aidl b/core/java/android/net/ITestNetworkManager.aidl
index d586038..2a863ad 100644
--- a/core/java/android/net/ITestNetworkManager.aidl
+++ b/core/java/android/net/ITestNetworkManager.aidl
@@ -33,7 +33,7 @@
     TestNetworkInterface createTapInterface();
 
     void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
-            in IBinder binder);
+            in int[] administratorUids, in IBinder binder);
 
     void teardownTestNetwork(int netId);
 }
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index f19a341..407ff04 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -70,6 +70,15 @@
     private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
     private static final String EMPTY_CERT = "";
 
+    /** @hide */
+    public static final List<String> DEFAULT_ALGORITHMS =
+            Collections.unmodifiableList(Arrays.asList(
+                    IpSecAlgorithm.CRYPT_AES_CBC,
+                    IpSecAlgorithm.AUTH_HMAC_SHA256,
+                    IpSecAlgorithm.AUTH_HMAC_SHA384,
+                    IpSecAlgorithm.AUTH_HMAC_SHA512,
+                    IpSecAlgorithm.AUTH_CRYPT_AES_GCM));
+
     @NonNull private final String mServerAddr;
     @NonNull private final String mUserIdentity;
 
@@ -92,6 +101,7 @@
     private final boolean mIsBypassable; // Defaults in builder
     private final boolean mIsMetered; // Defaults in builder
     private final int mMaxMtu; // Defaults in builder
+    private final boolean mIsRestrictedToTestNetworks;
 
     private Ikev2VpnProfile(
             int type,
@@ -107,7 +117,8 @@
             @NonNull List<String> allowedAlgorithms,
             boolean isBypassable,
             boolean isMetered,
-            int maxMtu) {
+            int maxMtu,
+            boolean restrictToTestNetworks) {
         super(type);
 
         checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
@@ -131,6 +142,7 @@
         mIsBypassable = isBypassable;
         mIsMetered = isMetered;
         mMaxMtu = maxMtu;
+        mIsRestrictedToTestNetworks = restrictToTestNetworks;
 
         validate();
     }
@@ -172,7 +184,56 @@
                 throw new IllegalArgumentException("Invalid auth method set");
         }
 
-        VpnProfile.validateAllowedAlgorithms(mAllowedAlgorithms);
+        validateAllowedAlgorithms(mAllowedAlgorithms);
+    }
+
+    /**
+     * Validates that the allowed algorithms are a valid set for IPsec purposes
+     *
+     * <p>In order for the algorithm list to be a valid set, it must contain at least one algorithm
+     * that provides Authentication, and one that provides Encryption. Authenticated Encryption with
+     * Associated Data (AEAD) algorithms are counted as providing Authentication and Encryption.
+     *
+     * @param allowedAlgorithms The list to be validated
+     */
+    private static void validateAllowedAlgorithms(@NonNull List<String> algorithmNames) {
+        VpnProfile.validateAllowedAlgorithms(algorithmNames);
+
+        // First, make sure no insecure algorithms were proposed.
+        if (algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_MD5)
+                || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA1)) {
+            throw new IllegalArgumentException("Algorithm not supported for IKEv2 VPN profiles");
+        }
+
+        // Validate that some valid combination (AEAD or AUTH + CRYPT) is present
+        if (hasAeadAlgorithms(algorithmNames) || hasNormalModeAlgorithms(algorithmNames)) {
+            return;
+        }
+
+        throw new IllegalArgumentException("Algorithm set missing support for Auth, Crypt or both");
+    }
+
+    /**
+     * Checks if the provided list has AEAD algorithms
+     *
+     * @hide
+     */
+    public static boolean hasAeadAlgorithms(@NonNull List<String> algorithmNames) {
+        return algorithmNames.contains(IpSecAlgorithm.AUTH_CRYPT_AES_GCM);
+    }
+
+    /**
+     * Checks the provided list has acceptable (non-AEAD) authentication and encryption algorithms
+     *
+     * @hide
+     */
+    public static boolean hasNormalModeAlgorithms(@NonNull List<String> algorithmNames) {
+        final boolean hasCrypt = algorithmNames.contains(IpSecAlgorithm.CRYPT_AES_CBC);
+        final boolean hasAuth = algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)
+                || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA384)
+                || algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA512);
+
+        return hasCrypt && hasAuth;
     }
 
     /** Retrieves the server address string. */
@@ -271,6 +332,15 @@
         return mMaxMtu;
     }
 
+    /**
+     * Returns whether or not this VPN profile is restricted to test networks.
+     *
+     * @hide
+     */
+    public boolean isRestrictedToTestNetworks() {
+        return mIsRestrictedToTestNetworks;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(
@@ -287,7 +357,8 @@
                 mAllowedAlgorithms,
                 mIsBypassable,
                 mIsMetered,
-                mMaxMtu);
+                mMaxMtu,
+                mIsRestrictedToTestNetworks);
     }
 
     @Override
@@ -310,7 +381,8 @@
                 && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
                 && mIsBypassable == other.mIsBypassable
                 && mIsMetered == other.mIsMetered
-                && mMaxMtu == other.mMaxMtu;
+                && mMaxMtu == other.mMaxMtu
+                && mIsRestrictedToTestNetworks == other.mIsRestrictedToTestNetworks;
     }
 
     /**
@@ -323,7 +395,8 @@
      */
     @NonNull
     public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
-        final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */);
+        final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */,
+                mIsRestrictedToTestNetworks);
         profile.type = mType;
         profile.server = mServerAddr;
         profile.ipsecIdentifier = mUserIdentity;
@@ -391,6 +464,9 @@
         builder.setBypassable(profile.isBypassable);
         builder.setMetered(profile.isMetered);
         builder.setMaxMtu(profile.maxMtu);
+        if (profile.isRestrictedToTestNetworks) {
+            builder.restrictToTestNetworks();
+        }
 
         switch (profile.type) {
             case TYPE_IKEV2_IPSEC_USER_PASS:
@@ -559,10 +635,11 @@
         @Nullable private X509Certificate mUserCert;
 
         @Nullable private ProxyInfo mProxyInfo;
-        @NonNull private List<String> mAllowedAlgorithms = new ArrayList<>();
+        @NonNull private List<String> mAllowedAlgorithms = DEFAULT_ALGORITHMS;
         private boolean mIsBypassable = false;
         private boolean mIsMetered = true;
-        private int mMaxMtu = 1360;
+        private int mMaxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;
+        private boolean mIsRestrictedToTestNetworks = false;
 
         /**
          * Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
@@ -745,7 +822,7 @@
          * @param isMetered {@code true} if the VPN network should be treated as metered regardless
          *     of underlying network meteredness. Defaults to {@code true}.
          * @return this {@link Builder} object to facilitate chaining of method calls
-         * @see NetworkCapabilities.NET_CAPABILITY_NOT_METERED
+         * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
          */
         @NonNull
         public Builder setMetered(boolean isMetered) {
@@ -756,7 +833,19 @@
         /**
          * Sets the allowable set of IPsec algorithms
          *
-         * <p>A list of allowed IPsec algorithms as defined in {@link IpSecAlgorithm}
+         * <p>If set, this will constrain the set of algorithms that the IPsec tunnel will use for
+         * integrity verification and encryption to the provided list.
+         *
+         * <p>The set of allowed IPsec algorithms is defined in {@link IpSecAlgorithm}. Adding of
+         * algorithms that are considered insecure (such as AUTH_HMAC_MD5 and AUTH_HMAC_SHA1) is not
+         * permitted, and will result in an IllegalArgumentException being thrown.
+         *
+         * <p>The provided algorithm list must contain at least one algorithm that provides
+         * Authentication, and one that provides Encryption. Authenticated Encryption with
+         * Associated Data (AEAD) algorithms provide both Authentication and Encryption.
+         *
+         * <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm},
+         * with the exception of those considered insecure (as described above).
          *
          * @param algorithmNames the list of supported IPsec algorithms
          * @return this {@link Builder} object to facilitate chaining of method calls
@@ -765,13 +854,28 @@
         @NonNull
         public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
             checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
-            VpnProfile.validateAllowedAlgorithms(algorithmNames);
+            validateAllowedAlgorithms(algorithmNames);
 
             mAllowedAlgorithms = algorithmNames;
             return this;
         }
 
         /**
+         * Restricts this profile to use test networks (only).
+         *
+         * <p>This method is for testing only, and must not be used by apps. Calling
+         * provisionVpnProfile() with a profile where test-network usage is enabled will require the
+         * MANAGE_TEST_NETWORKS permission.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder restrictToTestNetworks() {
+            mIsRestrictedToTestNetworks = true;
+            return this;
+        }
+
+        /**
          * Validates, builds and provisions the VpnProfile.
          *
          * @throws IllegalArgumentException if any of the required keys or values were invalid
@@ -792,7 +896,8 @@
                     mAllowedAlgorithms,
                     mIsBypassable,
                     mIsMetered,
-                    mMaxMtu);
+                    mMaxMtu,
+                    mIsRestrictedToTestNetworks);
         }
     }
 }
diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java
index 909998d..b3b0f11 100644
--- a/core/java/android/net/InvalidPacketException.java
+++ b/core/java/android/net/InvalidPacketException.java
@@ -28,7 +28,7 @@
  */
 @SystemApi
 public class InvalidPacketException extends Exception {
-    public final int error;
+    private final int mError;
 
     // Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS.
     /** Invalid IP address. */
@@ -56,6 +56,11 @@
      * See the error code for details.
      */
     public InvalidPacketException(@ErrorCode final int error) {
-        this.error = error;
+        this.mError = error;
+    }
+
+    /** Get error code. */
+    public int getError() {
+        return mError;
     }
 }
diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
index 2b8b7e6..e21cb44 100644
--- a/core/java/android/net/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -19,10 +19,10 @@
 import static android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS;
 import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.net.util.IpUtils;
-import android.os.Parcel;
 import android.util.Log;
 
 import java.net.InetAddress;
@@ -30,7 +30,6 @@
 /**
  * Represents the actual packets that are sent by the
  * {@link android.net.SocketKeepalive} API.
- *
  * @hide
  */
 @SystemApi
@@ -39,33 +38,37 @@
 
     /** Source IP address */
     @NonNull
-    public final InetAddress srcAddress;
+    private final InetAddress mSrcAddress;
 
     /** Destination IP address */
     @NonNull
-    public final InetAddress dstAddress;
+    private final InetAddress mDstAddress;
 
     /** Source port */
-    public final int srcPort;
+    private final int mSrcPort;
 
     /** Destination port */
-    public final int dstPort;
+    private final int mDstPort;
 
     /** Packet data. A raw byte string of packet data, not including the link-layer header. */
     private final byte[] mPacket;
 
+    // Note: If you add new fields, please modify the parcelling code in the child classes.
+
+
     // This should only be constructed via static factory methods, such as
     // nattKeepalivePacket.
     /**
      * A holding class for data necessary to build a keepalive packet.
      */
-    protected KeepalivePacketData(@NonNull InetAddress srcAddress, int srcPort,
-            @NonNull InetAddress dstAddress, int dstPort,
-                    @NonNull byte[] data) throws InvalidPacketException {
-        this.srcAddress = srcAddress;
-        this.dstAddress = dstAddress;
-        this.srcPort = srcPort;
-        this.dstPort = dstPort;
+    protected KeepalivePacketData(@NonNull InetAddress srcAddress,
+            @IntRange(from = 0, to = 65535) int srcPort, @NonNull InetAddress dstAddress,
+            @IntRange(from = 0, to = 65535) int dstPort,
+            @NonNull byte[] data) throws InvalidPacketException {
+        this.mSrcAddress = srcAddress;
+        this.mDstAddress = dstAddress;
+        this.mSrcPort = srcPort;
+        this.mDstPort = dstPort;
         this.mPacket = data;
 
         // Check we have two IP addresses of the same family.
@@ -82,26 +85,34 @@
         }
     }
 
+    /** Get source IP address. */
+    @NonNull
+    public InetAddress getSrcAddress() {
+        return mSrcAddress;
+    }
+
+    /** Get destination IP address. */
+    @NonNull
+    public InetAddress getDstAddress() {
+        return mDstAddress;
+    }
+
+    /** Get source port number. */
+    public int getSrcPort() {
+        return mSrcPort;
+    }
+
+    /** Get destination port number. */
+    public int getDstPort() {
+        return mDstPort;
+    }
+
+    /**
+     * Returns a byte array of the given packet data.
+     */
     @NonNull
     public byte[] getPacket() {
         return mPacket.clone();
     }
 
-    /** @hide */
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(srcAddress.getHostAddress());
-        out.writeString(dstAddress.getHostAddress());
-        out.writeInt(srcPort);
-        out.writeInt(dstPort);
-        out.writeByteArray(mPacket);
-    }
-
-    /** @hide */
-    protected KeepalivePacketData(Parcel in) {
-        srcAddress = NetworkUtils.numericToInetAddress(in.readString());
-        dstAddress = NetworkUtils.numericToInetAddress(in.readString());
-        srcPort = in.readInt();
-        dstPort = in.readInt();
-        mPacket = in.createByteArray();
-    }
 }
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index d25ee0e..651494d 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -21,6 +21,8 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.LinkPropertiesUtils;
+import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -93,36 +95,6 @@
     /**
      * @hide
      */
-    public static class CompareResult<T> {
-        public final List<T> removed = new ArrayList<>();
-        public final List<T> added = new ArrayList<>();
-
-        public CompareResult() {}
-
-        public CompareResult(Collection<T> oldItems, Collection<T> newItems) {
-            if (oldItems != null) {
-                removed.addAll(oldItems);
-            }
-            if (newItems != null) {
-                for (T newItem : newItems) {
-                    if (!removed.remove(newItem)) {
-                        added.add(newItem);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "removed=[" + TextUtils.join(",", removed)
-                    + "] added=[" + TextUtils.join(",", added)
-                    + "]";
-        }
-    }
-
-    /**
-     * @hide
-     */
     @UnsupportedAppUsage(implicitMember =
             "values()[Landroid/net/LinkProperties$ProvisioningChange;")
     public enum ProvisioningChange {
@@ -195,7 +167,19 @@
         this(source, false /* parcelSensitiveFields */);
     }
 
-    private LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
+    /**
+     * Create a copy of a {@link LinkProperties} that may preserve fields that were set
+     * based on the permissions of the process that originally received it.
+     *
+     * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
+     * they should not be shared outside of the process that receives them without appropriate
+     * checks.
+     * @param parcelSensitiveFields Whether the sensitive fields should be kept when parceling
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
         mParcelSensitiveFields = parcelSensitiveFields;
         if (source == null) return;
         mIfaceName = source.mIfaceName;
@@ -702,17 +686,29 @@
             route.getDestination(),
             route.getGateway(),
             mIfaceName,
-            route.getType());
+            route.getType(),
+            route.getMtu());
+    }
+
+    private int findRouteIndexByRouteKey(RouteInfo route) {
+        for (int i = 0; i < mRoutes.size(); i++) {
+            if (mRoutes.get(i).getRouteKey().equals(route.getRouteKey())) {
+                return i;
+            }
+        }
+        return -1;
     }
 
     /**
-     * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
-     * {@link RouteInfo} had an interface name set and that differs from the interface set for this
-     * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.  The proper
-     * course is to add either un-named or properly named {@link RouteInfo}.
+     * Adds a {@link RouteInfo} to this {@code LinkProperties}, if a {@link RouteInfo}
+     * with the same {@link RouteInfo.RouteKey} with different properties
+     * (e.g., different MTU), it will be updated. If the {@link RouteInfo} had an
+     * interface name set and that differs from the interface set for this
+     * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.
+     * The proper course is to add either un-named or properly named {@link RouteInfo}.
      *
      * @param route A {@link RouteInfo} to add to this object.
-     * @return {@code false} if the route was already present, {@code true} if it was added.
+     * @return {@code true} was added or updated, false otherwise.
      */
     public boolean addRoute(@NonNull RouteInfo route) {
         String routeIface = route.getInterface();
@@ -722,11 +718,20 @@
                             + " vs. " + mIfaceName);
         }
         route = routeWithInterface(route);
-        if (!mRoutes.contains(route)) {
+
+        int i = findRouteIndexByRouteKey(route);
+        if (i == -1) {
+            // Route was not present. Add it.
             mRoutes.add(route);
             return true;
+        } else if (mRoutes.get(i).equals(route)) {
+            // Route was present and has same properties. Do nothing.
+            return false;
+        } else {
+            // Route was present and has different properties. Update it.
+            mRoutes.set(i, route);
+            return true;
         }
-        return false;
     }
 
     /**
@@ -734,6 +739,7 @@
      * specify an interface and the interface must match the interface of this
      * {@code LinkProperties}, or it will not be removed.
      *
+     * @param route A {@link RouteInfo} specifying the route to remove.
      * @return {@code true} if the route was removed, {@code false} if it was not present.
      *
      * @hide
@@ -1068,6 +1074,21 @@
     }
 
     /**
+     * Returns true if this link has an IPv4 unreachable default route.
+     *
+     * @return {@code true} if there is an IPv4 unreachable default route, {@code false} otherwise.
+     * @hide
+     */
+    public boolean hasIpv4UnreachableDefaultRoute() {
+        for (RouteInfo r : mRoutes) {
+            if (r.isIPv4UnreachableDefault()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * For backward compatibility.
      * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
      * just yet.
@@ -1096,6 +1117,21 @@
     }
 
     /**
+     * Returns true if this link has an IPv6 unreachable default route.
+     *
+     * @return {@code true} if there is an IPv6 unreachable default route, {@code false} otherwise.
+     * @hide
+     */
+    public boolean hasIpv6UnreachableDefaultRoute() {
+        for (RouteInfo r : mRoutes) {
+            if (r.isIPv6UnreachableDefault()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * For backward compatibility.
      * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely
      * just yet.
@@ -1326,7 +1362,7 @@
      */
     @UnsupportedAppUsage
     public boolean isIdenticalInterfaceName(@NonNull LinkProperties target) {
-        return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
+        return LinkPropertiesUtils.isIdenticalInterfaceName(target, this);
     }
 
     /**
@@ -1349,10 +1385,7 @@
      */
     @UnsupportedAppUsage
     public boolean isIdenticalAddresses(@NonNull LinkProperties target) {
-        Collection<InetAddress> targetAddresses = target.getAddresses();
-        Collection<InetAddress> sourceAddresses = getAddresses();
-        return (sourceAddresses.size() == targetAddresses.size()) ?
-                    sourceAddresses.containsAll(targetAddresses) : false;
+        return LinkPropertiesUtils.isIdenticalAddresses(target, this);
     }
 
     /**
@@ -1364,15 +1397,7 @@
      */
     @UnsupportedAppUsage
     public boolean isIdenticalDnses(@NonNull LinkProperties target) {
-        Collection<InetAddress> targetDnses = target.getDnsServers();
-        String targetDomains = target.getDomains();
-        if (mDomains == null) {
-            if (targetDomains != null) return false;
-        } else {
-            if (!mDomains.equals(targetDomains)) return false;
-        }
-        return (mDnses.size() == targetDnses.size()) ?
-                mDnses.containsAll(targetDnses) : false;
+        return LinkPropertiesUtils.isIdenticalDnses(target, this);
     }
 
     /**
@@ -1425,9 +1450,7 @@
      */
     @UnsupportedAppUsage
     public boolean isIdenticalRoutes(@NonNull LinkProperties target) {
-        Collection<RouteInfo> targetRoutes = target.getRoutes();
-        return (mRoutes.size() == targetRoutes.size()) ?
-                mRoutes.containsAll(targetRoutes) : false;
+        return LinkPropertiesUtils.isIdenticalRoutes(target, this);
     }
 
     /**
@@ -1439,8 +1462,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public boolean isIdenticalHttpProxy(@NonNull LinkProperties target) {
-        return getHttpProxy() == null ? target.getHttpProxy() == null :
-                getHttpProxy().equals(target.getHttpProxy());
+        return LinkPropertiesUtils.isIdenticalHttpProxy(target, this);
     }
 
     /**
@@ -1603,22 +1625,6 @@
     }
 
     /**
-     * Create a copy of this {@link LinkProperties} that will preserve fields that were set
-     * based on the permissions of the process that received this {@link LinkProperties}.
-     *
-     * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
-     * they should not be shared outside of the process that receives them without appropriate
-     * checks.
-     * @hide
-     */
-    @SystemApi
-    @TestApi
-    @NonNull
-    public LinkProperties makeSensitiveFieldsParcelingCopy() {
-        return new LinkProperties(this, true /* parcelSensitiveFields */);
-    }
-
-    /**
      * Compares this {@code LinkProperties} instance against the target
      * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
      * all their fields are equal in values.
@@ -1663,26 +1669,6 @@
     }
 
     /**
-     * Compares the addresses in this LinkProperties with another
-     * LinkProperties, examining only addresses on the base link.
-     *
-     * @param target a LinkProperties with the new list of addresses
-     * @return the differences between the addresses.
-     * @hide
-     */
-    public @NonNull CompareResult<LinkAddress> compareAddresses(@Nullable LinkProperties target) {
-        /*
-         * Duplicate the LinkAddresses into removed, we will be removing
-         * address which are common between mLinkAddresses and target
-         * leaving the addresses that are different. And address which
-         * are in target but not in mLinkAddresses are placed in the
-         * addedAddresses.
-         */
-        return new CompareResult<>(mLinkAddresses,
-                target != null ? target.getLinkAddresses() : null);
-    }
-
-    /**
      * Compares the DNS addresses in this LinkProperties with another
      * LinkProperties, examining only DNS addresses on the base link.
      *
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 74c9aac..0e10c42 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -20,11 +20,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.MacAddressUtils;
 import android.net.wifi.WifiInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.BitUtils;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -33,7 +33,6 @@
 import java.net.UnknownHostException;
 import java.security.SecureRandom;
 import java.util.Arrays;
-import java.util.Random;
 
 /**
  * Representation of a MAC address.
@@ -109,21 +108,13 @@
         if (equals(BROADCAST_ADDRESS)) {
             return TYPE_BROADCAST;
         }
-        if (isMulticastAddress()) {
+        if ((mAddr & MULTICAST_MASK) != 0) {
             return TYPE_MULTICAST;
         }
         return TYPE_UNICAST;
     }
 
     /**
-     * @return true if this MacAddress is a multicast address.
-     * @hide
-     */
-    public boolean isMulticastAddress() {
-        return (mAddr & MULTICAST_MASK) != 0;
-    }
-
-    /**
      * @return true if this MacAddress is a locally assigned address.
      */
     public boolean isLocallyAssigned() {
@@ -192,7 +183,7 @@
      * @hide
      */
     public static boolean isMacAddress(byte[] addr) {
-        return addr != null && addr.length == ETHER_ADDR_LEN;
+        return MacAddressUtils.isMacAddress(addr);
     }
 
     /**
@@ -261,26 +252,11 @@
     }
 
     private static byte[] byteAddrFromLongAddr(long addr) {
-        byte[] bytes = new byte[ETHER_ADDR_LEN];
-        int index = ETHER_ADDR_LEN;
-        while (index-- > 0) {
-            bytes[index] = (byte) addr;
-            addr = addr >> 8;
-        }
-        return bytes;
+        return MacAddressUtils.byteAddrFromLongAddr(addr);
     }
 
     private static long longAddrFromByteAddr(byte[] addr) {
-        Preconditions.checkNotNull(addr);
-        if (!isMacAddress(addr)) {
-            throw new IllegalArgumentException(
-                    Arrays.toString(addr) + " was not a valid MAC address");
-        }
-        long longAddr = 0;
-        for (byte b : addr) {
-            longAddr = (longAddr << 8) + BitUtils.uint8(b);
-        }
-        return longAddr;
+        return MacAddressUtils.longAddrFromByteAddr(addr);
     }
 
     // Internal conversion function equivalent to longAddrFromByteAddr(byteAddrFromStringAddr(addr))
@@ -350,50 +326,7 @@
      * @hide
      */
     public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
-        return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
-    }
-
-    /**
-     * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
-     * unicast bit, are randomly selected.
-     *
-     * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
-     *
-     * @return a random locally assigned, unicast MacAddress.
-     *
-     * @hide
-     */
-    public static @NonNull MacAddress createRandomUnicastAddress() {
-        return createRandomUnicastAddress(null, new SecureRandom());
-    }
-
-    /**
-     * Returns a randomly generated MAC address using the given Random object and the same
-     * OUI values as the given MacAddress.
-     *
-     * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
-     *
-     * @param base a base MacAddress whose OUI is used for generating the random address.
-     *             If base == null then the OUI will also be randomized.
-     * @param r a standard Java Random object used for generating the random address.
-     * @return a random locally assigned MacAddress.
-     *
-     * @hide
-     */
-    public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
-        long addr;
-        if (base == null) {
-            addr = r.nextLong() & VALID_LONG_MASK;
-        } else {
-            addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
-        }
-        addr |= LOCALLY_ASSIGNED_MASK;
-        addr &= ~MULTICAST_MASK;
-        MacAddress mac = new MacAddress(addr);
-        if (mac.equals(DEFAULT_MAC_ADDRESS)) {
-            return createRandomUnicastAddress(base, r);
-        }
-        return mac;
+        return MacAddressUtils.createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
     }
 
     // Convenience function for working around the lack of byte literals.
diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java
index 68a3935..70c4a72 100644
--- a/core/java/android/net/MatchAllNetworkSpecifier.java
+++ b/core/java/android/net/MatchAllNetworkSpecifier.java
@@ -43,7 +43,8 @@
     }
 
     /** @hide */
-    public boolean satisfiedBy(NetworkSpecifier other) {
+    @Override
+    public boolean canBeSatisfiedBy(NetworkSpecifier other) {
         /*
          * The method is called by a NetworkRequest to see if it is satisfied by a proposed
          * network (e.g. as offered by a network factory). Since MatchAllNetweorkSpecifier must
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
index bd39c13..22288b6 100644
--- a/core/java/android/net/NattKeepalivePacketData.java
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -20,6 +20,7 @@
 import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.util.IpUtils;
 import android.os.Parcel;
@@ -30,6 +31,7 @@
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.util.Objects;
 
 /** @hide */
 @SystemApi
@@ -92,10 +94,10 @@
 
     /** Write to parcel */
     public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeString(srcAddress.getHostAddress());
-        out.writeString(dstAddress.getHostAddress());
-        out.writeInt(srcPort);
-        out.writeInt(dstPort);
+        out.writeString(getSrcAddress().getHostAddress());
+        out.writeString(getDstAddress().getHostAddress());
+        out.writeInt(getSrcPort());
+        out.writeInt(getDstPort());
     }
 
     /** Parcelable Creator */
@@ -113,7 +115,7 @@
                                     dstAddress, dstPort);
                     } catch (InvalidPacketException e) {
                         throw new IllegalArgumentException(
-                                "Invalid NAT-T keepalive data: " + e.error);
+                                "Invalid NAT-T keepalive data: " + e.getError());
                     }
                 }
 
@@ -121,4 +123,21 @@
                     return new NattKeepalivePacketData[size];
                 }
             };
+
+    @Override
+    public boolean equals(@Nullable final Object o) {
+        if (!(o instanceof NattKeepalivePacketData)) return false;
+        final NattKeepalivePacketData other = (NattKeepalivePacketData) o;
+        final InetAddress srcAddress = getSrcAddress();
+        final InetAddress dstAddress = getDstAddress();
+        return srcAddress.equals(other.getSrcAddress())
+            && dstAddress.equals(other.getDstAddress())
+            && getSrcPort() == other.getSrcPort()
+            && getDstPort() == other.getDstPort();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort());
+    }
 }
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 8d1ab33..3d641f5 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -27,6 +27,7 @@
 import android.system.OsConstants;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.okhttp.internalandroidapi.Dns;
 import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
 
@@ -61,17 +62,18 @@
 public class Network implements Parcelable {
 
     /**
+     * The unique id of the network.
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     public final int netId;
 
     // Objects used to perform per-network operations such as getSocketFactory
     // and openConnection, and a lock to protect access to them.
     private volatile NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
-    // mLock should be used to control write access to mUrlConnectionFactory.
-    // maybeInitUrlConnectionFactory() must be called prior to reading this field.
-    private volatile HttpURLConnectionFactory mUrlConnectionFactory;
+    // mUrlConnectionFactory is initialized lazily when it is first needed.
+    @GuardedBy("mLock")
+    private HttpURLConnectionFactory mUrlConnectionFactory;
     private final Object mLock = new Object();
 
     // Default connection pool values. These are evaluated at startup, just
@@ -169,6 +171,17 @@
     }
 
     /**
+     * Get the unique id of the network.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    public int getNetId() {
+        return netId;
+    }
+
+    /**
      * Returns a netid marked with the Private DNS bypass flag.
      *
      * This flag must be kept in sync with the NETID_USE_LOCAL_NAMESERVERS flag
@@ -283,36 +296,16 @@
         return mNetworkBoundSocketFactory;
     }
 
-    // TODO: This creates a connection pool and host resolver for
-    // every Network object, instead of one for every NetId. This is
-    // suboptimal, because an app could potentially have more than one
-    // Network object for the same NetId, causing increased memory footprint
-    // and performance penalties due to lack of connection reuse (connection
-    // setup time, congestion window growth time, etc.).
-    //
-    // Instead, investigate only having one connection pool and host resolver
-    // for every NetId, perhaps by using a static HashMap of NetIds to
-    // connection pools and host resolvers. The tricky part is deciding when
-    // to remove a map entry; a WeakHashMap shouldn't be used because whether
-    // a Network is referenced doesn't correlate with whether a new Network
-    // will be instantiated in the near future with the same NetID. A good
-    // solution would involve purging empty (or when all connections are timed
-    // out) ConnectionPools.
-    private void maybeInitUrlConnectionFactory() {
-        synchronized (mLock) {
-            if (mUrlConnectionFactory == null) {
-                // Set configuration on the HttpURLConnectionFactory that will be good for all
-                // connections created by this Network. Configuration that might vary is left
-                // until openConnection() and passed as arguments.
-                Dns dnsLookup = hostname -> Arrays.asList(Network.this.getAllByName(hostname));
-                HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory();
-                urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup
-                // A private connection pool just for this Network.
-                urlConnectionFactory.setNewConnectionPool(httpMaxConnections,
-                        httpKeepAliveDurationMs, TimeUnit.MILLISECONDS);
-                mUrlConnectionFactory = urlConnectionFactory;
-            }
-        }
+    private static HttpURLConnectionFactory createUrlConnectionFactory(Dns dnsLookup) {
+        // Set configuration on the HttpURLConnectionFactory that will be good for all
+        // connections created by this Network. Configuration that might vary is left
+        // until openConnection() and passed as arguments.
+        HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory();
+        urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup
+        // A private connection pool just for this Network.
+        urlConnectionFactory.setNewConnectionPool(httpMaxConnections,
+                httpKeepAliveDurationMs, TimeUnit.MILLISECONDS);
+        return urlConnectionFactory;
     }
 
     /**
@@ -353,9 +346,31 @@
      */
     public URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException {
         if (proxy == null) throw new IllegalArgumentException("proxy is null");
-        maybeInitUrlConnectionFactory();
+        // TODO: This creates a connection pool and host resolver for
+        // every Network object, instead of one for every NetId. This is
+        // suboptimal, because an app could potentially have more than one
+        // Network object for the same NetId, causing increased memory footprint
+        // and performance penalties due to lack of connection reuse (connection
+        // setup time, congestion window growth time, etc.).
+        //
+        // Instead, investigate only having one connection pool and host resolver
+        // for every NetId, perhaps by using a static HashMap of NetIds to
+        // connection pools and host resolvers. The tricky part is deciding when
+        // to remove a map entry; a WeakHashMap shouldn't be used because whether
+        // a Network is referenced doesn't correlate with whether a new Network
+        // will be instantiated in the near future with the same NetID. A good
+        // solution would involve purging empty (or when all connections are timed
+        // out) ConnectionPools.
+        final HttpURLConnectionFactory urlConnectionFactory;
+        synchronized (mLock) {
+            if (mUrlConnectionFactory == null) {
+                Dns dnsLookup = hostname -> Arrays.asList(getAllByName(hostname));
+                mUrlConnectionFactory = createUrlConnectionFactory(dnsLookup);
+            }
+            urlConnectionFactory = mUrlConnectionFactory;
+        }
         SocketFactory socketFactory = getSocketFactory();
-        return mUrlConnectionFactory.openConnection(url, socketFactory, proxy);
+        return urlConnectionFactory.openConnection(url, socketFactory, proxy);
     }
 
     /**
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index fef353f..65e772cb 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -23,27 +25,64 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * A Utility class for handling for communicating between bearer-specific
+ * A utility class for handling for communicating between bearer-specific
  * code and ConnectivityService.
  *
+ * An agent manages the life cycle of a network. A network starts its
+ * life cycle when {@link register} is called on NetworkAgent. The network
+ * is then connecting. When full L3 connectivity has been established,
+ * the agent shoud call {@link markConnected} to inform the system that
+ * this network is ready to use. When the network disconnects its life
+ * ends and the agent should call {@link unregister}, at which point the
+ * system will clean up and free resources.
+ * Any reconnection becomes a new logical network, so after a network
+ * is disconnected the agent cannot be used any more. Network providers
+ * should create a new NetworkAgent instance to handle new connections.
+ *
  * A bearer may have more than one NetworkAgent if it can simultaneously
  * support separate networks (IMS / Internet / MMS Apns on cellular, or
  * perhaps connections with different SSID or P2P for Wi-Fi).
  *
+ * This class supports methods to start and stop sending keepalive packets.
+ * Keepalive packets are typically sent at periodic intervals over a network
+ * with NAT when there is no other traffic to avoid the network forcefully
+ * closing the connection. NetworkAgents that manage technologies that
+ * have hardware support for keepalive should implement the related
+ * methods to save battery life. NetworkAgent that cannot get support
+ * without waking up the CPU should not, as this would be prohibitive in
+ * terms of battery - these agents should simply not override the related
+ * methods, which results in the implementation returning
+ * {@link SocketKeepalive.ERROR_UNSUPPORTED} as appropriate.
+ *
+ * Keepalive packets need to be sent at relatively frequent intervals
+ * (a few seconds to a few minutes). As the contents of keepalive packets
+ * depend on the current network status, hardware needs to be configured
+ * to send them and has a limited amount of memory to do so. The HAL
+ * formalizes this as slots that an implementation can configure to send
+ * the correct packets. Devices typically have a small number of slots
+ * per radio technology, and the specific number of slots for each
+ * technology is specified in configuration files.
+ * {@see SocketKeepalive} for details.
+ *
  * @hide
  */
 @SystemApi
@@ -65,7 +104,7 @@
     private final String LOG_TAG;
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
-    private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
+    private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
     private volatile long mLastBwRefreshTime = 0;
     private static final long BW_REFRESH_MIN_WIN_MS = 500;
     private boolean mBandwidthUpdateScheduled = false;
@@ -74,10 +113,13 @@
     // into the internal API of ConnectivityService.
     @NonNull
     private NetworkInfo mNetworkInfo;
+    @NonNull
+    private final Object mRegisterLock = new Object();
 
     /**
      * The ID of the {@link NetworkProvider} that created this object, or
      * {@link NetworkProvider#ID_NONE} if unknown.
+     * @hide
      */
     public final int providerId;
 
@@ -157,6 +199,14 @@
      */
     public static final int VALIDATION_STATUS_NOT_VALID = 2;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "VALIDATION_STATUS_" }, value = {
+            VALIDATION_STATUS_VALID,
+            VALIDATION_STATUS_NOT_VALID
+    })
+    public @interface ValidationStatus {}
+
     // TODO: remove.
     /** @hide */
     public static final int VALID_NETWORK = 1;
@@ -201,7 +251,7 @@
      * Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent
      * periodically on the given interval.
      *
-     *   arg1 = the slot number of the keepalive to start
+     *   arg1 = the hardware slot number of the keepalive to start
      *   arg2 = interval in seconds
      *   obj = KeepalivePacketData object describing the data to be sent
      *
@@ -213,7 +263,7 @@
     /**
      * Requests that the specified keepalive packet be stopped.
      *
-     * arg1 = slot number of the keepalive to stop.
+     * arg1 = hardware slot number of the keepalive to stop.
      *
      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
      * @hide
@@ -228,7 +278,7 @@
      * This is also sent by KeepaliveTracker to the app's {@link SocketKeepalive},
      * so that the app's {@link SocketKeepalive.Callback} methods can be called.
      *
-     * arg1 = slot number of the keepalive
+     * arg1 = hardware slot number of the keepalive
      * arg2 = error code
      * @hide
      */
@@ -258,7 +308,7 @@
      * remote site will send ACK packets in response to the keepalive packets, the firmware also
      * needs to be configured to properly filter the ACKs to prevent the system from waking up.
      * This does not happen with UDP, so this message is TCP-specific.
-     * arg1 = slot number of the keepalive to filter for.
+     * arg1 = hardware slot number of the keepalive to filter for.
      * obj = the keepalive packet to send repeatedly.
      * @hide
      */
@@ -267,7 +317,7 @@
     /**
      * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
      * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
-     * arg1 = slot number of the keepalive packet filter to remove.
+     * arg1 = hardware slot number of the keepalive packet filter to remove.
      * @hide
      */
     public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
@@ -440,7 +490,15 @@
                                 + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
                                 + redirectUrl);
                     }
-                    onValidationStatus(msg.arg1 /* status */, redirectUrl);
+                    Uri uri = null;
+                    try {
+                        if (null != redirectUrl) {
+                            uri = Uri.parse(redirectUrl);
+                        }
+                    } catch (Exception e) {
+                        Log.wtf(LOG_TAG, "Surprising URI : " + redirectUrl, e);
+                    }
+                    onValidationStatus(msg.arg1 /* status */, uri);
                     break;
                 }
                 case CMD_SAVE_ACCEPT_UNVALIDATED: {
@@ -448,7 +506,8 @@
                     break;
                 }
                 case CMD_START_SOCKET_KEEPALIVE: {
-                    onStartSocketKeepalive(msg.arg1 /* slot */, msg.arg2 /* interval */,
+                    onStartSocketKeepalive(msg.arg1 /* slot */,
+                            Duration.ofSeconds(msg.arg2) /* interval */,
                             (KeepalivePacketData) msg.obj /* packet */);
                     break;
                 }
@@ -488,23 +547,64 @@
 
     /**
      * Register this network agent with ConnectivityService.
+     *
+     * This method can only be called once per network agent.
+     *
      * @return the Network associated with this network agent (which can also be obtained later
      *         by calling getNetwork() on this agent).
+     * @throws IllegalStateException thrown by the system server if this network agent is
+     *         already registered.
      */
     @NonNull
     public Network register() {
         if (VDBG) log("Registering NetworkAgent");
-        final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
-                .getSystemService(Context.CONNECTIVITY_SERVICE);
-        mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
-                new NetworkInfo(mInitialConfiguration.info),
-                mInitialConfiguration.properties, mInitialConfiguration.capabilities,
-                mInitialConfiguration.score, mInitialConfiguration.config, providerId);
-        mInitialConfiguration = null; // All this memory can now be GC'd
+        synchronized (mRegisterLock) {
+            if (mNetwork != null) {
+                throw new IllegalStateException("Agent already registered");
+            }
+            final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
+                    .getSystemService(Context.CONNECTIVITY_SERVICE);
+            mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
+                    new NetworkInfo(mInitialConfiguration.info),
+                    mInitialConfiguration.properties, mInitialConfiguration.capabilities,
+                    mInitialConfiguration.score, mInitialConfiguration.config, providerId);
+            mInitialConfiguration = null; // All this memory can now be GC'd
+        }
         return mNetwork;
     }
 
     /**
+     * Register this network agent with a testing harness.
+     *
+     * The returned Messenger sends messages to the Handler. This allows a test to send
+     * this object {@code CMD_*} messages as if they came from ConnectivityService, which
+     * is useful for testing the behavior.
+     *
+     * @hide
+     */
+    public Messenger registerForTest(final Network network) {
+        log("Registering NetworkAgent for test");
+        synchronized (mRegisterLock) {
+            mNetwork = network;
+            mInitialConfiguration = null;
+        }
+        return new Messenger(mHandler);
+    }
+
+    /**
+     * Waits for the handler to be idle.
+     * This is useful for testing, and has smaller scope than an accessor to mHandler.
+     * TODO : move the implementation in common library with the tests
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean waitForIdle(final long timeoutMs) {
+        final ConditionVariable cv = new ConditionVariable(false);
+        mHandler.post(cv::open);
+        return cv.block(timeoutMs);
+    }
+
+    /**
      * @return The Network associated with this agent, or null if it's not registered yet.
      */
     @Nullable
@@ -543,20 +643,23 @@
      * Must be called by the agent when the network's {@link LinkProperties} change.
      * @param linkProperties the new LinkProperties.
      */
-    public void sendLinkProperties(@NonNull LinkProperties linkProperties) {
+    public final void sendLinkProperties(@NonNull LinkProperties linkProperties) {
         Objects.requireNonNull(linkProperties);
         queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
     }
 
     /**
      * Inform ConnectivityService that this agent has now connected.
+     * Call {@link #unregister} to disconnect.
      */
-    public void setConnected() {
+    public void markConnected() {
         if (mIsLegacy) {
             throw new UnsupportedOperationException(
-                    "Legacy agents can't call setConnected.");
+                    "Legacy agents can't call markConnected.");
         }
-        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+        // |reason| cannot be used by the non-legacy agents
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null /* reason */,
+                mNetworkInfo.getExtraInfo());
         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
     }
 
@@ -568,10 +671,11 @@
      */
     public void unregister() {
         if (mIsLegacy) {
-            throw new UnsupportedOperationException(
-                    "Legacy agents can't call unregister.");
+            throw new UnsupportedOperationException("Legacy agents can't call unregister.");
         }
-        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+        // When unregistering an agent nobody should use the extrainfo (or reason) any more.
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null /* reason */,
+                null /* extraInfo */);
         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
     }
 
@@ -584,6 +688,7 @@
      *
      * @deprecated this is for backward compatibility only.
      * @param legacySubtype the legacy subtype.
+     * @hide
      */
     @Deprecated
     public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
@@ -608,6 +713,7 @@
      *
      * @deprecated this is for backward compatibility only.
      * @param extraInfo the ExtraInfo.
+     * @hide
      */
     @Deprecated
     public void setLegacyExtraInfo(@Nullable final String extraInfo) {
@@ -623,7 +729,7 @@
      * @hide TODO: expose something better.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public void sendNetworkInfo(NetworkInfo networkInfo) {
+    public final void sendNetworkInfo(NetworkInfo networkInfo) {
         if (!mIsLegacy) {
             throw new UnsupportedOperationException("Only legacy agents can call sendNetworkInfo.");
         }
@@ -634,7 +740,7 @@
      * Must be called by the agent when the network's {@link NetworkCapabilities} change.
      * @param networkCapabilities the new NetworkCapabilities.
      */
-    public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+    public final void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
         Objects.requireNonNull(networkCapabilities);
         mBandwidthUpdatePending.set(false);
         mLastBwRefreshTime = System.currentTimeMillis();
@@ -644,9 +750,10 @@
 
     /**
      * Must be called by the agent to update the score of this network.
-     * @param score the new score.
+     *
+     * @param score the new score, between 0 and 99.
      */
-    public void sendNetworkScore(int score) {
+    public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
         if (score < 0) {
             throw new IllegalArgumentException("Score must be >= 0");
         }
@@ -711,6 +818,7 @@
     /**
      * Called when ConnectivityService request a bandwidth update. The parent factory
      * shall try to overwrite this method and produce a bandwidth update if capable.
+     * @hide
      */
     public void onBandwidthUpdateRequested() {
         pollLceData();
@@ -729,15 +837,15 @@
      * {@code VALIDATION_STATUS_VALID} if Internet connectivity was validated,
      * {@code VALIDATION_STATUS_NOT_VALID} if Internet connectivity was not validated.
      *
-     * This may be called multiple times as network status changes, or if there are multiple
-     * subsequent attempts to validate connectivity that fail.
+     * This is guaranteed to be called again when the network status changes, but the system
+     * may also call this multiple times even if the status does not change.
      *
      * @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}.
-     * @param redirectUrl If Internet connectivity is being redirected (e.g., on a captive portal),
+     * @param redirectUri If Internet connectivity is being redirected (e.g., on a captive portal),
      *        this is the destination the probes are being redirected to, otherwise {@code null}.
      */
-    public void onValidationStatus(int status, @Nullable String redirectUrl) {
-        networkStatus(status, redirectUrl);
+    public void onValidationStatus(@ValidationStatus int status, @Nullable Uri redirectUri) {
+        networkStatus(status, null == redirectUri ? "" : redirectUri.toString());
     }
     /** @hide TODO delete once subclasses have moved to onValidationStatus */
     protected void networkStatus(int status, String redirectUrl) {
@@ -763,13 +871,25 @@
      * Requests that the network hardware send the specified packet at the specified interval.
      *
      * @param slot the hardware slot on which to start the keepalive.
-     * @param intervalSeconds the interval between packets
+     * @param interval the interval between packets, between 10 and 3600. Note that this API
+     *                 does not support sub-second precision and will round off the request.
      * @param packet the packet to send.
      */
-    public void onStartSocketKeepalive(int slot, int intervalSeconds,
+    // seconds is from SocketKeepalive.MIN_INTERVAL_SEC to MAX_INTERVAL_SEC, but these should
+    // not be exposed as constants because they may change in the future (API guideline 4.8)
+    // and should have getters if exposed at all. Getters can't be used in the annotation,
+    // so the values unfortunately need to be copied.
+    public void onStartSocketKeepalive(int slot, @NonNull Duration interval,
             @NonNull KeepalivePacketData packet) {
-        Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot, intervalSeconds,
-                packet);
+        final long intervalSeconds = interval.getSeconds();
+        if (intervalSeconds < SocketKeepalive.MIN_INTERVAL_SEC
+                || intervalSeconds > SocketKeepalive.MAX_INTERVAL_SEC) {
+            throw new IllegalArgumentException("Interval needs to be comprised between "
+                    + SocketKeepalive.MIN_INTERVAL_SEC + " and " + SocketKeepalive.MAX_INTERVAL_SEC
+                    + " but was " + intervalSeconds);
+        }
+        final Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot,
+                (int) intervalSeconds, packet);
         startSocketKeepalive(msg);
         msg.recycle();
     }
@@ -797,9 +917,11 @@
      * Must be called by the agent when a socket keepalive event occurs.
      *
      * @param slot the hardware slot on which the event occurred.
-     * @param event the event that occurred.
+     * @param event the event that occurred, as one of the SocketKeepalive.ERROR_*
+     *              or SocketKeepalive.SUCCESS constants.
      */
-    public void sendSocketKeepaliveEvent(int slot, int event) {
+    public final void sendSocketKeepaliveEvent(int slot,
+            @SocketKeepalive.KeepaliveEvent int event) {
         queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event);
     }
     /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
@@ -841,9 +963,18 @@
     }
 
     /**
-     * Called by ConnectivityService to inform this network transport of signal strength thresholds
+     * Called by ConnectivityService to inform this network agent of signal strength thresholds
      * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
      *
+     * When the system updates the list of thresholds that should wake up the CPU for a
+     * given agent it will call this method on the agent. The agent that implement this
+     * should implement it in hardware so as to ensure the CPU will be woken up on breach.
+     * Agents are expected to react to a breach by sending an updated NetworkCapabilities
+     * object with the appropriate signal strength to sendNetworkCapabilities.
+     *
+     * The specific units are bearer-dependent. See details on the units and requests in
+     * {@link NetworkCapabilities.Builder#setSignalStrength}.
+     *
      * @param thresholds the array of thresholds that should trigger wakeups.
      */
     public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) {
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
index 7e2db4a..fee868a 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -108,6 +108,7 @@
     /**
      *
      * @return whether the sign in to network notification is enabled by this configuration.
+     * @hide
      */
     public boolean isProvisioningNotificationEnabled() {
         return !provisioningNotificationDisabled;
@@ -122,6 +123,7 @@
 
     /**
      * @return the subscriber ID, or null if none.
+     * @hide
      */
     @Nullable
     public String getSubscriberId() {
@@ -138,6 +140,7 @@
 
     /**
      * @return whether NAT64 prefix detection is enabled.
+     * @hide
      */
     public boolean isNat64DetectionEnabled() {
         return !skip464xlat;
@@ -152,6 +155,7 @@
     /**
      * @return the legacy type
      */
+    @ConnectivityManager.LegacyNetworkType
     public int getLegacyType() {
         return legacyType;
     }
@@ -203,7 +207,7 @@
     /**
      * Builder class to facilitate constructing {@link NetworkAgentConfig} objects.
      */
-    public static class Builder {
+    public static final class Builder {
         private final NetworkAgentConfig mConfig = new NetworkAgentConfig();
 
         /**
@@ -247,6 +251,7 @@
          * Sets the subscriber ID for this network.
          *
          * @return this builder, to facilitate chaining.
+         * @hide
          */
         @NonNull
         public Builder setSubscriberId(@Nullable String subscriberId) {
@@ -259,6 +264,7 @@
          * and reduce idle traffic on networks that are known to be IPv6-only without a NAT64.
          *
          * @return this builder, to facilitate chaining.
+         * @hide
          */
         @NonNull
         public Builder disableNat64Detection() {
@@ -271,6 +277,7 @@
          * perform its own carrier-specific provisioning procedure.
          *
          * @return this builder, to facilitate chaining.
+         * @hide
          */
         @NonNull
         public Builder disableProvisioningNotification() {
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 873d6e914..57d5d03 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -16,9 +16,12 @@
 
 package android.net;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -32,14 +35,13 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.BitUtils;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.Arrays;
 import java.util.Objects;
 import java.util.Set;
 import java.util.StringJoiner;
@@ -88,6 +90,7 @@
     /**
      * Completely clears the contents of this object, removing even the capabilities that are set
      * by default when the object is constructed.
+     * @hide
      */
     public void clearAll() {
         mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
@@ -96,7 +99,7 @@
         mTransportInfo = null;
         mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
         mUids = null;
-        mAdministratorUids.clear();
+        mAdministratorUids = new int[0];
         mOwnerUid = Process.INVALID_UID;
         mSSID = null;
         mPrivateDnsBroken = false;
@@ -117,7 +120,7 @@
         mTransportInfo = nc.mTransportInfo;
         mSignalStrength = nc.mSignalStrength;
         setUids(nc.mUids); // Will make the defensive copy
-        setAdministratorUids(nc.mAdministratorUids);
+        setAdministratorUids(nc.getAdministratorUids());
         mOwnerUid = nc.mOwnerUid;
         mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
         mSSID = nc.mSSID;
@@ -166,6 +169,7 @@
             NET_CAPABILITY_OEM_PAID,
             NET_CAPABILITY_MCX,
             NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+            NET_CAPABILITY_TEMPORARILY_NOT_METERED,
     })
     public @interface NetCapability { }
 
@@ -333,8 +337,16 @@
     @SystemApi
     public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24;
 
+    /**
+     * This capability will be set for networks that are generally metered, but are currently
+     * unmetered, e.g., because the user is in a particular area. This capability can be changed at
+     * any time. When it is removed, applications are responsible for stopping any data transfer
+     * that should not occur on a metered network.
+     */
+    public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -350,7 +362,8 @@
             | (1 << NET_CAPABILITY_FOREGROUND)
             | (1 << NET_CAPABILITY_NOT_CONGESTED)
             | (1 << NET_CAPABILITY_NOT_SUSPENDED)
-            | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+            | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY
+            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED));
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -414,12 +427,28 @@
             | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
 
     /**
+     * Capabilities that are allowed for test networks. This list must be set so that it is safe
+     * for an unprivileged user to create a network with these capabilities via shell. As such,
+     * it must never contain capabilities that are generally useful to the system, such as
+     * INTERNET, IMS, SUPL, etc.
+     */
+    private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
+            (1 << NET_CAPABILITY_NOT_METERED)
+            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+            | (1 << NET_CAPABILITY_NOT_RESTRICTED)
+            | (1 << NET_CAPABILITY_NOT_VPN)
+            | (1 << NET_CAPABILITY_NOT_ROAMING)
+            | (1 << NET_CAPABILITY_NOT_CONGESTED)
+            | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+
+    /**
      * Adds the given capability to this {@code NetworkCapability} instance.
-     * Multiple capabilities may be applied sequentially.  Note that when searching
-     * for a network to satisfy a request, all capabilities requested must be satisfied.
+     * Note that when searching for a network to satisfy a request, all capabilities
+     * requested must be satisfied.
      *
      * @param capability the capability to be added.
      * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
      */
     public @NonNull NetworkCapabilities addCapability(@NetCapability int capability) {
         // If the given capability was previously added to the list of unwanted capabilities
@@ -434,9 +463,9 @@
 
     /**
      * Adds the given capability to the list of unwanted capabilities of this
-     * {@code NetworkCapability} instance.  Multiple unwanted capabilities may be applied
-     * sequentially.  Note that when searching for a network to satisfy a request, the network
-     * must not contain any capability from unwanted capability list.
+     * {@code NetworkCapability} instance. Note that when searching for a network to
+     * satisfy a request, the network must not contain any capability from unwanted capability
+     * list.
      * <p>
      * If the capability was previously added to the list of required capabilities (for
      * example, it was there by default or added using {@link #addCapability(int)} method), then
@@ -456,6 +485,7 @@
      *
      * @param capability the capability to be removed.
      * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
      */
     public @NonNull NetworkCapabilities removeCapability(@NetCapability int capability) {
         // Note that this method removes capabilities that were added via addCapability(int),
@@ -470,7 +500,7 @@
     /**
      * Sets (or clears) the given capability on this {@link NetworkCapabilities}
      * instance.
-     *
+     * @hide
      */
     public @NonNull NetworkCapabilities setCapability(@NetCapability int capability,
             boolean value) {
@@ -613,7 +643,6 @@
      * @return {@code true} if the network should be restricted.
      * @hide
      */
-    @SystemApi
     public boolean deduceRestrictedCapability() {
         // Check if we have any capability that forces the network to be restricted.
         final boolean forceRestrictedCapability =
@@ -644,6 +673,34 @@
     }
 
     /**
+     * Test networks have strong restrictions on what capabilities they can have. Enforce these
+     * restrictions.
+     * @hide
+     */
+    public void restrictCapabilitesForTestNetwork(int creatorUid) {
+        final long originalCapabilities = mNetworkCapabilities;
+        final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
+        final int originalSignalStrength = mSignalStrength;
+        final int originalOwnerUid = getOwnerUid();
+        final int[] originalAdministratorUids = getAdministratorUids();
+        clearAll();
+        // Reset the transports to only contain TRANSPORT_TEST.
+        mTransportTypes = (1 << TRANSPORT_TEST);
+        mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
+        mNetworkSpecifier = originalSpecifier;
+        mSignalStrength = originalSignalStrength;
+
+        // Only retain the owner and administrator UIDs if they match the app registering the remote
+        // caller that registered the network.
+        if (originalOwnerUid == creatorUid) {
+            setOwnerUid(creatorUid);
+        }
+        if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) {
+            setAdministratorUids(new int[] {creatorUid});
+        }
+    }
+
+    /**
      * Representing the transport type.  Apps should generally not care about transport.  A
      * request for a fast internet connection could be satisfied by a number of different
      * transports.  If any are specified here it will be satisfied a Network that matches
@@ -731,7 +788,7 @@
 
     /**
      * Adds the given transport type to this {@code NetworkCapability} instance.
-     * Multiple transports may be applied sequentially.  Note that when searching
+     * Multiple transports may be applied.  Note that when searching
      * for a network to satisfy a request, any listed in the request will satisfy the request.
      * For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
      * {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
@@ -740,6 +797,7 @@
      *
      * @param transportType the transport type to be added.
      * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
      */
     public @NonNull NetworkCapabilities addTransportType(@Transport int transportType) {
         checkValidTransportType(transportType);
@@ -852,6 +910,7 @@
 
     /**
      * Set the UID of the owner app.
+     * @hide
      */
     public @NonNull NetworkCapabilities setOwnerUid(final int uid) {
         mOwnerUid = uid;
@@ -869,6 +928,8 @@
      *   <li>The user's location toggle is on
      * </ol>
      *
+     * Instances of NetworkCapabilities sent to apps without the appropriate permissions will
+     * have this field cleared out.
      */
     public int getOwnerUid() {
         return mOwnerUid;
@@ -884,18 +945,22 @@
      * <p>For NetworkCapability instances being sent from the System Server, this value MUST be
      * empty unless the destination is 1) the System Server, or 2) Telephony. In either case, the
      * receiving entity must have the ACCESS_FINE_LOCATION permission and target R+.
+     *
+     * <p>When received from an app in a NetworkRequest this is always cleared out by the system
+     * server. This field is never used for matching NetworkRequests to NetworkAgents.
      */
-    private final List<Integer> mAdministratorUids = new ArrayList<>();
+    @NonNull private int[] mAdministratorUids = new int[0];
 
     /**
-     * Sets the list of UIDs that are administrators of this network.
+     * Sets the int[] of UIDs that are administrators of this network.
      *
      * <p>UIDs included in administratorUids gain administrator privileges over this Network.
      * Examples of UIDs that should be included in administratorUids are:
+     *
      * <ul>
-     *     <li>Carrier apps with privileges for the relevant subscription
-     *     <li>Active VPN apps
-     *     <li>Other application groups with a particular Network-related role
+     *   <li>Carrier apps with privileges for the relevant subscription
+     *   <li>Active VPN apps
+     *   <li>Other application groups with a particular Network-related role
      * </ul>
      *
      * <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
@@ -903,28 +968,75 @@
      * <p>An app is granted owner privileges over Networks that it supplies. The owner UID MUST
      * always be included in administratorUids.
      *
+     * <p>The administrator UIDs are set by network agents.
+     *
      * @param administratorUids the UIDs to be set as administrators of this Network.
+     * @throws IllegalArgumentException if duplicate UIDs are contained in administratorUids
+     * @see #mAdministratorUids
      * @hide
      */
     @NonNull
-    @SystemApi
-    public NetworkCapabilities setAdministratorUids(
-            @NonNull final List<Integer> administratorUids) {
-        mAdministratorUids.clear();
-        mAdministratorUids.addAll(administratorUids);
+    public NetworkCapabilities setAdministratorUids(@NonNull final int[] administratorUids) {
+        mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length);
+        Arrays.sort(mAdministratorUids);
+        for (int i = 0; i < mAdministratorUids.length - 1; i++) {
+            if (mAdministratorUids[i] >= mAdministratorUids[i + 1]) {
+                throw new IllegalArgumentException("All administrator UIDs must be unique");
+            }
+        }
         return this;
     }
 
     /**
-     * Retrieves the list of UIDs that are administrators of this Network.
+     * Retrieves the UIDs that are administrators of this Network.
      *
-     * @return the List of UIDs that are administrators of this Network
+     * <p>This is only populated in NetworkCapabilities objects that come from network agents for
+     * networks that are managed by specific apps on the system, such as carrier privileged apps or
+     * wifi suggestion apps. This will include the network owner.
+     *
+     * @return the int[] of UIDs that are administrators of this Network
+     * @see #mAdministratorUids
      * @hide
      */
     @NonNull
     @SystemApi
-    public List<Integer> getAdministratorUids() {
-        return Collections.unmodifiableList(mAdministratorUids);
+    @TestApi
+    public int[] getAdministratorUids() {
+        return Arrays.copyOf(mAdministratorUids, mAdministratorUids.length);
+    }
+
+    /**
+     * Tests if the set of administrator UIDs of this network is the same as that of the passed one.
+     *
+     * <p>The administrator UIDs must be in sorted order.
+     *
+     * <p>nc is assumed non-null. Else, NPE.
+     *
+     * @hide
+     */
+    @VisibleForTesting(visibility = PRIVATE)
+    public boolean equalsAdministratorUids(@NonNull final NetworkCapabilities nc) {
+        return Arrays.equals(mAdministratorUids, nc.mAdministratorUids);
+    }
+
+    /**
+     * Combine the administrator UIDs of the capabilities.
+     *
+     * <p>This is only legal if either of the administrators lists are empty, or if they are equal.
+     * Combining administrator UIDs is only possible for combining non-overlapping sets of UIDs.
+     *
+     * <p>If both administrator lists are non-empty but not equal, they conflict with each other. In
+     * this case, it would not make sense to add them together.
+     */
+    private void combineAdministratorUids(@NonNull final NetworkCapabilities nc) {
+        if (nc.mAdministratorUids.length == 0) return;
+        if (mAdministratorUids.length == 0) {
+            mAdministratorUids = Arrays.copyOf(nc.mAdministratorUids, nc.mAdministratorUids.length);
+            return;
+        }
+        if (!equalsAdministratorUids(nc)) {
+            throw new IllegalStateException("Can't combine two different administrator UID lists");
+        }
     }
 
     /**
@@ -945,15 +1057,10 @@
      * Sets the upstream bandwidth for this network in Kbps.  This always only refers to
      * the estimated first hop transport bandwidth.
      * <p>
-     * Note that when used to request a network, this specifies the minimum acceptable.
-     * When received as the state of an existing network this specifies the typical
-     * first hop bandwidth expected.  This is never measured, but rather is inferred
-     * from technology type and other link parameters.  It could be used to differentiate
-     * between very slow 1xRTT cellular links and other faster networks or even between
-     * 802.11b vs 802.11AC wifi technologies.  It should not be used to differentiate between
-     * fast backhauls and slow backhauls.
+     * {@see Builder#setLinkUpstreamBandwidthKbps}
      *
      * @param upKbps the estimated first hop upstream (device to network) bandwidth.
+     * @hide
      */
     public @NonNull NetworkCapabilities setLinkUpstreamBandwidthKbps(int upKbps) {
         mLinkUpBandwidthKbps = upKbps;
@@ -974,15 +1081,10 @@
      * Sets the downstream bandwidth for this network in Kbps.  This always only refers to
      * the estimated first hop transport bandwidth.
      * <p>
-     * Note that when used to request a network, this specifies the minimum acceptable.
-     * When received as the state of an existing network this specifies the typical
-     * first hop bandwidth expected.  This is never measured, but rather is inferred
-     * from technology type and other link parameters.  It could be used to differentiate
-     * between very slow 1xRTT cellular links and other faster networks or even between
-     * 802.11b vs 802.11AC wifi technologies.  It should not be used to differentiate between
-     * fast backhauls and slow backhauls.
+     * {@see Builder#setLinkUpstreamBandwidthKbps}
      *
      * @param downKbps the estimated first hop downstream (network to device) bandwidth.
+     * @hide
      */
     public @NonNull NetworkCapabilities setLinkDownstreamBandwidthKbps(int downKbps) {
         mLinkDownBandwidthKbps = downKbps;
@@ -1041,6 +1143,7 @@
      * @param networkSpecifier A concrete, parcelable framework class that extends
      *                         NetworkSpecifier.
      * @return This NetworkCapabilities instance, to facilitate chaining.
+     * @hide
      */
     public @NonNull NetworkCapabilities setNetworkSpecifier(
             @NonNull NetworkSpecifier networkSpecifier) {
@@ -1062,7 +1165,6 @@
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
-    @SystemApi
     public @NonNull NetworkCapabilities setTransportInfo(@NonNull TransportInfo transportInfo) {
         mTransportInfo = transportInfo;
         return this;
@@ -1072,7 +1174,7 @@
      * Gets the optional bearer specific network specifier. May be {@code null} if not set.
      *
      * @return The optional {@link NetworkSpecifier} specifying the bearer specific network
-     *         specifier or {@code null}. See {@link #setNetworkSpecifier}.
+     *         specifier or {@code null}.
      */
     public @Nullable NetworkSpecifier getNetworkSpecifier() {
         return mNetworkSpecifier;
@@ -1099,7 +1201,7 @@
     }
 
     private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
-        return mNetworkSpecifier == null || mNetworkSpecifier.satisfiedBy(nc.mNetworkSpecifier)
+        return mNetworkSpecifier == null || mNetworkSpecifier.canBeSatisfiedBy(nc.mNetworkSpecifier)
                 || nc.mNetworkSpecifier instanceof MatchAllNetworkSpecifier;
     }
 
@@ -1142,6 +1244,7 @@
      * effect when requesting a callback.
      *
      * @param signalStrength the bearer-specific signal strength.
+     * @hide
      */
     public @NonNull NetworkCapabilities setSignalStrength(int signalStrength) {
         mSignalStrength = signalStrength;
@@ -1368,7 +1471,6 @@
      * Sets the SSID of this network.
      * @hide
      */
-    @SystemApi
     public @NonNull NetworkCapabilities setSSID(@Nullable String ssid) {
         mSSID = ssid;
         return this;
@@ -1379,7 +1481,8 @@
      * @hide
      */
     @SystemApi
-    public @Nullable String getSSID() {
+    @TestApi
+    public @Nullable String getSsid() {
         return mSSID;
     }
 
@@ -1431,6 +1534,7 @@
         combineUids(nc);
         combineSSIDs(nc);
         combineRequestor(nc);
+        combineAdministratorUids(nc);
     }
 
     /**
@@ -1544,7 +1648,8 @@
                 && equalsUids(that)
                 && equalsSSID(that)
                 && equalsPrivateDnsBroken(that)
-                && equalsRequestor(that);
+                && equalsRequestor(that)
+                && equalsAdministratorUids(that);
     }
 
     @Override
@@ -1564,7 +1669,8 @@
                 + Objects.hashCode(mTransportInfo) * 41
                 + Objects.hashCode(mPrivateDnsBroken) * 43
                 + Objects.hashCode(mRequestorUid) * 47
-                + Objects.hashCode(mRequestorPackageName) * 53;
+                + Objects.hashCode(mRequestorPackageName) * 53
+                + Arrays.hashCode(mAdministratorUids) * 59;
     }
 
     @Override
@@ -1585,7 +1691,7 @@
         dest.writeArraySet(mUids);
         dest.writeString(mSSID);
         dest.writeBoolean(mPrivateDnsBroken);
-        dest.writeList(mAdministratorUids);
+        dest.writeIntArray(getAdministratorUids());
         dest.writeInt(mOwnerUid);
         dest.writeInt(mRequestorUid);
         dest.writeString(mRequestorPackageName);
@@ -1609,7 +1715,7 @@
                         null /* ClassLoader, null for default */);
                 netCap.mSSID = in.readString();
                 netCap.mPrivateDnsBroken = in.readBoolean();
-                netCap.setAdministratorUids(in.readArrayList(null));
+                netCap.setAdministratorUids(in.createIntArray());
                 netCap.mOwnerUid = in.readInt();
                 netCap.mRequestorUid = in.readInt();
                 netCap.mRequestorPackageName = in.readString();
@@ -1666,8 +1772,8 @@
             sb.append(" OwnerUid: ").append(mOwnerUid);
         }
 
-        if (!mAdministratorUids.isEmpty()) {
-            sb.append(" AdministratorUids: ").append(mAdministratorUids);
+        if (mAdministratorUids.length == 0) {
+            sb.append(" AdministratorUids: ").append(Arrays.toString(mAdministratorUids));
         }
 
         if (null != mSSID) {
@@ -1782,6 +1888,7 @@
             case NET_CAPABILITY_OEM_PAID:             return "OEM_PAID";
             case NET_CAPABILITY_MCX:                  return "MCX";
             case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
+            case NET_CAPABILITY_TEMPORARILY_NOT_METERED:    return "TEMPORARILY_NOT_METERED";
             default:                                  return Integer.toString(capability);
         }
     }
@@ -1859,25 +1966,32 @@
     }
 
     /**
-     * Set the uid of the app making the request.
+     * Set the UID of the app making the request.
      *
-     * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
-     * via the public {@link ConnectivityManager} API's will have this field overwritten.
+     * For instances of NetworkCapabilities representing a request, sets the
+     * UID of the app making the request. For a network created by the system,
+     * sets the UID of the only app whose requests can match this network.
+     * This can be set to {@link Process#INVALID_UID} if there is no such app,
+     * or if this instance of NetworkCapabilities is about to be sent to a
+     * party that should not learn about this.
      *
      * @param uid UID of the app.
      * @hide
      */
-    @SystemApi
     public @NonNull NetworkCapabilities setRequestorUid(int uid) {
         mRequestorUid = uid;
         return this;
     }
 
     /**
-     * @return the uid of the app making the request.
+     * Returns the UID of the app making the request.
      *
-     * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest}
-     * object was not obtained from {@link ConnectivityManager}.
+     * For a NetworkRequest being made by an app, contains the app's UID. For a network
+     * created by the system, contains the UID of the only app whose requests can match
+     * this network, or {@link Process#INVALID_UID} if none or if the
+     * caller does not have permission to learn about this.
+     *
+     * @return the uid of the app making the request.
      * @hide
      */
     public int getRequestorUid() {
@@ -1887,23 +2001,29 @@
     /**
      * Set the package name of the app making the request.
      *
-     * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
-     * via the public {@link ConnectivityManager} API's will have this field overwritten.
+     * For instances of NetworkCapabilities representing a request, sets the
+     * package name of the app making the request. For a network created by the system,
+     * sets the package name of the only app whose requests can match this network.
+     * This can be set to null if there is no such app, or if this instance of
+     * NetworkCapabilities is about to be sent to a party that should not learn about this.
      *
      * @param packageName package name of the app.
      * @hide
      */
-    @SystemApi
     public @NonNull NetworkCapabilities setRequestorPackageName(@NonNull String packageName) {
         mRequestorPackageName = packageName;
         return this;
     }
 
     /**
-     * @return the package name of the app making the request.
+     * Returns the package name of the app making the request.
      *
-     * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained
-     * from {@link ConnectivityManager}.
+     * For a NetworkRequest being made by an app, contains the app's package name. For a
+     * network created by the system, contains the package name of the only app whose
+     * requests can match this network, or null if none or if the caller does not have
+     * permission to learn about this.
+     *
+     * @return the package name of the app making the request.
      * @hide
      */
     @Nullable
@@ -1912,9 +2032,9 @@
     }
 
     /**
-     * Set the uid and package name of the app making the request.
+     * Set the uid and package name of the app causing this network to exist.
      *
-     * Note: This is intended to be only invoked from within connectivitiy service.
+     * {@see #setRequestorUid} and {@link #setRequestorPackageName}
      *
      * @param uid UID of the app.
      * @param packageName package name of the app.
@@ -1973,4 +2093,316 @@
         return mRequestorUid == nc.mRequestorUid
                 && TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
     }
+
+    /**
+     * Builder class for NetworkCapabilities.
+     *
+     * This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
+     * the built class require holding a signature permission to use - mostly
+     * {@link android.Manifest.permission.NETWORK_FACTORY}, but refer to the specific
+     * description of each setter. As this class lives entirely in app space it does not
+     * enforce these restrictions itself but the system server clears out the relevant
+     * fields when receiving a NetworkCapabilities object from a caller without the
+     * appropriate permission.
+     *
+     * Apps don't use this builder directly. Instead, they use {@link NetworkRequest} via
+     * its builder object.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static class Builder {
+        private final NetworkCapabilities mCaps;
+
+        /**
+         * Creates a new Builder to construct NetworkCapabilities objects.
+         */
+        public Builder() {
+            mCaps = new NetworkCapabilities();
+        }
+
+        /**
+         * Creates a new Builder of NetworkCapabilities from an existing instance.
+         */
+        public Builder(@NonNull final NetworkCapabilities nc) {
+            Objects.requireNonNull(nc);
+            mCaps = new NetworkCapabilities(nc);
+        }
+
+        /**
+         * Adds the given transport type.
+         *
+         * Multiple transports may be added. Note that when searching for a network to satisfy a
+         * request, satisfying any of the transports listed in the request will satisfy the request.
+         * For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
+         * {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
+         * to be selected. This is logically different than
+         * {@code NetworkCapabilities.NET_CAPABILITY_*}.
+         *
+         * @param transportType the transport type to be added or removed.
+         * @return this builder
+         */
+        @NonNull
+        public Builder addTransportType(@Transport int transportType) {
+            checkValidTransportType(transportType);
+            mCaps.addTransportType(transportType);
+            return this;
+        }
+
+        /**
+         * Removes the given transport type.
+         *
+         * {@see #addTransportType}.
+         *
+         * @param transportType the transport type to be added or removed.
+         * @return this builder
+         */
+        @NonNull
+        public Builder removeTransportType(@Transport int transportType) {
+            checkValidTransportType(transportType);
+            mCaps.removeTransportType(transportType);
+            return this;
+        }
+
+        /**
+         * Adds the given capability.
+         *
+         * @param capability the capability
+         * @return this builder
+         */
+        @NonNull
+        public Builder addCapability(@NetCapability final int capability) {
+            mCaps.setCapability(capability, true);
+            return this;
+        }
+
+        /**
+         * Removes the given capability.
+         *
+         * @param capability the capability
+         * @return this builder
+         */
+        @NonNull
+        public Builder removeCapability(@NetCapability final int capability) {
+            mCaps.setCapability(capability, false);
+            return this;
+        }
+
+        /**
+         * Sets the owner UID.
+         *
+         * The default value is {@link Process#INVALID_UID}. Pass this value to reset.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source.
+         *
+         * @param ownerUid the owner UID
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setOwnerUid(final int ownerUid) {
+            mCaps.setOwnerUid(ownerUid);
+            return this;
+        }
+
+        /**
+         * Sets the list of UIDs that are administrators of this network.
+         *
+         * <p>UIDs included in administratorUids gain administrator privileges over this
+         * Network. Examples of UIDs that should be included in administratorUids are:
+         * <ul>
+         *     <li>Carrier apps with privileges for the relevant subscription
+         *     <li>Active VPN apps
+         *     <li>Other application groups with a particular Network-related role
+         * </ul>
+         *
+         * <p>In general, user-supplied networks (such as WiFi networks) do not have
+         * administrators.
+         *
+         * <p>An app is granted owner privileges over Networks that it supplies. The owner
+         * UID MUST always be included in administratorUids.
+         *
+         * The default value is the empty array. Pass an empty array to reset.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source, such as an app using reflection to call this or
+         * mutate the member in the built object.
+         *
+         * @param administratorUids the UIDs to be set as administrators of this Network.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setAdministratorUids(@NonNull final int[] administratorUids) {
+            Objects.requireNonNull(administratorUids);
+            mCaps.setAdministratorUids(administratorUids);
+            return this;
+        }
+
+        /**
+         * Sets the upstream bandwidth of the link.
+         *
+         * Sets the upstream bandwidth for this network in Kbps. This always only refers to
+         * the estimated first hop transport bandwidth.
+         * <p>
+         * Note that when used to request a network, this specifies the minimum acceptable.
+         * When received as the state of an existing network this specifies the typical
+         * first hop bandwidth expected. This is never measured, but rather is inferred
+         * from technology type and other link parameters. It could be used to differentiate
+         * between very slow 1xRTT cellular links and other faster networks or even between
+         * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
+         * fast backhauls and slow backhauls.
+         *
+         * @param upKbps the estimated first hop upstream (device to network) bandwidth.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setLinkUpstreamBandwidthKbps(final int upKbps) {
+            mCaps.setLinkUpstreamBandwidthKbps(upKbps);
+            return this;
+        }
+
+        /**
+         * Sets the downstream bandwidth for this network in Kbps. This always only refers to
+         * the estimated first hop transport bandwidth.
+         * <p>
+         * Note that when used to request a network, this specifies the minimum acceptable.
+         * When received as the state of an existing network this specifies the typical
+         * first hop bandwidth expected. This is never measured, but rather is inferred
+         * from technology type and other link parameters. It could be used to differentiate
+         * between very slow 1xRTT cellular links and other faster networks or even between
+         * 802.11b vs 802.11AC wifi technologies. It should not be used to differentiate between
+         * fast backhauls and slow backhauls.
+         *
+         * @param downKbps the estimated first hop downstream (network to device) bandwidth.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setLinkDownstreamBandwidthKbps(final int downKbps) {
+            mCaps.setLinkDownstreamBandwidthKbps(downKbps);
+            return this;
+        }
+
+        /**
+         * Sets the optional bearer specific network specifier.
+         * This has no meaning if a single transport is also not specified, so calling
+         * this without a single transport set will generate an exception, as will
+         * subsequently adding or removing transports after this is set.
+         * </p>
+         *
+         * @param specifier a concrete, parcelable framework class that extends NetworkSpecifier,
+         *        or null to clear it.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setNetworkSpecifier(@Nullable final NetworkSpecifier specifier) {
+            mCaps.setNetworkSpecifier(specifier);
+            return this;
+        }
+
+        /**
+         * Sets the optional transport specific information.
+         *
+         * @param info A concrete, parcelable framework class that extends {@link TransportInfo},
+         *             or null to clear it.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setTransportInfo(@Nullable final TransportInfo info) {
+            mCaps.setTransportInfo(info);
+            return this;
+        }
+
+        /**
+         * Sets the signal strength. This is a signed integer, with higher values indicating a
+         * stronger signal. The exact units are bearer-dependent. For example, Wi-Fi uses the
+         * same RSSI units reported by wifi code.
+         * <p>
+         * Note that when used to register a network callback, this specifies the minimum
+         * acceptable signal strength. When received as the state of an existing network it
+         * specifies the current value. A value of code SIGNAL_STRENGTH_UNSPECIFIED} means
+         * no value when received and has no effect when requesting a callback.
+         *
+         * Note: for security the system will throw if it receives a NetworkRequest where
+         * the underlying NetworkCapabilities has this member set from a source that does
+         * not hold the {@link android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP}
+         * permission. Apps with this permission can use this indirectly through
+         * {@link android.net.NetworkRequest}.
+         *
+         * @param signalStrength the bearer-specific signal strength.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP)
+        public Builder setSignalStrength(final int signalStrength) {
+            mCaps.setSignalStrength(signalStrength);
+            return this;
+        }
+
+        /**
+         * Sets the SSID of this network.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source, like an app using reflection to set this.
+         *
+         * @param ssid the SSID, or null to clear it.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setSsid(@Nullable final String ssid) {
+            mCaps.setSSID(ssid);
+            return this;
+        }
+
+        /**
+         * Set the uid of the app causing this network to exist.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source.
+         *
+         * @param uid UID of the app.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setRequestorUid(final int uid) {
+            mCaps.setRequestorUid(uid);
+            return this;
+        }
+
+        /**
+         * Set the package name of the app causing this network to exist.
+         *
+         * Note: for security the system will clear out this field when received from a
+         * non-privileged source.
+         *
+         * @param packageName package name of the app, or null to clear it.
+         * @return this builder
+         */
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+        public Builder setRequestorPackageName(@Nullable final String packageName) {
+            mCaps.setRequestorPackageName(packageName);
+            return this;
+        }
+
+        /**
+         * Builds the instance of the capabilities.
+         *
+         * @return the built instance of NetworkCapabilities.
+         */
+        @NonNull
+        public NetworkCapabilities build() {
+            if (mCaps.getOwnerUid() != Process.INVALID_UID) {
+                if (!ArrayUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) {
+                    throw new IllegalStateException("The owner UID must be included in "
+                            + " administrator UIDs.");
+                }
+            }
+            return new NetworkCapabilities(mCaps);
+        }
+    }
 }
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index febc730..4edbcd0 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -25,6 +25,7 @@
 import android.net.wifi.WifiManager;
 import android.os.Build;
 import android.service.NetworkIdentityProto;
+import android.telephony.Annotation.NetworkType;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -39,16 +40,6 @@
 public class NetworkIdentity implements Comparable<NetworkIdentity> {
     private static final String TAG = "NetworkIdentity";
 
-    /**
-     * When enabled, combine all {@link #mSubType} together under
-     * {@link #SUBTYPE_COMBINED}.
-     *
-     * @deprecated we no longer offer to collect statistics on a per-subtype
-     *             basis; this is always disabled.
-     */
-    @Deprecated
-    public static final boolean COMBINE_SUBTYPE_ENABLED = true;
-
     public static final int SUBTYPE_COMBINED = -1;
 
     final int mType;
@@ -63,7 +54,7 @@
             int type, int subType, String subscriberId, String networkId, boolean roaming,
             boolean metered, boolean defaultNetwork) {
         mType = type;
-        mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType;
+        mSubType = subType;
         mSubscriberId = subscriberId;
         mNetworkId = networkId;
         mRoaming = roaming;
@@ -95,7 +86,7 @@
         final StringBuilder builder = new StringBuilder("{");
         builder.append("type=").append(getNetworkTypeName(mType));
         builder.append(", subType=");
-        if (COMBINE_SUBTYPE_ENABLED) {
+        if (mSubType == SUBTYPE_COMBINED) {
             builder.append("COMBINED");
         } else {
             builder.append(mSubType);
@@ -187,13 +178,14 @@
     }
 
     /**
-     * Build a {@link NetworkIdentity} from the given {@link NetworkState},
-     * assuming that any mobile networks are using the current IMSI.
+     * Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType},
+     * assuming that any mobile networks are using the current IMSI. The subType if applicable,
+     * should be set as one of the TelephonyManager.NETWORK_TYPE_* constants, or
+     * {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not.
      */
     public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
-            boolean defaultNetwork) {
+            boolean defaultNetwork, @NetworkType int subType) {
         final int type = state.networkInfo.getType();
-        final int subType = state.networkInfo.getSubtype();
 
         String subscriberId = null;
         String networkId = null;
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 08fe159..d752901 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -22,6 +22,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.Annotation.NetworkType;
+import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -538,7 +539,7 @@
     @Override
     public String toString() {
         synchronized (this) {
-            StringBuilder builder = new StringBuilder("[");
+            final StringBuilder builder = new StringBuilder("[");
             builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()).
             append("], state: ").append(mState).append("/").append(mDetailedState).
             append(", reason: ").append(mReason == null ? "(unspecified)" : mReason).
@@ -551,6 +552,32 @@
         }
     }
 
+    /**
+     * Returns a brief summary string suitable for debugging.
+     * @hide
+     */
+    public String toShortString() {
+        synchronized (this) {
+            final StringBuilder builder = new StringBuilder();
+            builder.append(getTypeName());
+
+            final String subtype = getSubtypeName();
+            if (!TextUtils.isEmpty(subtype)) {
+                builder.append("[").append(subtype).append("]");
+            }
+
+            builder.append(" ");
+            builder.append(mDetailedState);
+            if (mIsRoaming) {
+                builder.append(" ROAMING");
+            }
+            if (mExtraInfo != null) {
+                builder.append(" extra: ").append(mExtraInfo);
+            }
+            return builder.toString();
+        }
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 14442a2..1922b6d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -21,7 +21,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -56,7 +55,6 @@
  * @hide
  */
 @SystemService(Context.NETWORK_POLICY_SERVICE)
-@SystemApi
 public class NetworkPolicyManager {
 
     /* POLICY_* are masks and can be ORed, although currently they are not.*/
@@ -162,11 +160,13 @@
 
     /**
      * Mask used to check if an override value is marked as unmetered.
+     * @hide
      */
     public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1 << 0;
 
     /**
      * Mask used to check if an override value is marked as congested.
+     * @hide
      */
     public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 1 << 1;
 
@@ -294,7 +294,6 @@
 
     /** @hide */
     @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
-    @SystemApi
     public void registerSubscriptionCallback(@NonNull SubscriptionCallback callback) {
         if (callback == null) {
             throw new NullPointerException("Callback cannot be null.");
@@ -309,7 +308,6 @@
 
     /** @hide */
     @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
-    @SystemApi
     public void unregisterSubscriptionCallback(@NonNull SubscriptionCallback callback) {
         if (callback == null) {
             throw new NullPointerException("Callback cannot be null.");
@@ -373,6 +371,7 @@
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first
      * @param callingPackage the name of the package making the call.
+     * @hide
      */
     public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
             @SubscriptionOverrideMask int overrideValue, long timeoutMillis,
@@ -391,6 +390,7 @@
      * @param subId the subscriber this relationship applies to.
      * @param plans the list of plans.
      * @param callingPackage the name of the package making the call
+     * @hide
      */
     public void setSubscriptionPlans(int subId, @NonNull SubscriptionPlan[] plans,
             @NonNull String callingPackage) {
@@ -406,6 +406,7 @@
      *
      * @param subId the subscriber to get the subscription plans for.
      * @param callingPackage the name of the package making the call.
+     * @hide
      */
     @NonNull
     public SubscriptionPlan[] getSubscriptionPlans(int subId, @NonNull String callingPackage) {
@@ -549,7 +550,6 @@
     }
 
     /** @hide */
-    @SystemApi
     public static class SubscriptionCallback {
         /**
          * Notify clients of a new override about a given subscription.
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index 2c0e4aa7..75086cf 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -33,8 +34,8 @@
  * {@link NetworkAgent}s. The networks can then provide connectivity to apps and can be interacted
  * with via networking APIs such as {@link ConnectivityManager}.
  *
- * Subclasses should implement {@link #onNetworkRequested} and {@link #onRequestWithdrawn} to
- * receive {@link NetworkRequest}s sent by the system and by apps. A network that is not the
+ * Subclasses should implement {@link #onNetworkRequested} and {@link #onNetworkRequestWithdrawn}
+ * to receive {@link NetworkRequest}s sent by the system and by apps. A network that is not the
  * best (highest-scoring) network for any request is generally not used by the system, and torn
  * down.
  *
@@ -77,7 +78,7 @@
      * Constructs a new NetworkProvider.
      *
      * @param looper the Looper on which to run {@link #onNetworkRequested} and
-     *               {@link #onRequestWithdrawn}.
+     *               {@link #onNetworkRequestWithdrawn}.
      * @param name the name of the listener, used only for debugging.
      *
      * @hide
@@ -94,7 +95,7 @@
                         onNetworkRequested((NetworkRequest) m.obj, m.arg1, m.arg2);
                         break;
                     case CMD_CANCEL_REQUEST:
-                        onRequestWithdrawn((NetworkRequest) m.obj);
+                        onNetworkRequestWithdrawn((NetworkRequest) m.obj);
                         break;
                     default:
                         Log.e(mName, "Unhandled message: " + m.what);
@@ -106,10 +107,12 @@
     }
 
     // TODO: consider adding a register() method so ConnectivityManager does not need to call this.
+    /** @hide */
     public @Nullable Messenger getMessenger() {
         return mMessenger;
     }
 
+    /** @hide */
     public @NonNull String getName() {
         return mName;
     }
@@ -140,14 +143,15 @@
      *  @hide
      */
     @SystemApi
-    public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {}
+    public void onNetworkRequested(@NonNull NetworkRequest request,
+            @IntRange(from = 0, to = 99) int score, int providerId) {}
 
     /**
      *  Called when a NetworkRequest is withdrawn.
      *  @hide
      */
     @SystemApi
-    public void onRequestWithdrawn(@NonNull NetworkRequest request) {}
+    public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {}
 
     /**
      * Asserts that no provider will ever be able to satisfy the specified request. The provider
@@ -155,7 +159,7 @@
      * satisfying this request, and that the request cannot be satisfied. The application filing the
      * request will receive an {@link NetworkCallback#onUnavailable()} callback.
      *
-     * @param request the request that cannot be fulfilled
+     * @param request the request that permanently cannot be fulfilled
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index b0bf64e..b1e9c35 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -473,10 +473,8 @@
      *
      * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not
      *           satisfy any request.
-     * @hide
      */
-    @SystemApi
-    public boolean satisfiedBy(@Nullable NetworkCapabilities nc) {
+    public boolean canBeSatisfiedBy(@Nullable NetworkCapabilities nc) {
         return networkCapabilities.satisfiedByNetworkCapabilities(nc);
     }
 
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index c233ec0..6539e05 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -24,6 +24,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
@@ -468,11 +469,13 @@
          *
          * @param networks List of {@link ScoredNetwork} containing updated scores.
          */
+        @SuppressLint("CallbackMethodName")
         void updateScores(@NonNull List<ScoredNetwork> networks);
 
         /**
          * Invokes when all the previously provided scores are no longer valid.
          */
+        @SuppressLint("CallbackMethodName")
         void clearScores();
     }
 
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
index 2dd0c4e..160259e 100644
--- a/core/java/android/net/NetworkSpecifier.java
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -35,7 +35,9 @@
      * @hide
      */
     @SystemApi
-    public abstract boolean satisfiedBy(@Nullable NetworkSpecifier other);
+    public boolean canBeSatisfiedBy(@Nullable NetworkSpecifier other) {
+        return false;
+    }
 
     /**
      * Optional method which can be overridden by concrete implementations of NetworkSpecifier to
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index a46c410..86f3dfd 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -19,15 +19,17 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.content.Context;
+import android.os.IBinder;
+import android.os.ServiceManager;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 /**
- *
- * Constants for client code communicating with the network stack service.
+ * Constants and utilities for client code communicating with the network stack service.
  * @hide
  */
 @SystemApi
@@ -43,6 +45,34 @@
     public static final String PERMISSION_MAINLINE_NETWORK_STACK =
             "android.permission.MAINLINE_NETWORK_STACK";
 
+    @Nullable
+    private static volatile IBinder sMockService;
+
+    /**
+     * Get an {@link IBinder} representing the NetworkStack stable AIDL Interface, if registered.
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    @TestApi
+    public static IBinder getService() {
+        final IBinder mockService = sMockService;
+        if (mockService != null) return mockService;
+        return ServiceManager.getService(Context.NETWORK_STACK_SERVICE);
+    }
+
+    /**
+     * Set a mock service for testing, to be returned by future calls to {@link #getService()}.
+     *
+     * <p>Passing a {@code null} {@code mockService} resets {@link #getService()} to normal
+     * behavior.
+     * @hide
+     */
+    @TestApi
+    public static void setServiceForTest(@Nullable IBinder mockService) {
+        sMockService = mockService;
+    }
+
     private NetworkStack() {}
 
     /**
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 2f536ff..b7fb280 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -21,7 +21,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
@@ -58,9 +57,12 @@
 public final class NetworkStats implements Parcelable {
     private static final String TAG = "NetworkStats";
 
-    /** {@link #iface} value when interface details unavailable. */
-    @SuppressLint("CompileTimeConstant")
+    /**
+     * {@link #iface} value when interface details unavailable.
+     * @hide
+     */
     @Nullable public static final String IFACE_ALL = null;
+
     /**
      * Virtual network interface for video telephony. This is for VT data usage counting
      * purpose.
@@ -248,7 +250,13 @@
     @UnsupportedAppUsage
     private long[] operations;
 
-    /** @hide */
+    /**
+     * Basic element of network statistics. Contains the number of packets and number of bytes
+     * transferred on both directions in a given set of conditions. See
+     * {@link Entry#Entry(String, int, int, int, int, int, int, long, long, long, long, long)}.
+     *
+     * @hide
+     */
     @SystemApi
     public static class Entry {
         /** @hide */
@@ -319,6 +327,35 @@
                     rxBytes, rxPackets, txBytes, txPackets, operations);
         }
 
+        /**
+         * Construct a {@link Entry} object by giving statistics of packet and byte transferred on
+         * both direction, and associated with a set of given conditions.
+         *
+         * @param iface interface name of this {@link Entry}. Or null if not specified.
+         * @param uid uid of this {@link Entry}. {@link #UID_TETHERING} if this {@link Entry} is
+         *            for tethering. Or {@link #UID_ALL} if this {@link NetworkStats} is only
+         *            counting iface stats.
+         * @param set usage state of this {@link Entry}. Should be one of the following
+         *            values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}.
+         * @param tag tag of this {@link Entry}.
+         * @param metered metered state of this {@link Entry}. Should be one of the following
+         *                values: {link #METERED_YES}, {link #METERED_NO}.
+         * @param roaming roaming state of this {@link Entry}. Should be one of the following
+         *                values: {link #ROAMING_YES}, {link #ROAMING_NO}.
+         * @param defaultNetwork default network status of this {@link Entry}. Should be one
+         *                       of the following values: {link #DEFAULT_NETWORK_YES},
+         *                       {link #DEFAULT_NETWORK_NO}.
+         * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should
+         *                represent the contents of IP packets, including IP headers.
+         * @param rxPackets Number of packets received for this {@link Entry}. Statistics should
+         *                  represent the contents of IP packets, including IP headers.
+         * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should
+         *                represent the contents of IP packets, including IP headers.
+         * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should
+         *                  represent the contents of IP packets, including IP headers.
+         * @param operations count of network operations performed for this {@link Entry}. This can
+         *                   be used to derive bytes-per-operation.
+         */
         public Entry(@Nullable String iface, int uid, @State int set, int tag,
                 @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork,
                 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
@@ -466,7 +503,7 @@
         NetworkStats.Entry entry = null;
         for (int i = 0; i < size; i++) {
             entry = getValues(i, entry);
-            clone.addEntry(entry);
+            clone.insertEntry(entry);
         }
         return clone;
     }
@@ -493,26 +530,26 @@
 
     /** @hide */
     @VisibleForTesting
-    public NetworkStats addIfaceValues(
+    public NetworkStats insertEntry(
             String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
-        return addEntry(
+        return insertEntry(
                 iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
     }
 
     /** @hide */
     @VisibleForTesting
-    public NetworkStats addEntry(String iface, int uid, int set, int tag, long rxBytes,
+    public NetworkStats insertEntry(String iface, int uid, int set, int tag, long rxBytes,
             long rxPackets, long txBytes, long txPackets, long operations) {
-        return addEntry(new Entry(
+        return insertEntry(new Entry(
                 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
     /** @hide */
     @VisibleForTesting
-    public NetworkStats addEntry(String iface, int uid, int set, int tag, int metered, int roaming,
-            int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
-            long operations) {
-        return addEntry(new Entry(
+    public NetworkStats insertEntry(String iface, int uid, int set, int tag, int metered,
+            int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes,
+            long txPackets, long operations) {
+        return insertEntry(new Entry(
                 iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets,
                 txBytes, txPackets, operations));
     }
@@ -522,7 +559,7 @@
      * object can be recycled across multiple calls.
      * @hide
      */
-    public NetworkStats addEntry(Entry entry) {
+    public NetworkStats insertEntry(Entry entry) {
         if (size >= capacity) {
             final int newLength = Math.max(size, 10) * 3 / 2;
             iface = Arrays.copyOf(iface, newLength);
@@ -665,7 +702,7 @@
                 entry.roaming, entry.defaultNetwork);
         if (i == -1) {
             // only create new entry when positive contribution
-            addEntry(entry);
+            insertEntry(entry);
         } else {
             rxBytes[i] += entry.rxBytes;
             rxPackets[i] += entry.rxPackets;
@@ -684,7 +721,7 @@
      * @param entry the {@link Entry} to add.
      * @return a new constructed {@link NetworkStats} object that contains the result.
      */
-    public @NonNull NetworkStats addValues(@NonNull Entry entry) {
+    public @NonNull NetworkStats addEntry(@NonNull Entry entry) {
         return this.clone().combineValues(entry);
     }
 
@@ -1003,7 +1040,7 @@
                 entry.operations = Math.max(entry.operations, 0);
             }
 
-            result.addEntry(entry);
+            result.insertEntry(entry);
         }
 
         return result;
@@ -1158,18 +1195,24 @@
 
     /**
      * Remove all rows that match one of specified UIDs.
+     * This mutates the original structure in place.
      * @hide
      */
     public void removeUids(int[] uids) {
-        int nextOutputEntry = 0;
-        for (int i = 0; i < size; i++) {
-            if (!ArrayUtils.contains(uids, uid[i])) {
-                maybeCopyEntry(nextOutputEntry, i);
-                nextOutputEntry++;
-            }
-        }
+        filter(e -> !ArrayUtils.contains(uids, e.uid));
+    }
 
-        size = nextOutputEntry;
+    /**
+     * Remove all rows that match one of specified UIDs.
+     * @return the result object.
+     * @hide
+     */
+    @NonNull
+    public NetworkStats removeEmptyEntries() {
+        final NetworkStats ret = this.clone();
+        ret.filter(e -> e.rxBytes != 0 || e.rxPackets != 0 || e.txBytes != 0 || e.txPackets != 0
+                || e.operations != 0);
+        return ret;
     }
 
     /**
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 5498f74..cb9463a 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -34,9 +34,13 @@
 import static android.net.NetworkStats.ROAMING_YES;
 import static android.net.wifi.WifiInfo.sanitizeSsid;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.BackupUtils;
 import android.util.Log;
 
@@ -73,6 +77,14 @@
     public static final int MATCH_BLUETOOTH = 8;
     public static final int MATCH_PROXY = 9;
 
+    /**
+     * Include all network types when filtering. This is meant to merge in with the
+     * {@code TelephonyManager.NETWORK_TYPE_*} constants, and thus needs to stay in sync.
+     *
+     * @hide
+     */
+    public static final int NETWORK_TYPE_ALL = -1;
+
     private static boolean isKnownMatchRule(final int rule) {
         switch (rule) {
             case MATCH_MOBILE:
@@ -117,7 +129,22 @@
     }
 
     /**
-     * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks,
+     * Template to match cellular networks with the given IMSI and {@code ratType}.
+     * Use {@link #NETWORK_TYPE_ALL} to include all network types when filtering.
+     * See {@code TelephonyManager.NETWORK_TYPE_*}.
+     */
+    public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
+            @NetworkType int ratType) {
+        if (TextUtils.isEmpty(subscriberId)) {
+            return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
+                    METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType);
+        }
+        return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
+                METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType);
+    }
+
+    /**
+     * Template to match metered {@link ConnectivityManager#TYPE_MOBILE} networks,
      * regardless of IMSI.
      */
     @UnsupportedAppUsage
@@ -126,7 +153,7 @@
     }
 
     /**
-     * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks,
+     * Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks,
      * regardless of SSID.
      */
     @UnsupportedAppUsage
@@ -192,6 +219,7 @@
     private final int mMetered;
     private final int mRoaming;
     private final int mDefaultNetwork;
+    private final int mSubType;
 
     @UnsupportedAppUsage
     public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
@@ -201,11 +229,11 @@
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
             String networkId) {
         this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
-                DEFAULT_NETWORK_ALL);
+                DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL);
     }
 
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
-            String networkId, int metered, int roaming, int defaultNetwork) {
+            String networkId, int metered, int roaming, int defaultNetwork, int subType) {
         mMatchRule = matchRule;
         mSubscriberId = subscriberId;
         mMatchSubscriberIds = matchSubscriberIds;
@@ -213,6 +241,7 @@
         mMetered = metered;
         mRoaming = roaming;
         mDefaultNetwork = defaultNetwork;
+        mSubType = subType;
 
         if (!isKnownMatchRule(matchRule)) {
             Log.e(TAG, "Unknown network template rule " + matchRule
@@ -228,6 +257,7 @@
         mMetered = in.readInt();
         mRoaming = in.readInt();
         mDefaultNetwork = in.readInt();
+        mSubType = in.readInt();
     }
 
     @Override
@@ -239,6 +269,7 @@
         dest.writeInt(mMetered);
         dest.writeInt(mRoaming);
         dest.writeInt(mDefaultNetwork);
+        dest.writeInt(mSubType);
     }
 
     @Override
@@ -271,13 +302,16 @@
             builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString(
                     mDefaultNetwork));
         }
+        if (mSubType != NETWORK_TYPE_ALL) {
+            builder.append(", subType=").append(mSubType);
+        }
         return builder.toString();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
-                mDefaultNetwork);
+                mDefaultNetwork, mSubType);
     }
 
     @Override
@@ -289,7 +323,8 @@
                     && Objects.equals(mNetworkId, other.mNetworkId)
                     && mMetered == other.mMetered
                     && mRoaming == other.mRoaming
-                    && mDefaultNetwork == other.mDefaultNetwork;
+                    && mDefaultNetwork == other.mDefaultNetwork
+                    && mSubType == other.mSubType;
         }
         return false;
     }
@@ -376,6 +411,11 @@
             || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork);
     }
 
+    private boolean matchesCollapsedRatType(NetworkIdentity ident) {
+        return mSubType == NETWORK_TYPE_ALL
+                || getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType);
+    }
+
     public boolean matchesSubscriberId(String subscriberId) {
         return ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
     }
@@ -388,9 +428,52 @@
             // TODO: consider matching against WiMAX subscriber identity
             return true;
         } else {
+            // Only metered mobile network would be matched regardless of metered filter.
+            // This is used to exclude non-metered APNs, e.g. IMS. See ag/908650.
+            // TODO: Respect metered filter and remove mMetered condition.
             return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered))
                     && !ArrayUtils.isEmpty(mMatchSubscriberIds)
-                    && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
+                    && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
+                    && matchesCollapsedRatType(ident);
+        }
+    }
+
+    /**
+     * Get a Radio Access Technology(RAT) type that is representative of a group of RAT types.
+     * The mapping is corresponding to {@code TelephonyManager#NETWORK_CLASS_BIT_MASK_*}.
+     *
+     * @param ratType An integer defined in {@code TelephonyManager#NETWORK_TYPE_*}.
+     */
+    // TODO: 1. Consider move this to TelephonyManager if used by other modules.
+    //       2. Consider make this configurable.
+    //       3. Use TelephonyManager APIs when available.
+    public static int getCollapsedRatType(int ratType) {
+        switch (ratType) {
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+            case TelephonyManager.NETWORK_TYPE_GSM:
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+            case TelephonyManager.NETWORK_TYPE_IDEN:
+            case TelephonyManager.NETWORK_TYPE_CDMA:
+            case TelephonyManager.NETWORK_TYPE_1xRTT:
+                return TelephonyManager.NETWORK_TYPE_GSM;
+            case TelephonyManager.NETWORK_TYPE_EVDO_0:
+            case TelephonyManager.NETWORK_TYPE_EVDO_A:
+            case TelephonyManager.NETWORK_TYPE_EVDO_B:
+            case TelephonyManager.NETWORK_TYPE_EHRPD:
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+                return TelephonyManager.NETWORK_TYPE_UMTS;
+            case TelephonyManager.NETWORK_TYPE_LTE:
+            case TelephonyManager.NETWORK_TYPE_IWLAN:
+                return TelephonyManager.NETWORK_TYPE_LTE;
+            case TelephonyManager.NETWORK_TYPE_NR:
+                return TelephonyManager.NETWORK_TYPE_NR;
+            default:
+                return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
     }
 
@@ -421,7 +504,8 @@
         if (ident.mType == TYPE_WIMAX) {
             return true;
         } else {
-            return sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered);
+            return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered))
+                    && matchesCollapsedRatType(ident);
         }
     }
 
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 08cc4e2..779f7bc 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -31,7 +31,6 @@
 import java.io.FileDescriptor;
 import java.math.BigInteger;
 import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.SocketException;
 import java.net.UnknownHostException;
@@ -313,15 +312,6 @@
     }
 
     /**
-     * Check if IP address type is consistent between two InetAddress.
-     * @return true if both are the same type.  False otherwise.
-     */
-    public static boolean addressTypeMatches(InetAddress left, InetAddress right) {
-        return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) ||
-                ((left instanceof Inet6Address) && (right instanceof Inet6Address)));
-    }
-
-    /**
      * Convert a 32 char hex string into a Inet6Address.
      * throws a runtime exception if the string isn't 32 chars, isn't hex or can't be
      * made into an Inet6Address
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index 767b693..5e2a718 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -8,4 +8,4 @@
 reminv@google.com
 satk@google.com
 
-per-file SSL*, Uri*, Url* = prb@google.com, dauletz@google.com, narayan@google.com, tobiast@google.com
+per-file SSL*, Uri*, Url* = prb@google.com, dauletz@google.com, narayan@google.com, ngeoffray@google.com
diff --git a/core/java/android/net/PlatformVpnProfile.java b/core/java/android/net/PlatformVpnProfile.java
index fbae637..445ec91 100644
--- a/core/java/android/net/PlatformVpnProfile.java
+++ b/core/java/android/net/PlatformVpnProfile.java
@@ -60,6 +60,9 @@
     public static final int TYPE_IKEV2_IPSEC_RSA = VpnProfile.TYPE_IKEV2_IPSEC_RSA;
 
     /** @hide */
+    public static final int MAX_MTU_DEFAULT = 1360;
+
+    /** @hide */
     @PlatformVpnType protected final int mType;
 
     /** @hide */
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 67bad53..e550f85 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -22,9 +22,11 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.util.NetUtils;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Pair;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -424,6 +426,16 @@
     }
 
     /**
+     * Indicates if this route is an unreachable default route.
+     *
+     * @return {@code true} if it's an unreachable route with prefix length of 0.
+     * @hide
+     */
+    private boolean isUnreachableDefaultRoute() {
+        return mType == RTN_UNREACHABLE && mDestination.getPrefixLength() == 0;
+    }
+
+    /**
      * Indicates if this route is an IPv4 default route.
      * @hide
      */
@@ -432,6 +444,14 @@
     }
 
     /**
+     * Indicates if this route is an IPv4 unreachable default route.
+     * @hide
+     */
+    public boolean isIPv4UnreachableDefault() {
+        return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet4Address;
+    }
+
+    /**
      * Indicates if this route is an IPv6 default route.
      * @hide
      */
@@ -440,6 +460,14 @@
     }
 
     /**
+     * Indicates if this route is an IPv6 unreachable default route.
+     * @hide
+     */
+    public boolean isIPv6UnreachableDefault() {
+        return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet6Address;
+    }
+
+    /**
      * Indicates if this route is a host route (ie, matches only a single host address).
      *
      * @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6,
@@ -483,21 +511,7 @@
     @UnsupportedAppUsage
     @Nullable
     public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
-        if ((routes == null) || (dest == null)) return null;
-
-        RouteInfo bestRoute = null;
-        // pick a longest prefix match under same address type
-        for (RouteInfo route : routes) {
-            if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
-                if ((bestRoute != null) &&
-                        (bestRoute.mDestination.getPrefixLength() >=
-                        route.mDestination.getPrefixLength())) {
-                    continue;
-                }
-                if (route.matches(dest)) bestRoute = route;
-            }
-        }
-        return bestRoute;
+        return NetUtils.selectBestRoute(routes, dest);
     }
 
     /**
@@ -540,6 +554,30 @@
     }
 
     /**
+     * A helper class that contains the destination and the gateway in a {@code RouteInfo},
+     * used by {@link ConnectivityService#updateRoutes} or
+     * {@link LinkProperties#addRoute} to calculate the list to be updated.
+     *
+     * @hide
+     */
+    public static class RouteKey extends Pair<IpPrefix, InetAddress> {
+        RouteKey(@NonNull IpPrefix destination, @Nullable InetAddress gateway) {
+            super(destination, gateway);
+        }
+    }
+
+    /**
+     * Get {@code RouteKey} of this {@code RouteInfo}.
+     * @return a {@code RouteKey} object.
+     *
+     * @hide
+     */
+    @NonNull
+    public RouteKey getRouteKey() {
+        return new RouteKey(mDestination, mGateway);
+    }
+
+    /**
      *  Returns a hashcode for this <code>RouteInfo</code> object.
      */
     public int hashCode() {
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index fc9a8f6..46aef10 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -109,6 +109,17 @@
     })
     public @interface ErrorCode {}
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            SUCCESS,
+            ERROR_INVALID_LENGTH,
+            ERROR_UNSUPPORTED,
+            ERROR_INSUFFICIENT_RESOURCES,
+            ERROR_HARDWARE_UNSUPPORTED
+    })
+    public @interface KeepaliveEvent {}
+
     /**
      * The minimum interval in seconds between keepalive packet transmissions.
      *
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
index 6ae5971..3f2aa17 100644
--- a/core/java/android/net/StringNetworkSpecifier.java
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -40,7 +40,7 @@
 
     /** @hide */
     @Override
-    public boolean satisfiedBy(NetworkSpecifier other) {
+    public boolean canBeSatisfiedBy(NetworkSpecifier other) {
         return equals(other);
     }
 
diff --git a/core/java/android/net/TelephonyNetworkSpecifier.java b/core/java/android/net/TelephonyNetworkSpecifier.java
index 726f770..aafebd7 100644
--- a/core/java/android/net/TelephonyNetworkSpecifier.java
+++ b/core/java/android/net/TelephonyNetworkSpecifier.java
@@ -97,7 +97,7 @@
 
     /** @hide */
     @Override
-    public boolean satisfiedBy(NetworkSpecifier other) {
+    public boolean canBeSatisfiedBy(NetworkSpecifier other) {
         // Any generic requests should be satisfied by a specific telephony network.
         // For simplicity, we treat null same as MatchAllNetworkSpecifier
         return equals(other) || other == null || other instanceof MatchAllNetworkSpecifier;
diff --git a/core/java/android/net/TestNetworkManager.java b/core/java/android/net/TestNetworkManager.java
index 4ac4a69..a0a563b 100644
--- a/core/java/android/net/TestNetworkManager.java
+++ b/core/java/android/net/TestNetworkManager.java
@@ -16,6 +16,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -29,6 +30,18 @@
  */
 @TestApi
 public class TestNetworkManager {
+    /**
+     * Prefix for tun interfaces created by this class.
+     * @hide
+     */
+    public static final String TEST_TUN_PREFIX = "testtun";
+
+    /**
+     * Prefix for tap interfaces created by this class.
+     * @hide
+     */
+    public static final String TEST_TAP_PREFIX = "testtap";
+
     @NonNull private static final String TAG = TestNetworkManager.class.getSimpleName();
 
     @NonNull private final ITestNetworkManager mService;
@@ -53,6 +66,19 @@
         }
     }
 
+    private void setupTestNetwork(
+            @NonNull String iface,
+            @Nullable LinkProperties lp,
+            boolean isMetered,
+            @NonNull int[] administratorUids,
+            @NonNull IBinder binder) {
+        try {
+            mService.setupTestNetwork(iface, lp, isMetered, administratorUids, binder);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Sets up a capability-limited, testing-only network for a given interface
      *
@@ -66,11 +92,7 @@
     public void setupTestNetwork(
             @NonNull LinkProperties lp, boolean isMetered, @NonNull IBinder binder) {
         Preconditions.checkNotNull(lp, "Invalid LinkProperties");
-        try {
-            mService.setupTestNetwork(lp.getInterfaceName(), lp, isMetered, binder);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        setupTestNetwork(lp.getInterfaceName(), lp, isMetered, new int[0], binder);
     }
 
     /**
@@ -82,11 +104,21 @@
      */
     @TestApi
     public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
-        try {
-            mService.setupTestNetwork(iface, null, true, binder);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        setupTestNetwork(iface, null, true, new int[0], binder);
+    }
+
+    /**
+     * Sets up a capability-limited, testing-only network for a given interface with the given
+     * administrator UIDs.
+     *
+     * @param iface the name of the interface to be used for the Network LinkProperties.
+     * @param administratorUids The administrator UIDs to be used for the test-only network
+     * @param binder A binder object guarding the lifecycle of this test network.
+     * @hide
+     */
+    public void setupTestNetwork(
+            @NonNull String iface, @NonNull int[] administratorUids, @NonNull IBinder binder) {
+        setupTestNetwork(iface, null, true, administratorUids, binder);
     }
 
     /**
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 2041cfb..c87b827 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -75,7 +75,7 @@
     }
 
     /**
-     * Create an instance of the VpnManger with the given context.
+     * Create an instance of the VpnManager with the given context.
      *
      * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
      * {@link Context.getSystemService()} method call.
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 63e5107..9c2c5b8 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -836,7 +836,7 @@
          * @param isMetered {@code true} if VPN network should be treated as metered regardless of
          *     underlying network meteredness
          * @return this {@link Builder} object to facilitate chaining method calls
-         * @see #setUnderlyingNetworks(Networks[])
+         * @see #setUnderlyingNetworks(Network[])
          * @see ConnectivityManager#isActiveNetworkMetered()
          */
         @NonNull
diff --git a/core/java/android/net/http/OWNERS b/core/java/android/net/http/OWNERS
index 3092612..3271d24 100644
--- a/core/java/android/net/http/OWNERS
+++ b/core/java/android/net/http/OWNERS
@@ -1,4 +1,4 @@
 narayan@google.com
-tobiast@google.com
+ngeoffray@google.com
 include platform/libcore:/OWNERS
 include platform/external/conscrypt:/OWNERS
diff --git a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java b/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
deleted file mode 100644
index 740aa92..0000000
--- a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.net.netstats.provider;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.net.NetworkStats;
-
-/**
- * A base class that allows external modules to implement a custom network statistics provider.
- * @hide
- */
-@SystemApi
-public abstract class AbstractNetworkStatsProvider {
-    /**
-     * A value used by {@link #setLimit} and {@link #setAlert} indicates there is no limit.
-     */
-    public static final int QUOTA_UNLIMITED = -1;
-
-    /**
-     * Called by {@code NetworkStatsService} when global polling is needed. Custom
-     * implementation of providers MUST respond to it by calling
-     * {@link NetworkStatsProviderCallback#onStatsUpdated} within one minute. Responding
-     * later than this may cause the stats to be dropped.
-     *
-     * @param token a positive number identifying the new state of the system under which
-     *              {@link NetworkStats} have to be gathered from now on. When this is called,
-     *              custom implementations of providers MUST report the latest stats with the
-     *              previous token, under which stats were being gathered so far.
-     */
-    public abstract void requestStatsUpdate(int token);
-
-    /**
-     * Called by {@code NetworkStatsService} when setting the interface quota for the specified
-     * upstream interface. When this is called, the custom implementation should block all egress
-     * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
-     * been reached, and MUST respond to it by calling
-     * {@link NetworkStatsProviderCallback#onLimitReached()}.
-     *
-     * @param iface the interface requiring the operation.
-     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
-     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
-     */
-    public abstract void setLimit(@NonNull String iface, long quotaBytes);
-
-    /**
-     * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
-     * MUST call {@link NetworkStatsProviderCallback#onAlertReached()} when {@code quotaBytes} bytes
-     * have been reached. Unlike {@link #setLimit(String, long)}, the custom implementation should
-     * not block all egress packets.
-     *
-     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
-     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
-     */
-    public abstract void setAlert(long quotaBytes);
-}
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
index 55b3d4e..4078b24 100644
--- a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
+++ b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
@@ -22,7 +22,7 @@
  * @hide
  */
 oneway interface INetworkStatsProvider {
-    void requestStatsUpdate(int token);
-    void setLimit(String iface, long quotaBytes);
-    void setAlert(long quotaBytes);
+    void onRequestStatsUpdate(int token);
+    void onSetLimit(String iface, long quotaBytes);
+    void onSetAlert(long quotaBytes);
 }
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
index 3ea9318..bd336dd 100644
--- a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
+++ b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
@@ -24,8 +24,8 @@
  * @hide
  */
 oneway interface INetworkStatsProviderCallback {
-    void onStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
-    void onAlertReached();
-    void onLimitReached();
+    void notifyStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
+    void notifyAlertReached();
+    void notifyLimitReached();
     void unregister();
 }
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProvider.java b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
new file mode 100644
index 0000000..7639d22
--- /dev/null
+++ b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
@@ -0,0 +1,195 @@
+/*
+ * 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.net.netstats.provider;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.NetworkStats;
+import android.os.RemoteException;
+
+/**
+ * A base class that allows external modules to implement a custom network statistics provider.
+ * @hide
+ */
+@SystemApi
+public abstract class NetworkStatsProvider {
+    /**
+     * A value used by {@link #onSetLimit} and {@link #onSetAlert} indicates there is no limit.
+     */
+    public static final int QUOTA_UNLIMITED = -1;
+
+    @NonNull private final INetworkStatsProvider mProviderBinder =
+            new INetworkStatsProvider.Stub() {
+
+        @Override
+        public void onRequestStatsUpdate(int token) {
+            NetworkStatsProvider.this.onRequestStatsUpdate(token);
+        }
+
+        @Override
+        public void onSetLimit(String iface, long quotaBytes) {
+            NetworkStatsProvider.this.onSetLimit(iface, quotaBytes);
+        }
+
+        @Override
+        public void onSetAlert(long quotaBytes) {
+            NetworkStatsProvider.this.onSetAlert(quotaBytes);
+        }
+    };
+
+    // The binder given by the service when successfully registering. Only null before registering,
+    // never null once non-null.
+    @Nullable
+    private INetworkStatsProviderCallback mProviderCbBinder;
+
+    /**
+     * Return the binder invoked by the service and redirect function calls to the overridden
+     * methods.
+     * @hide
+     */
+    @NonNull
+    public INetworkStatsProvider getProviderBinder() {
+        return mProviderBinder;
+    }
+
+    /**
+     * Store the binder that was returned by the service when successfully registering. Note that
+     * the provider cannot be re-registered. Hence this method can only be called once per provider.
+     *
+     * @hide
+     */
+    public void setProviderCallbackBinder(@NonNull INetworkStatsProviderCallback binder) {
+        if (mProviderCbBinder != null) {
+            throw new IllegalArgumentException("provider is already registered");
+        }
+        mProviderCbBinder = binder;
+    }
+
+    /**
+     * Get the binder that was returned by the service when successfully registering. Or null if the
+     * provider was never registered.
+     *
+     * @hide
+     */
+    @Nullable
+    public INetworkStatsProviderCallback getProviderCallbackBinder() {
+        return mProviderCbBinder;
+    }
+
+    /**
+     * Get the binder that was returned by the service when successfully registering. Throw an
+     * {@link IllegalStateException} if the provider is not registered.
+     *
+     * @hide
+     */
+    @NonNull
+    public INetworkStatsProviderCallback getProviderCallbackBinderOrThrow() {
+        if (mProviderCbBinder == null) {
+            throw new IllegalStateException("the provider is not registered");
+        }
+        return mProviderCbBinder;
+    }
+
+    /**
+     * Notify the system of new network statistics.
+     *
+     * Send the network statistics recorded since the last call to {@link #notifyStatsUpdated}. Must
+     * be called as soon as possible after {@link NetworkStatsProvider#onRequestStatsUpdate(int)}
+     * being called. Responding later increases the probability stats will be dropped. The
+     * provider can also call this whenever it wants to reports new stats for any reason.
+     * Note that the system will not necessarily immediately propagate the statistics to
+     * reflect the update.
+     *
+     * @param token the token under which these stats were gathered. Providers can call this method
+     *              with the current token as often as they want, until the token changes.
+     *              {@see NetworkStatsProvider#onRequestStatsUpdate()}
+     * @param ifaceStats the {@link NetworkStats} per interface to be reported.
+     *                   The provider should not include any traffic that is already counted by
+     *                   kernel interface counters.
+     * @param uidStats the same stats as above, but counts {@link NetworkStats}
+     *                 per uid.
+     */
+    public void notifyStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
+            @NonNull NetworkStats uidStats) {
+        try {
+            getProviderCallbackBinderOrThrow().notifyStatsUpdated(token, ifaceStats, uidStats);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Notify system that the quota set by {@code onSetAlert} has been reached.
+     */
+    public void notifyAlertReached() {
+        try {
+            getProviderCallbackBinderOrThrow().notifyAlertReached();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Notify system that the quota set by {@code onSetLimit} has been reached.
+     */
+    public void notifyLimitReached() {
+        try {
+            getProviderCallbackBinderOrThrow().notifyLimitReached();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Called by {@code NetworkStatsService} when it requires to know updated stats.
+     * The provider MUST respond by calling {@link #notifyStatsUpdated} as soon as possible.
+     * Responding later increases the probability stats will be dropped. Memory allowing, the
+     * system will try to take stats into account up to one minute after calling
+     * {@link #onRequestStatsUpdate}.
+     *
+     * @param token a positive number identifying the new state of the system under which
+     *              {@link NetworkStats} have to be gathered from now on. When this is called,
+     *              custom implementations of providers MUST tally and report the latest stats with
+     *              the previous token, under which stats were being gathered so far.
+     */
+    public abstract void onRequestStatsUpdate(int token);
+
+    /**
+     * Called by {@code NetworkStatsService} when setting the interface quota for the specified
+     * upstream interface. When this is called, the custom implementation should block all egress
+     * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
+     * been reached, and MUST respond to it by calling
+     * {@link NetworkStatsProvider#notifyLimitReached()}.
+     *
+     * @param iface the interface requiring the operation.
+     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
+     */
+    public abstract void onSetLimit(@NonNull String iface, long quotaBytes);
+
+    /**
+     * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
+     * MUST call {@link NetworkStatsProvider#notifyAlertReached()} when {@code quotaBytes} bytes
+     * have been reached. Unlike {@link #onSetLimit(String, long)}, the custom implementation should
+     * not block all egress packets.
+     *
+     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
+     */
+    public abstract void onSetAlert(long quotaBytes);
+}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java b/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
deleted file mode 100644
index e17a8ee..0000000
--- a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.net.netstats.provider;
-
-import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.net.NetworkStats;
-import android.os.RemoteException;
-
-/**
- * A callback class that allows callers to report events to the system.
- * @hide
- */
-@SystemApi
-@SuppressLint("CallbackMethodName")
-public class NetworkStatsProviderCallback {
-    @NonNull private final INetworkStatsProviderCallback mBinder;
-
-    /** @hide */
-    public NetworkStatsProviderCallback(@NonNull INetworkStatsProviderCallback binder) {
-        mBinder = binder;
-    }
-
-    /**
-     * Notify the system of new network statistics.
-     *
-     * Send the network statistics recorded since the last call to {@link #onStatsUpdated}. Must be
-     * called within one minute of {@link AbstractNetworkStatsProvider#requestStatsUpdate(int)}
-     * being called. The provider can also call this whenever it wants to reports new stats for any
-     * reason. Note that the system will not necessarily immediately propagate the statistics to
-     * reflect the update.
-     *
-     * @param token the token under which these stats were gathered. Providers can call this method
-     *              with the current token as often as they want, until the token changes.
-     *              {@see AbstractNetworkStatsProvider#requestStatsUpdate()}
-     * @param ifaceStats the {@link NetworkStats} per interface to be reported.
-     *                   The provider should not include any traffic that is already counted by
-     *                   kernel interface counters.
-     * @param uidStats the same stats as above, but counts {@link NetworkStats}
-     *                 per uid.
-     */
-    public void onStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
-            @NonNull NetworkStats uidStats) {
-        try {
-            mBinder.onStatsUpdated(token, ifaceStats, uidStats);
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
-     * Notify system that the quota set by {@code setAlert} has been reached.
-     */
-    public void onAlertReached() {
-        try {
-            mBinder.onAlertReached();
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
-     * Notify system that the quota set by {@code setLimit} has been reached.
-     */
-    public void onLimitReached() {
-        try {
-            mBinder.onLimitReached();
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
-     * Unregister the provider and the referencing callback.
-     */
-    public void unregister() {
-        try {
-            mBinder.unregister();
-        } catch (RemoteException e) {
-            // Ignore error.
-        }
-    }
-}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java b/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
deleted file mode 100644
index 4bf7c9b..0000000
--- a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.net.netstats.provider;
-
-import android.annotation.NonNull;
-
-/**
- * A wrapper class of {@link INetworkStatsProvider} that hides the binder interface from exposing
- * to outer world.
- *
- * @hide
- */
-public class NetworkStatsProviderWrapper extends INetworkStatsProvider.Stub {
-    @NonNull final AbstractNetworkStatsProvider mProvider;
-
-    public NetworkStatsProviderWrapper(AbstractNetworkStatsProvider provider) {
-        mProvider = provider;
-    }
-
-    @Override
-    public void requestStatsUpdate(int token) {
-        mProvider.requestStatsUpdate(token);
-    }
-
-    @Override
-    public void setLimit(@NonNull String iface, long quotaBytes) {
-        mProvider.setLimit(iface, quotaBytes);
-    }
-
-    @Override
-    public void setAlert(long quotaBytes) {
-        mProvider.setAlert(quotaBytes);
-    }
-}
diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java
index e9ea99f..6967084 100644
--- a/core/java/android/net/util/SocketUtils.java
+++ b/core/java/android/net/util/SocketUtils.java
@@ -66,6 +66,10 @@
 
     /**
      * Make socket address that packet sockets can bind to.
+     *
+     * @param protocol the layer 2 protocol of the packets to receive. One of the {@code ETH_P_*}
+     *                 constants in {@link android.system.OsConstants}.
+     * @param ifIndex the interface index on which packets will be received.
      */
     @NonNull
     public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex) {
@@ -78,6 +82,9 @@
     /**
      * Make a socket address that packet socket can send packets to.
      * @deprecated Use {@link #makePacketSocketAddress(int, int, byte[])} instead.
+     *
+     * @param ifIndex the interface index on which packets will be sent.
+     * @param hwAddr the hardware address to which packets will be sent.
      */
     @Deprecated
     @NonNull
@@ -89,7 +96,12 @@
     }
 
     /**
-     * Make a socket address that packet socket can send packets to.
+     * Make a socket address that a packet socket can send packets to.
+     *
+     * @param protocol the layer 2 protocol of the packets to send. One of the {@code ETH_P_*}
+     *                 constants in {@link android.system.OsConstants}.
+     * @param ifIndex the interface index on which packets will be sent.
+     * @param hwAddr the hardware address to which packets will be sent.
      */
     @NonNull
     public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex,
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index d320f61..c61f10f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -245,13 +245,25 @@
 
     /**
      * Mandatory String extra field in {@link #ACTION_PREFERRED_PAYMENT_CHANGED}
-     * Indicates the condition when trigger this event.
+     * Indicates the condition when trigger this event. Possible values are:
+     * {@link #PREFERRED_PAYMENT_LOADED},
+     * {@link #PREFERRED_PAYMENT_CHANGED},
+     * {@link #PREFERRED_PAYMENT_UPDATED},
      */
     public static final String EXTRA_PREFERRED_PAYMENT_CHANGED_REASON =
             "android.nfc.extra.PREFERRED_PAYMENT_CHANGED_REASON";
-
+    /**
+     * Nfc is enabled and the preferred payment aids are registered.
+     */
     public static final int PREFERRED_PAYMENT_LOADED = 1;
+    /**
+     * User selected another payment application as the preferred payment.
+     */
     public static final int PREFERRED_PAYMENT_CHANGED = 2;
+    /**
+     * Current preferred payment has issued an update (registered/unregistered new aids or has been
+     * updated itself).
+     */
     public static final int PREFERRED_PAYMENT_UPDATED = 3;
 
     public static final int STATE_OFF = 1;
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index f1c74a6..7bf82e8 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -672,7 +672,7 @@
             recoverService();
             if (sService == null) {
                 Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
+                throw e.rethrowFromSystemServer();
             }
             try {
                 ApduServiceInfo serviceInfo =
@@ -680,7 +680,7 @@
                 return (serviceInfo != null ? serviceInfo.getAids() : null);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
+                throw e.rethrowFromSystemServer();
             }
         }
     }
@@ -690,9 +690,16 @@
      *
      * @return The route destination secure element name of the preferred payment service.
      *         HCE payment: "Host"
-     *         OffHost payment: prefix SIM or prefix eSE string.
-     *                          "OffHost" if the payment service does not specify secure element
-     *                          name.
+     *         OffHost payment: 1. String with prefix SIM or prefix eSE string.
+     *                             Ref: GSMA TS.26 - NFC Handset Requirements
+     *                             TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be
+     *                                               SIM[smartcard slot]
+     *                                               (e.g. SIM/SIM1, SIM2… SIMn).
+     *                             TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be
+     *                                               eSE[number]
+     *                                               (e.g. eSE/eSE1, eSE2, etc.).
+     *                          2. "OffHost" if the payment service does not specify secure element
+     *                             name.
      */
     @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
     @Nullable
@@ -711,7 +718,7 @@
             recoverService();
             if (sService == null) {
                 Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
+                throw e.rethrowFromSystemServer();
             }
             try {
                 ApduServiceInfo serviceInfo =
@@ -727,7 +734,7 @@
 
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
+                throw e.rethrowFromSystemServer();
             }
         }
     }
@@ -739,7 +746,7 @@
      */
     @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
     @Nullable
-    public String getDescriptionForPreferredPaymentService() {
+    public CharSequence getDescriptionForPreferredPaymentService() {
         try {
             ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(mContext.getUserId());
             return (serviceInfo != null ? serviceInfo.getDescription() : null);
@@ -747,7 +754,7 @@
             recoverService();
             if (sService == null) {
                 Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
+                throw e.rethrowFromSystemServer();
             }
             try {
                 ApduServiceInfo serviceInfo =
@@ -755,7 +762,7 @@
                 return (serviceInfo != null ? serviceInfo.getDescription() : null);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
+                throw e.rethrowFromSystemServer();
             }
         }
     }
diff --git a/core/java/android/os/BadParcelableException.java b/core/java/android/os/BadParcelableException.java
index 7e0b1a5..9b1343c 100644
--- a/core/java/android/os/BadParcelableException.java
+++ b/core/java/android/os/BadParcelableException.java
@@ -32,4 +32,8 @@
     public BadParcelableException(Exception cause) {
         super(cause);
     }
+    /** @hide */
+    public BadParcelableException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index eb67492..0f53d4f 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -106,6 +106,12 @@
     public static final String HARDWARE = getString("ro.hardware");
 
     /**
+     * The hardware variant (SKU), if available.
+     */
+    @NonNull
+    public static final String SKU = getString("ro.boot.product.hardware.sku");
+
+    /**
      * Whether this build was for an emulator device.
      * @hide
      */
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 9c999b2..a28f5fb 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -16,6 +16,9 @@
 
 package android.os;
 
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 
 /**
@@ -114,20 +117,37 @@
             = "android.os.action.UPDATE_CARRIER_ID_DB";
 
     /**
-    * Broadcast intent action indicating that the updated emergency number database is available.
-    * <p>Extra: "VERSION" the numeric version of the new data. Devices should only install if the
-    * update version is newer than the current one.
-    * <p>Extra: "REQUIRED_HASH" the hash of the current update data.
-    * <p>Input: {@link android.content.Intent#getData} is URI of downloaded emergency number file.
-    * Devices should pick up the downloaded file and persist to the database
-    * {@code com.android.internal.telephony.emergency.EmergencyNumberTracker}.
+    * Update the emergency number database into the devices.
+    * <p>Extra: {@link #EXTRA_VERSION} the numeric version of the database.
+    * <p>Extra: {@link #EXTRA_REQUIRED_HASH} the hash of the database.
+    * <p>Input: {@link android.content.Intent#getData} the URI to download emergency number
+    * database.
     *
     * @hide
     */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.UPDATE_CONFIG)
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB =
             "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
 
+    /**
+     * An integer to indicate the numeric version of the new data. Devices should only install
+     * if the update version is newer than the current one.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_VERSION = "android.os.extra.VERSION";
+
+    /**
+     * A string to indicate the hash of the data.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REQUIRED_HASH = "android.os.extra.REQUIRED_HASH";
+
     private ConfigUpdate() {
     }
 }
diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index 37ca868..041d21f 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -157,7 +157,6 @@
         out.writeInt(mUid);
         out.writeString(mPkg);
         writeAudioAttributes(mAttrs, out, flags);
-        out.writeParcelable(mAttrs, flags);
         out.writeStrongBinder(mController.asBinder());
         out.writeStrongBinder(mToken);
     }
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index fa7404c..51df971 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -792,6 +792,17 @@
     }
 
     /**
+     * Remove any pending posts of messages with code 'what' and whose obj is
+     * 'object' that are in the message queue.  If <var>object</var> is null,
+     * all messages will be removed.
+     *
+     *@hide
+     */
+    public final void removeEqualMessages(int what, @Nullable Object object) {
+        mQueue.removeEqualMessages(this, what, object);
+    }
+
+    /**
      * Remove any pending posts of callbacks and sent messages whose
      * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
      * all callbacks and messages will be removed.
@@ -801,6 +812,16 @@
     }
 
     /**
+     * Remove any pending posts of callbacks and sent messages whose
+     * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
+     * all callbacks and messages will be removed.
+     *
+     *@hide
+     */
+    public final void removeCallbacksAndEqualMessages(@Nullable Object token) {
+        mQueue.removeCallbacksAndEqualMessages(this, token);
+    }
+    /**
      * Check if there are any pending posts of messages with code 'what' in
      * the message queue.
      */
@@ -825,6 +846,16 @@
     }
 
     /**
+     * Check if there are any pending posts of messages with code 'what' and
+     * whose obj is 'object' in the message queue.
+     *
+     *@hide
+     */
+    public final boolean hasEqualMessages(int what, @Nullable Object object) {
+        return mQueue.hasEqualMessages(this, what, object);
+    }
+
+    /**
      * Check if there are any pending posts of messages with callback r in
      * the message queue.
      */
diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java
index 92fcbb6..4dd797a 100644
--- a/core/java/android/os/HandlerThread.java
+++ b/core/java/android/os/HandlerThread.java
@@ -71,23 +71,35 @@
     /**
      * This method returns the Looper associated with this thread. If this thread not been started
      * or for any reason isAlive() returns false, this method will return null. If this thread
-     * has been started, this method will block until the looper has been initialized.  
+     * has been started, this method will block until the looper has been initialized.
      * @return The looper.
      */
     public Looper getLooper() {
         if (!isAlive()) {
             return null;
         }
-        
+
+        boolean wasInterrupted = false;
+
         // If the thread has been started, wait until the looper has been created.
         synchronized (this) {
             while (isAlive() && mLooper == null) {
                 try {
                     wait();
                 } catch (InterruptedException e) {
+                    wasInterrupted = true;
                 }
             }
         }
+
+        /*
+         * We may need to restore the thread's interrupted flag, because it may
+         * have been cleared above since we eat InterruptedExceptions
+         */
+        if (wasInterrupted) {
+            Thread.currentThread().interrupt();
+        }
+
         return mLooper;
     }
 
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index c43644d..f6eb8c2 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -293,13 +293,16 @@
      * then the given {@link DeathRecipient}'s
      * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
      * will be called.
-     * 
+     *
+     * <p>This will automatically be unlinked when all references to the linked
+     * binder proxy are dropped.</p>
+     *
      * <p>You will only receive death notifications for remote binders,
-     * as local binders by definition can't die without you dying as well.
-     * 
+     * as local binders by definition can't die without you dying as well.</p>
+     *
      * @throws RemoteException if the target IBinder's
      * process has already died.
-     * 
+     *
      * @see #unlinkToDeath
      */
     public void linkToDeath(@NonNull DeathRecipient recipient, int flags)
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 4e91057..5293504 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -617,6 +617,23 @@
         }
     }
 
+    boolean hasEqualMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h && p.what == what && (object == null || object.equals(p.obj))) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
     @UnsupportedAppUsage
     boolean hasMessages(Handler h, Runnable r, Object object) {
         if (h == null) {
@@ -686,6 +703,40 @@
         }
     }
 
+    void removeEqualMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.what == what
+                   && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.what == what
+                        && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
     void removeMessages(Handler h, Runnable r, Object object) {
         if (h == null || r == null) {
             return;
@@ -720,6 +771,41 @@
         }
     }
 
+    void removeEqualMessages(Handler h, Runnable r, Object object) {
+        if (h == null || r == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.callback == r
+                   && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.callback == r
+                        && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+
     void removeCallbacksAndMessages(Handler h, Object object) {
         if (h == null) {
             return;
@@ -753,6 +839,39 @@
         }
     }
 
+    void removeCallbacksAndEqualMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h
+                    && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
     private void removeAllMessagesLocked() {
         Message p = mMessages;
         while (p != null) {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 382c838..c17eafd 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3044,15 +3044,15 @@
         } catch (IllegalAccessException e) {
             Log.e(TAG, "Illegal access when unmarshalling: " + name, e);
             throw new BadParcelableException(
-                    "IllegalAccessException when unmarshalling: " + name);
+                    "IllegalAccessException when unmarshalling: " + name, e);
         } catch (ClassNotFoundException e) {
             Log.e(TAG, "Class not found when unmarshalling: " + name, e);
             throw new BadParcelableException(
-                    "ClassNotFoundException when unmarshalling: " + name);
+                    "ClassNotFoundException when unmarshalling: " + name, e);
         } catch (NoSuchFieldException e) {
             throw new BadParcelableException("Parcelable protocol requires a "
                     + "Parcelable.Creator object called "
-                    + "CREATOR on class " + name);
+                    + "CREATOR on class " + name, e);
         }
         if (creator == null) {
             throw new BadParcelableException("Parcelable protocol requires a "
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 6632ca5..9b360ed 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -118,7 +118,21 @@
      * by this Parcelable object instance.
      */
     public @ContentsFlags int describeContents();
-    
+
+    /**
+     * 'Stable' means this parcelable is guaranteed to be stable for multiple years.
+     * It must be guaranteed by setting stability field in aidl_interface,
+     * OR explicitly override this method from @JavaOnlyStableParcelable marked Parcelable.
+     * WARNING: isStable() is only expected to be overridden by auto-generated code,
+     * OR @JavaOnlyStableParcelable marked Parcelable only if there is guaranteed to
+     * be only once copy of the parcelable on the system.
+     * @return true if this parcelable is stable.
+     * @hide
+     */
+    default boolean isStable() {
+        return false;
+    }
+
     /**
      * Flatten this object in to a Parcel.
      * 
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 1f2a6d9..ad4fd16 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -29,6 +29,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.service.dreams.Sandman;
+import android.sysprop.InitProperties;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
@@ -1331,12 +1332,25 @@
         }
     }
 
+
+    /**
+     * Returns {@code true} if this device supports rebooting userspace.
+     *
+     * <p>This method exists solely for the sake of re-using same logic between {@code PowerManager}
+     * and {@code PowerManagerService}.
+     *
+     * @hide
+     */
+    public static boolean isRebootingUserspaceSupportedImpl() {
+        return InitProperties.is_userspace_reboot_supported().orElse(false);
+    }
+
     /**
      * Returns {@code true} if this device supports rebooting userspace.
      */
     // TODO(b/138605180): add link to documentation once it's ready.
     public boolean isRebootingUserspaceSupported() {
-        return SystemProperties.getBoolean("ro.init.userspace_reboot.is_supported", false);
+        return isRebootingUserspaceSupportedImpl();
     }
 
     /**
diff --git a/core/java/android/os/Registrant.java b/core/java/android/os/Registrant.java
index d6afd04..bde7ec1 100644
--- a/core/java/android/os/Registrant.java
+++ b/core/java/android/os/Registrant.java
@@ -46,7 +46,7 @@
     {
         internalNotifyRegistrant (null, null);
     }
-    
+
     @UnsupportedAppUsage
     public void
     notifyResult(Object result)
@@ -81,9 +81,7 @@
             Message msg = Message.obtain();
 
             msg.what = what;
-            
             msg.obj = new AsyncResult(userObj, result, exception);
-            
             h.sendMessage(msg);
         }
     }
@@ -126,4 +124,3 @@
     int             what;
     Object          userObj;
 }
-
diff --git a/core/java/android/os/RegistrantList.java b/core/java/android/os/RegistrantList.java
index 98f949b..53e0ae4 100644
--- a/core/java/android/os/RegistrantList.java
+++ b/core/java/android/os/RegistrantList.java
@@ -42,9 +42,9 @@
     {
         // if the handler is already in the registrant list, remove it
         remove(h);
-        add(new Registrant(h, what, obj));        
+        add(new Registrant(h, what, obj));
     }
-    
+
     @UnsupportedAppUsage
     public synchronized void
     add(Registrant r)
@@ -59,7 +59,7 @@
     {
         for (int i = registrants.size() - 1; i >= 0 ; i--) {
             Registrant  r = (Registrant) registrants.get(i);
-            
+
             if (r.refH == null) {
                 registrants.remove(i);
             }
@@ -88,7 +88,7 @@
             r.internalNotifyRegistrant(result, exception);
        }
     }
-    
+
     @UnsupportedAppUsage
     public /*synchronized*/ void
     notifyRegistrants()
@@ -109,14 +109,14 @@
         internalNotifyRegistrants (result, null);
     }
 
-    
+
     @UnsupportedAppUsage
     public /*synchronized*/ void
     notifyRegistrants(AsyncResult ar)
     {
         internalNotifyRegistrants(ar.result, ar.exception);
     }
-    
+
     @UnsupportedAppUsage
     public synchronized void
     remove(Handler h)
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index 814aac4..044892a 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -317,6 +317,16 @@
     }
 
     /**
+     * Returns number of arguments that haven't been processed yet.
+     */
+    public int getRemainingArgsCount() {
+        if (mArgPos >= mArgs.length) {
+            return 0;
+        }
+        return mArgs.length - mArgPos;
+    }
+
+    /**
      * Return the next argument on the command line, whatever it is; if there are
      * no arguments left, throws an IllegalArgumentException to report this to the user.
      */
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 3faaff7..8145707 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1015,7 +1015,6 @@
              * behaviors or empty states. Instead, apps should store data needed
              * while a user is locked under device protected storage areas.
              *
-             * @see Context#createCredentialProtectedStorageContext()
              * @see Context#createDeviceProtectedStorageContext()
              */
             public @NonNull Builder detectCredentialProtectedWhileLocked() {
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index de274c0..e907e22 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -614,9 +614,6 @@
      * encountered. Device is corrupted, and future updates must not be applied.
      * The device cannot recover without flashing and factory resets.
      * </ul>
-     *
-     * @throws ServiceSpecificException if other transient errors has occurred.
-     * A reboot may or may not help resolving the issue.
      */
     @WorkerThread
     @ErrorCode
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 75b4724..8900573 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -55,21 +55,16 @@
 
     /**
      * A click effect. Use this effect as a baseline, as it's the most common type of click effect.
-     *
-     * @see #get(int)
      */
     public static final int EFFECT_CLICK = Effect.CLICK;
 
     /**
      * A double click effect.
-     *
-     * @see #get(int)
      */
     public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
 
     /**
      * A tick effect. This effect is less strong compared to {@link #EFFECT_CLICK}.
-     * @see #get(int)
      */
     public static final int EFFECT_TICK = Effect.TICK;
 
@@ -93,7 +88,6 @@
 
     /**
      * A heavy click effect. This effect is stronger than {@link #EFFECT_CLICK}.
-     * @see #get(int)
      */
     public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
 
diff --git a/core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl b/core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl
index 0ae353d..5d8f6d1 100644
--- a/core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl
+++ b/core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl
@@ -17,11 +17,12 @@
 package android.os.incremental;
 
 /**
- * Wraps two file descriptors that Incremental Service uses to communicate
+ * Wraps the file descriptors Incremental Service uses to communicate
  * with Incremental FileSystem.
  * @hide
  */
 parcelable IncrementalFileSystemControlParcel {
     @nullable ParcelFileDescriptor cmd;
+    @nullable ParcelFileDescriptor pendingReads;
     @nullable ParcelFileDescriptor log;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index c7709b9..c31017b 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2304,6 +2304,19 @@
         }
     }
 
+    /**
+     * Check whether the device supports filesystem checkpoint.
+     *
+     * @return true if the device supports filesystem checkpoint, false otherwise.
+     */
+    public boolean isCheckpointSupported() {
+        try {
+            return mStorageManager.supportsCheckpoint();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private final Object mFuseAppLoopLock = new Object();
 
     @GuardedBy("mFuseAppLoopLock")
diff --git a/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java b/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
index 12503f6..89cd430 100644
--- a/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
+++ b/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
@@ -28,7 +28,6 @@
  * store data needed while a user is locked under device protected storage
  * areas.
  *
- * @see Context#createCredentialProtectedStorageContext()
  * @see Context#createDeviceProtectedStorageContext()
  */
 public final class CredentialProtectedWhileLockedViolation extends Violation {
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 1eb7664..dd2ea81 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -16,7 +16,6 @@
 package android.provider;
 
 import android.annotation.IntDef;
-import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
 import android.content.Context;
 import android.net.Uri;
@@ -240,7 +239,6 @@
      * blocked.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_NOT_BLOCKED = 0;
 
     /**
@@ -248,7 +246,6 @@
      * because it is in the list of blocked numbers maintained by the provider.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_IN_LIST = 1;
 
     /**
@@ -256,7 +253,6 @@
      * because it is from a restricted number.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_RESTRICTED = 2;
 
     /**
@@ -264,7 +260,6 @@
      * because it is from an unknown number.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_UNKNOWN_NUMBER = 3;
 
     /**
@@ -272,7 +267,6 @@
      * because it is from a pay phone.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_PAYPHONE = 4;
 
     /**
@@ -280,14 +274,12 @@
      * because it is from a number not in the users contacts.
      * @hide
      */
-    @SystemApi
     public static final int STATUS_BLOCKED_NOT_IN_CONTACTS = 5;
 
     /**
      * Integer reason indicating whether a call was blocked, and if so why.
      * @hide
      */
-    @SystemApi
     public static final String RES_BLOCK_STATUS = "block_status";
 
     /** @hide */
@@ -298,31 +290,6 @@
             "can_current_user_block_numbers";
 
     /** @hide */
-    @SystemApi
-    public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
-
-    /** @hide */
-    public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression";
-
-    /** @hide */
-    @SystemApi
-    public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
-
-    /** @hide */
-    public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
-            "get_block_suppression_status";
-
-    /** @hide */
-    public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION =
-            "should_show_emergency_call_notification";
-
-    /** @hide */
-    public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting";
-
-    /** @hide */
-    public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting";
-
-    /** @hide */
     public static final String RES_CAN_BLOCK_NUMBERS = "can_block";
 
     /** @hide */
@@ -439,11 +406,26 @@
         public static final String ACTION_BLOCK_SUPPRESSION_STATE_CHANGED =
                 "android.provider.action.BLOCK_SUPPRESSION_STATE_CHANGED";
 
+        public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
+
+        public static final String METHOD_END_BLOCK_SUPPRESSION = "end_block_suppression";
+
+        public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
+
+        public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
+                "get_block_suppression_status";
+
+        public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION =
+                "should_show_emergency_call_notification";
+
         public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed";
 
         public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP =
                 "blocking_suppressed_until_timestamp";
 
+        public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting";
+        public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting";
+
         /* Preference key of block numbers not in contacts setting. */
         public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED =
                 "block_numbers_not_in_contacts_setting";
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 9c6c92a..17fae1c 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -797,7 +797,6 @@
          * to changes.
          *
          * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
-         * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
          */
         @NonNull
         public static final Uri ENTERPRISE_CONTENT_URI =
@@ -1796,7 +1795,6 @@
          * to changes.
          *
          * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
-         * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
          */
         @NonNull
         public static final Uri ENTERPRISE_CONTENT_URI =
@@ -2010,7 +2008,6 @@
          * {@link DevicePolicyManager#setCrossProfileCalendarPackages(ComponentName, Set)}.
          *
          * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
-         * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
          */
         @NonNull
         public static final Uri ENTERPRISE_CONTENT_URI =
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 4bbd213..3980a5f 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ContentInterface;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -248,14 +247,14 @@
      * Get string array identifies the type or types of metadata returned
      * using DocumentsContract#getDocumentMetadata.
      *
-     * @see #getDocumentMetadata(ContentInterface, Uri)
+     * @see #getDocumentMetadata(ContentResolver, Uri)
      */
     public static final String METADATA_TYPES = "android:documentMetadataTypes";
 
     /**
      * Get Exif information using DocumentsContract#getDocumentMetadata.
      *
-     * @see #getDocumentMetadata(ContentInterface, Uri)
+     * @see #getDocumentMetadata(ContentResolver, Uri)
      */
     public static final String METADATA_EXIF = "android:documentExif";
 
@@ -263,7 +262,7 @@
      * Get total count of all documents currently stored under the given
      * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
      *
-     * @see #getDocumentMetadata(ContentInterface, Uri)
+     * @see #getDocumentMetadata(ContentResolver, Uri)
      */
     public static final String METADATA_TREE_COUNT = "android:metadataTreeCount";
 
@@ -271,7 +270,7 @@
      * Get total size of all documents currently stored under the given
      * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
      *
-     * @see #getDocumentMetadata(ContentInterface, Uri)
+     * @see #getDocumentMetadata(ContentResolver, Uri)
      */
     public static final String METADATA_TREE_SIZE = "android:metadataTreeSize";
 
@@ -395,7 +394,7 @@
          * Flag indicating that a document can be represented as a thumbnail.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#getDocumentThumbnail(ContentInterface, Uri,
+         * @see DocumentsContract#getDocumentThumbnail(ContentResolver, Uri,
          *      Point, CancellationSignal)
          * @see DocumentsProvider#openDocumentThumbnail(String, Point,
          *      android.os.CancellationSignal)
@@ -421,7 +420,7 @@
          * Flag indicating that a document is deletable.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#deleteDocument(ContentInterface, Uri)
+         * @see DocumentsContract#deleteDocument(ContentResolver, Uri)
          * @see DocumentsProvider#deleteDocument(String)
          */
         public static final int FLAG_SUPPORTS_DELETE = 1 << 2;
@@ -459,7 +458,7 @@
          * Flag indicating that a document can be renamed.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#renameDocument(ContentInterface, Uri, String)
+         * @see DocumentsContract#renameDocument(ContentResolver, Uri, String)
          * @see DocumentsProvider#renameDocument(String, String)
          */
         public static final int FLAG_SUPPORTS_RENAME = 1 << 6;
@@ -469,7 +468,7 @@
          * within the same document provider.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#copyDocument(ContentInterface, Uri, Uri)
+         * @see DocumentsContract#copyDocument(ContentResolver, Uri, Uri)
          * @see DocumentsProvider#copyDocument(String, String)
          */
         public static final int FLAG_SUPPORTS_COPY = 1 << 7;
@@ -479,7 +478,7 @@
          * within the same document provider.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#moveDocument(ContentInterface, Uri, Uri, Uri)
+         * @see DocumentsContract#moveDocument(ContentResolver, Uri, Uri, Uri)
          * @see DocumentsProvider#moveDocument(String, String, String)
          */
         public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
@@ -503,7 +502,7 @@
          * Flag indicating that a document can be removed from a parent.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#removeDocument(ContentInterface, Uri, Uri)
+         * @see DocumentsContract#removeDocument(ContentResolver, Uri, Uri)
          * @see DocumentsProvider#removeDocument(String, String)
          */
         public static final int FLAG_SUPPORTS_REMOVE = 1 << 10;
@@ -539,7 +538,7 @@
          * using DocumentsContract#getDocumentMetadata
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri)
+         * @see DocumentsContract#getDocumentMetadata(ContentResolver, Uri)
          */
         public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
     }
@@ -721,7 +720,7 @@
          * Flag indicating that this root can be ejected.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#ejectRoot(ContentInterface, Uri)
+         * @see DocumentsContract#ejectRoot(ContentResolver, Uri)
          * @see DocumentsProvider#ejectRoot(String)
          */
         public static final int FLAG_SUPPORTS_EJECT = 1 << 5;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d6869c5..cadae5c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8612,7 +8612,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
 
         /**
@@ -10197,6 +10196,8 @@
        public static final String NETSTATS_SAMPLE_ENABLED = "netstats_sample_enabled";
        /** {@hide} */
        public static final String NETSTATS_AUGMENT_ENABLED = "netstats_augment_enabled";
+       /** {@hide} */
+       public static final String NETSTATS_COMBINE_SUBTYPE_ENABLED = "netstats_combine_subtype_enabled";
 
        /** {@hide} */
        public static final String NETSTATS_DEV_BUCKET_DURATION = "netstats_dev_bucket_duration";
@@ -12060,17 +12061,6 @@
         public static final String ALWAYS_ON_DISPLAY_CONSTANTS = "always_on_display_constants";
 
         /**
-        * System VDSO global setting. This links to the "sys.vdso" system property.
-        * The following values are supported:
-        * false  -> both 32 and 64 bit vdso disabled
-        * 32     -> 32 bit vdso enabled
-        * 64     -> 64 bit vdso enabled
-        * Any other value defaults to both 32 bit and 64 bit true.
-        * @hide
-        */
-        public static final String SYS_VDSO = "sys_vdso";
-
-        /**
         * UidCpuPower global setting. This links the sys.uidcpupower system property.
         * The following values are supported:
         * 0 -> /proc/uid_cpupower/* are disabled
@@ -13407,12 +13397,12 @@
          * <p>
          * Type: int (0 for false, 1 for true)
          * @hide
-         * @deprecated Use {@link android.provider.Telephony.SimInfo#ENHANCED_4G_MODE_ENABLED}
-         * instead.
+         * @deprecated Use
+         * {@link android.provider.Telephony.SimInfo#COLUMN_ENHANCED_4G_MODE_ENABLED} instead.
          */
         @Deprecated
         public static final String ENHANCED_4G_MODE_ENABLED =
-                Telephony.SimInfo.ENHANCED_4G_MODE_ENABLED;
+                Telephony.SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED;
 
         /**
          * Whether VT (Video Telephony over IMS) is enabled
@@ -13420,10 +13410,10 @@
          * Type: int (0 for false, 1 for true)
          *
          * @hide
-         * @deprecated Use {@link android.provider.Telephony.SimInfo#VT_IMS_ENABLED} instead.
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_VT_IMS_ENABLED} instead.
          */
         @Deprecated
-        public static final String VT_IMS_ENABLED = Telephony.SimInfo.VT_IMS_ENABLED;
+        public static final String VT_IMS_ENABLED = Telephony.SimInfo.COLUMN_VT_IMS_ENABLED;
 
         /**
          * Whether WFC is enabled
@@ -13431,10 +13421,11 @@
          * Type: int (0 for false, 1 for true)
          *
          * @hide
-         * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ENABLED} instead.
+         * @deprecated Use
+         * {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_ENABLED} instead.
          */
         @Deprecated
-        public static final String WFC_IMS_ENABLED = Telephony.SimInfo.WFC_IMS_ENABLED;
+        public static final String WFC_IMS_ENABLED = Telephony.SimInfo.COLUMN_WFC_IMS_ENABLED;
 
         /**
          * WFC mode on home/non-roaming network.
@@ -13442,10 +13433,10 @@
          * Type: int - 2=Wi-Fi preferred, 1=Cellular preferred, 0=Wi-Fi only
          *
          * @hide
-         * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_MODE} instead.
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_MODE} instead.
          */
         @Deprecated
-        public static final String WFC_IMS_MODE = Telephony.SimInfo.WFC_IMS_MODE;
+        public static final String WFC_IMS_MODE = Telephony.SimInfo.COLUMN_WFC_IMS_MODE;
 
         /**
          * WFC mode on roaming network.
@@ -13453,11 +13444,12 @@
          * Type: int - see {@link #WFC_IMS_MODE} for values
          *
          * @hide
-         * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_MODE}
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_ROAMING_MODE}
          * instead.
          */
         @Deprecated
-        public static final String WFC_IMS_ROAMING_MODE = Telephony.SimInfo.WFC_IMS_ROAMING_MODE;
+        public static final String WFC_IMS_ROAMING_MODE =
+                Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE;
 
         /**
          * Whether WFC roaming is enabled
@@ -13465,12 +13457,12 @@
          * Type: int (0 for false, 1 for true)
          *
          * @hide
-         * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_ENABLED}
+         * @deprecated Use {@link android.provider.Telephony.SimInfo#COLUMN_WFC_IMS_ROAMING_ENABLED}
          * instead
          */
         @Deprecated
         public static final String WFC_IMS_ROAMING_ENABLED =
-                Telephony.SimInfo.WFC_IMS_ROAMING_ENABLED;
+                Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED;
 
         /**
          * Whether user can enable/disable LTE as a preferred network. A carrier might control
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index a897a8e..a95fe3c 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1306,7 +1306,6 @@
              * @hide
              */
             @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
             public static final String ACTION_SMS_MMS_DB_LOST =
                     "android.provider.action.SMS_MMS_DB_LOST";
 
@@ -1365,7 +1364,6 @@
      * Base column for the table that contain Carrier Public key.
      * @hide
      */
-    @SystemApi
     public interface CarrierColumns extends BaseColumns {
 
         /**
@@ -4358,6 +4356,7 @@
          * Indicates that whether the message has been broadcasted to the application.
          * <P>Type: BOOLEAN</P>
          */
+        // TODO: deprecate this in S.
         public static final String MESSAGE_BROADCASTED = "message_broadcasted";
 
         /**
@@ -4864,7 +4863,6 @@
          * Contains mappings between matching rules with carrier id for all carriers.
          * @hide
          */
-        @SystemApi
         public static final class All implements BaseColumns {
 
             /**
@@ -4940,7 +4938,6 @@
      * Contains SIM Information
      * @hide
      */
-    @SystemApi
     public static final class SimInfo {
         /**
          * Not instantiable.
@@ -4950,6 +4947,7 @@
 
         /**
          * The {@code content://} style URI for this provider.
+         * @hide
          */
         @NonNull
         public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo");
@@ -4957,25 +4955,32 @@
         /**
          * TelephonyProvider unique key column name is the subscription id.
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
+        public static final String COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID = "_id";
 
         /**
          * TelephonyProvider column name for a unique identifier for the subscription within the
          * specific subscription type. For example, it contains SIM ICC Identifier subscriptions
          * on Local SIMs. and Mac-address for Remote-SIM Subscriptions for Bluetooth devices.
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String ICC_ID = "icc_id";
+        public static final String COLUMN_ICC_ID = "icc_id";
 
         /**
          * TelephonyProvider column name for user SIM_SlOT_INDEX
          * <P>Type: INTEGER (int)</P>
+         *
+         * @hide
          */
-        public static final String SIM_SLOT_INDEX = "sim_id";
+        public static final String COLUMN_SIM_SLOT_INDEX = "sim_id";
 
         /**
          * SIM is not inserted
+         * @hide
          */
         public static final int SIM_NOT_INSERTED = -1;
 
@@ -4984,14 +4989,18 @@
          * <P>Type: INTEGER (int)</P> {@link #SUBSCRIPTION_TYPE_LOCAL_SIM} for Local-SIM
          * Subscriptions, {@link #SUBSCRIPTION_TYPE_REMOTE_SIM} for Remote-SIM Subscriptions.
          * Default value is 0.
+         *
+         * @hide
          */
-        public static final String SUBSCRIPTION_TYPE = "subscription_type";
+        public static final String COLUMN_SUBSCRIPTION_TYPE = "subscription_type";
 
         /**
          * This constant is to designate a subscription as a Local-SIM Subscription.
          * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on
          * the device.
          * </p>
+         *
+         * @hide
          */
         public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0;
 
@@ -5018,6 +5027,8 @@
          * phone; i.e., new Remote-SIM subscription treats the reconnected phone as a Remote-SIM
          * that was never seen before.
          * </p>
+         *
+         * @hide
          */
         public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1;
 
@@ -5030,71 +5041,89 @@
          * subscription and while is in voice call.
          *
          * Default value is empty string.
+         *
+         * @hide
          */
-        public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules";
+        public static final String COLUMN_DATA_ENABLED_OVERRIDE_RULES =
+                "data_enabled_override_rules";
 
         /**
          * TelephonyProvider column name for user displayed name.
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String DISPLAY_NAME = "display_name";
+        public static final String COLUMN_DISPLAY_NAME = "display_name";
 
         /**
          * TelephonyProvider column name for the service provider name for the SIM.
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String CARRIER_NAME = "carrier_name";
+        public static final String COLUMN_CARRIER_NAME = "carrier_name";
 
         /**
          * TelephonyProvider column name for source of the user displayed name.
          * <P>Type: INT (int)</P> with one of the NAME_SOURCE_XXXX values below
+         *
+         * @hide
          */
-        public static final String NAME_SOURCE = "name_source";
+        public static final String COLUMN_NAME_SOURCE = "name_source";
 
-        /** The name_source is the default, which is from the carrier id. */
-        public static final int NAME_SOURCE_DEFAULT = 0;
+        /** The name_source is from the carrier id. {@hide} */
+        public static final int NAME_SOURCE_CARRIER_ID = 0;
 
         /**
          * The name_source is from SIM EF_SPN.
+         * @hide
          */
         public static final int NAME_SOURCE_SIM_SPN = 1;
 
         /**
          * The name_source is from user input
+         * @hide
          */
         public static final int NAME_SOURCE_USER_INPUT = 2;
 
         /**
          * The name_source is carrier (carrier app, carrier config, etc.)
+         * @hide
          */
         public static final int NAME_SOURCE_CARRIER = 3;
 
         /**
          * The name_source is from SIM EF_PNN.
+         * @hide
          */
         public static final int NAME_SOURCE_SIM_PNN = 4;
 
         /**
          * TelephonyProvider column name for the color of a SIM.
          * <P>Type: INTEGER (int)</P>
+         *
+         * @hide
          */
-        public static final String COLOR = "color";
+        public static final String COLUMN_COLOR = "color";
 
-        /** TelephonyProvider column name for the default color of a SIM {@hide} */
+        /** The default color of a SIM {@hide} */
         public static final int COLOR_DEFAULT = 0;
 
         /**
          * TelephonyProvider column name for the phone number of a SIM.
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String NUMBER = "number";
+        public static final String COLUMN_NUMBER = "number";
 
         /**
          * TelephonyProvider column name for the number display format of a SIM.
          * <P>Type: INTEGER (int)</P>
+         *
          * @hide
          */
-        public static final String DISPLAY_NUMBER_FORMAT = "display_number_format";
+        public static final String COLUMN_DISPLAY_NUMBER_FORMAT = "display_number_format";
 
         /**
          * TelephonyProvider column name for the default display format of a SIM
@@ -5105,73 +5134,89 @@
         /**
          * TelephonyProvider column name for whether data roaming is enabled.
          * <P>Type: INTEGER (int)</P>
+         *
+         * @hide
          */
-        public static final String DATA_ROAMING = "data_roaming";
+        public static final String COLUMN_DATA_ROAMING = "data_roaming";
 
-        /** Indicates that data roaming is enabled for a subscription */
+        /** Indicates that data roaming is enabled for a subscription {@hide} */
         public static final int DATA_ROAMING_ENABLE = 1;
 
-        /** Indicates that data roaming is disabled for a subscription */
+        /** Indicates that data roaming is disabled for a subscription {@hide} */
         public static final int DATA_ROAMING_DISABLE = 0;
 
-        /** TelephonyProvider column name for default data roaming setting: disable */
-        public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE;
-
         /**
          * TelephonyProvider column name for subscription carrier id.
          * @see TelephonyManager#getSimCarrierId()
          * <p>Type: INTEGER (int) </p>
+         *
+         * @hide
          */
-        public static final String CARRIER_ID = "carrier_id";
+        public static final String COLUMN_CARRIER_ID = "carrier_id";
 
         /**
          * A comma-separated list of EHPLMNs associated with the subscription
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String EHPLMNS = "ehplmns";
+        public static final String COLUMN_EHPLMNS = "ehplmns";
 
         /**
          * A comma-separated list of HPLMNs associated with the subscription
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String HPLMNS = "hplmns";
+        public static final String COLUMN_HPLMNS = "hplmns";
 
         /**
          * TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String MCC_STRING = "mcc_string";
+        public static final String COLUMN_MCC_STRING = "mcc_string";
 
         /**
          * TelephonyProvider column name for the MNC associated with a SIM, stored as a string.
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String MNC_STRING = "mnc_string";
+        public static final String COLUMN_MNC_STRING = "mnc_string";
 
         /**
          * TelephonyProvider column name for the MCC associated with a SIM.
          * <P>Type: INTEGER (int)</P>
+         *
+         * @hide
          */
-        public static final String MCC = "mcc";
+        public static final String COLUMN_MCC = "mcc";
 
         /**
          * TelephonyProvider column name for the MNC associated with a SIM.
          * <P>Type: INTEGER (int)</P>
+         *
+         * @hide
          */
-        public static final String MNC = "mnc";
+        public static final String COLUMN_MNC = "mnc";
 
         /**
          * TelephonyProvider column name for the iso country code associated with a SIM.
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String ISO_COUNTRY_CODE = "iso_country_code";
+        public static final String COLUMN_ISO_COUNTRY_CODE = "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 = "sim_provisioning_status";
+        public static final String COLUMN_SIM_PROVISIONING_STATUS = "sim_provisioning_status";
 
         /** The sim is provisioned {@hide} */
         public static final int SIM_PROVISIONED = 0;
@@ -5180,147 +5225,174 @@
          * 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.
+         *
+         * @hide
          */
-        public static final String IS_EMBEDDED = "is_embedded";
+        public static final String COLUMN_IS_EMBEDDED = "is_embedded";
 
         /**
          * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of
          * the current enabled profile on the card, while for eUICC card it is the EID of the card.
          * <P>Type: TEXT (String)</P>
+         *
+         * @hide
          */
-        public static final String CARD_ID = "card_id";
+        public static final String COLUMN_CARD_ID = "card_id";
 
         /**
          * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
-         * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
+         * {@link UiccAccessRule#encodeRules}. Only present if {@link #COLUMN_IS_EMBEDDED} is 1.
          * <p>TYPE: BLOB
+         *
+         * @hide
          */
-        public static final String ACCESS_RULES = "access_rules";
+        public static final String COLUMN_ACCESS_RULES = "access_rules";
 
         /**
          * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
          * {@link UiccAccessRule#encodeRules} but for the rules that come from CarrierConfigs.
          * Only present if there are access rules in CarrierConfigs
          * <p>TYPE: BLOB
+         *
+         * @hide
          */
-        public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS =
+        public static final String COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS =
                 "access_rules_from_carrier_configs";
 
         /**
          * TelephonyProvider column name identifying whether an embedded subscription is on a
          * removable card. Such subscriptions are marked inaccessible as soon as the current card
          * is removed. Otherwise, they will remain accessible unless explicitly deleted. Only
-         * present if {@link #IS_EMBEDDED} is 1.
+         * present if {@link #COLUMN_IS_EMBEDDED} is 1.
          * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable.
+         *
+         * @hide
          */
-        public static final String IS_REMOVABLE = "is_removable";
+        public static final String COLUMN_IS_REMOVABLE = "is_removable";
 
-        /** TelephonyProvider column name for extreme threat in CB settings */
-        public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts";
+        /** TelephonyProvider column name for extreme threat in CB settings {@hide} */
+        public static final String COLUMN_CB_EXTREME_THREAT_ALERT =
+                "enable_cmas_extreme_threat_alerts";
 
-        /** TelephonyProvider column name for severe threat in CB settings */
-        public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts";
+        /** TelephonyProvider column name for severe threat in CB settings {@hide} */
+        public static final String COLUMN_CB_SEVERE_THREAT_ALERT =
+                "enable_cmas_severe_threat_alerts";
 
-        /** TelephonyProvider column name for amber alert in CB settings */
-        public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts";
+        /** TelephonyProvider column name for amber alert in CB settings {@hide} */
+        public static final String COLUMN_CB_AMBER_ALERT = "enable_cmas_amber_alerts";
 
-        /** TelephonyProvider column name for emergency alert in CB settings */
-        public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts";
+        /** TelephonyProvider column name for emergency alert in CB settings {@hide} */
+        public static final String COLUMN_CB_EMERGENCY_ALERT = "enable_emergency_alerts";
 
-        /** TelephonyProvider column name for alert sound duration in CB settings */
-        public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration";
+        /** TelephonyProvider column name for alert sound duration in CB settings {@hide} */
+        public static final String COLUMN_CB_ALERT_SOUND_DURATION = "alert_sound_duration";
 
-        /** TelephonyProvider column name for alert reminder interval in CB settings */
-        public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
+        /** TelephonyProvider column name for alert reminder interval in CB settings {@hide} */
+        public static final String COLUMN_CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval";
 
-        /** TelephonyProvider column name for enabling vibrate in CB settings */
-        public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate";
+        /** TelephonyProvider column name for enabling vibrate in CB settings {@hide} */
+        public static final String COLUMN_CB_ALERT_VIBRATE = "enable_alert_vibrate";
 
-        /** TelephonyProvider column name for enabling alert speech in CB settings */
-        public static final String CB_ALERT_SPEECH = "enable_alert_speech";
+        /** TelephonyProvider column name for enabling alert speech in CB settings {@hide} */
+        public static final String COLUMN_CB_ALERT_SPEECH = "enable_alert_speech";
 
-        /** TelephonyProvider column name for ETWS test alert in CB settings */
-        public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
+        /** TelephonyProvider column name for ETWS test alert in CB settings {@hide} */
+        public static final String COLUMN_CB_ETWS_TEST_ALERT = "enable_etws_test_alerts";
 
-        /** TelephonyProvider column name for enable channel50 alert in CB settings */
-        public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
+        /** TelephonyProvider column name for enable channel50 alert in CB settings {@hide} */
+        public static final String COLUMN_CB_CHANNEL_50_ALERT = "enable_channel_50_alerts";
 
-        /** TelephonyProvider column name for CMAS test alert in CB settings */
-        public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts";
+        /** TelephonyProvider column name for CMAS test alert in CB settings {@hide} */
+        public static final String COLUMN_CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts";
 
-        /** TelephonyProvider column name for Opt out dialog in CB settings */
-        public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
+        /** TelephonyProvider column name for Opt out dialog in CB settings {@hide} */
+        public static final String COLUMN_CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
 
         /**
          * TelephonyProvider column name for enable Volte.
          *
          * If this setting is not initialized (set to -1)  then we use the Carrier Config value
          * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
+         *
+         * @hide
          */
-        public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+        public static final String COLUMN_ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
 
-        /** TelephonyProvider column name for enable VT (Video Telephony over IMS) */
-        public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+        /** TelephonyProvider column name for enable VT (Video Telephony over IMS) {@hide} */
+        public static final String COLUMN_VT_IMS_ENABLED = "vt_ims_enabled";
 
-        /** TelephonyProvider column name for enable Wifi calling */
-        public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+        /** TelephonyProvider column name for enable Wifi calling {@hide} */
+        public static final String COLUMN_WFC_IMS_ENABLED = "wfc_ims_enabled";
 
-        /** TelephonyProvider column name for Wifi calling mode */
-        public static final String WFC_IMS_MODE = "wfc_ims_mode";
+        /** TelephonyProvider column name for Wifi calling mode {@hide} */
+        public static final String COLUMN_WFC_IMS_MODE = "wfc_ims_mode";
 
-        /** TelephonyProvider column name for Wifi calling mode in roaming */
-        public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+        /** TelephonyProvider column name for Wifi calling mode in roaming {@hide} */
+        public static final String COLUMN_WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
 
-        /** TelephonyProvider column name for enable Wifi calling in roaming */
-        public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+        /** TelephonyProvider column name for enable Wifi calling in roaming {@hide} */
+        public static final String COLUMN_WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
 
         /**
-         * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this
-         * subscription.
+         * TelephonyProvider column name for determining if the user has enabled IMS RCS User
+         * Capability Exchange (UCE) for this subscription.
+         *
+         * @hide
          */
-        public static final String IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
+        public static final String COLUMN_IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
 
         /**
          * TelephonyProvider column name for whether a subscription is opportunistic, that is,
          * whether the network it connects to is limited in functionality or coverage.
          * For example, CBRS.
          * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic.
+         *
+         * @hide
          */
-        public static final String IS_OPPORTUNISTIC = "is_opportunistic";
+        public static final String COLUMN_IS_OPPORTUNISTIC = "is_opportunistic";
 
         /**
          * TelephonyProvider column name for group ID. Subscriptions with same group ID
          * are considered bundled together, and should behave as a single subscription at
          * certain scenarios.
+         *
+         * @hide
          */
-        public static final String GROUP_UUID = "group_uuid";
+        public static final String COLUMN_GROUP_UUID = "group_uuid";
 
         /**
          * TelephonyProvider column name for group owner. It's the package name who created
          * the subscription group.
+         *
+         * @hide
          */
-        public static final String GROUP_OWNER = "group_owner";
+        public static final String COLUMN_GROUP_OWNER = "group_owner";
 
         /**
          * TelephonyProvider column name for whether a subscription is metered or not, that is,
          * whether the network it connects to charges for subscription or not. For example, paid
          * CBRS or unpaid.
+         *
          * @hide
          */
-        public static final String IS_METERED = "is_metered";
+        public static final String COLUMN_IS_METERED = "is_metered";
 
         /**
          * TelephonyProvider column name for the profile class of a subscription
-         * Only present if {@link #IS_EMBEDDED} is 1.
+         * Only present if {@link #COLUMN_IS_EMBEDDED} is 1.
          * <P>Type: INTEGER (int)</P>
+         *
+         * @hide
          */
-        public static final String PROFILE_CLASS = "profile_class";
+        public static final String COLUMN_PROFILE_CLASS = "profile_class";
 
         /**
          * A testing profile can be pre-loaded or downloaded onto
          * the eUICC and provides connectivity to test equipment
          * for the purpose of testing the device and the eUICC. It
          * is not intended to store any operator credentials.
+         *
+         * @hide
          */
         public static final int PROFILE_CLASS_TESTING = 0;
 
@@ -5328,6 +5400,8 @@
          * A provisioning profile is pre-loaded onto the eUICC and
          * provides connectivity to a mobile network solely for the
          * purpose of provisioning profiles.
+         *
+         * @hide
          */
         public static final int PROFILE_CLASS_PROVISIONING = 1;
 
@@ -5335,6 +5409,8 @@
          * An operational profile can be pre-loaded or downloaded
          * onto the eUICC and provides services provided by the
          * operator.
+         *
+         * @hide
          */
         public static final int PROFILE_CLASS_OPERATIONAL = 2;
 
@@ -5342,25 +5418,32 @@
          * The profile class is unset. This occurs when profile class
          * info is not available. The subscription either has no profile
          * metadata or the profile metadata did not encode profile class.
+         *
+         * @hide
          */
         public static final int PROFILE_CLASS_UNSET = -1;
 
-        /** Default profile class */
-        public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
-
         /**
          * IMSI (International Mobile Subscriber Identity).
          * <P>Type: TEXT </P>
+         *
+         * @hide
          */
-        public static final String IMSI = "imsi";
+        public static final String COLUMN_IMSI = "imsi";
 
-        /** Whether uicc applications is set to be enabled or disabled. By default it's enabled. */
-        public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled";
+        /**
+         * Whether uicc applications is set to be enabled or disabled. By default it's enabled.
+         * @hide
+         */
+        public static final String COLUMN_UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled";
 
         /**
          * TelephonyProvider column name for allowed network types. Indicate which network types
          * are allowed. Default is -1.
+         * <P>Type: BIGINT (long) </P>
+         *
+         * @hide
          */
-        public static final String ALLOWED_NETWORK_TYPES = "allowed_network_types";
+        public static final String COLUMN_ALLOWED_NETWORK_TYPES = "allowed_network_types";
     }
 }
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 7f68d91..90c934d 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -160,7 +160,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED)
+    @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION)
     public boolean reset() {
         if (!mService.isConnected()) {
             Log.e(TAG, "service is not connected");
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index c28d2bb..e274460 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -262,7 +262,7 @@
          *
          * @param condition condition used to trigger the updates.
          * @param updates actions to be applied to the
-         * {@link #CustomDescription.Builder(RemoteViews) template presentation} when the condition
+         * {@link #Builder(RemoteViews) template presentation} when the condition
          * is satisfied.
          *
          * @return this builder
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 12376e8..974f0ea 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -123,7 +123,7 @@
          * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
          *
          * @deprecated use
-         * {@link #ImageTransformation.Builder(AutofillId, Pattern, int, CharSequence)} instead.
+         * {@link #Builder(AutofillId, Pattern, int, CharSequence)} instead.
          */
         @Deprecated
         public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 94b9d05..3a70bef 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -628,7 +628,7 @@
          *
          * <p>The sanitizer can also be used as an alternative for a
          * {@link #setValidator(Validator) validator}. If any of the {@code ids} is a
-         * {@link #SaveInfo.Builder(int, AutofillId[]) required id} and the {@code sanitizer} fails
+         * {@link #Builder(int, AutofillId[]) required id} and the {@code sanitizer} fails
          * because of it, then the save UI is not shown.
          *
          * @param sanitizer an implementation provided by the Android System.
@@ -686,7 +686,7 @@
          * Builds a new {@link SaveInfo} instance.
          *
          * @throws IllegalStateException if no
-         * {@link #SaveInfo.Builder(int, AutofillId[]) required ids},
+         * {@link #Builder(int, AutofillId[]) required ids},
          * or {@link #setOptionalIds(AutofillId[]) optional ids}, or {@link #FLAG_DELAY_SAVE}
          * were set
          */
diff --git a/core/java/android/telephony/CellBroadcastIntents.java b/core/java/android/telephony/CellBroadcastIntents.java
index 32d330e..e07f69a 100644
--- a/core/java/android/telephony/CellBroadcastIntents.java
+++ b/core/java/android/telephony/CellBroadcastIntents.java
@@ -46,7 +46,11 @@
 
     /**
      * Broadcast intent action for notifying area information has been updated. The information
-     * can be retrieved by {@link CellBroadcastService#getCellBroadcastAreaInfo(int)}
+     * can be retrieved by {@link CellBroadcastService#getCellBroadcastAreaInfo(int)}. The
+     * associated SIM slot index of updated area information can be retrieved through the extra
+     * {@link SubscriptionManager#EXTRA_SLOT_INDEX}.
+     *
+     * @see SubscriptionManager#EXTRA_SLOT_INDEX
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_AREA_INFO_UPDATED =
@@ -81,7 +85,6 @@
             int initialCode, int slotIndex) {
         Intent backgroundIntent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
         backgroundIntent.putExtra(EXTRA_MESSAGE, smsCbMessage);
-        backgroundIntent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         putPhoneIdAndSubIdExtra(context, backgroundIntent, slotIndex);
 
         String receiverPermission = Manifest.permission.RECEIVE_SMS;
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 1dca7fd..d640775 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -21,6 +21,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.compat.annotation.ChangeId;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Binder;
 import android.os.Build;
@@ -65,6 +66,43 @@
     private static final boolean DBG = false; // STOPSHIP if true
 
     /**
+     * Experiment flag to set the per-pid registration limit for PhoneStateListeners
+     *
+     * Limit on registrations of {@link PhoneStateListener}s on a per-pid
+     * basis. When this limit is exceeded, any calls to {@link TelephonyManager#listen} will fail
+     * with an {@link IllegalStateException}.
+     *
+     * {@link android.os.Process#PHONE_UID}, {@link android.os.Process#SYSTEM_UID}, and the uid that
+     * TelephonyRegistry runs under are exempt from this limit.
+     *
+     * If the value of the flag is less than 1, enforcement of the limit will be disabled.
+     * @hide
+     */
+    public static final String FLAG_PER_PID_REGISTRATION_LIMIT =
+            "phone_state_listener_per_pid_registration_limit";
+
+    /**
+     * Default value for the per-pid registation limit.
+     * See {@link #FLAG_PER_PID_REGISTRATION_LIMIT}.
+     * @hide
+     */
+    public static final int DEFAULT_PER_PID_REGISTRATION_LIMIT = 50;
+
+    /**
+     * This change enables a limit on the number of {@link PhoneStateListener} objects any process
+     * may register via {@link TelephonyManager#listen}. The default limit is 50, which may change
+     * via remote device config updates.
+     *
+     * This limit is enforced via an {@link IllegalStateException} thrown from
+     * {@link TelephonyManager#listen} when the offending process attempts to register one too many
+     * listeners.
+     *
+     * @hide
+     */
+    @ChangeId
+    public static final long PHONE_STATE_LISTENER_LIMIT_CHANGE_ID = 150880553L;
+
+    /**
      * Stop listening for updates.
      *
      * The PhoneStateListener is not tied to any subscription and unregistered for any update.
@@ -176,7 +214,6 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
-    @SystemApi
     public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH          = 0x00000200;
 
     /**
@@ -342,8 +379,6 @@
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see #onEmergencyNumberListChanged
      */
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
 
@@ -422,11 +457,22 @@
      * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @see #onRegistrationFailed()
+     * @see #onRegistrationFailed
      */
     @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
     public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
 
+    /**
+     * Listen for Barring Information for the current registered / camped cell.
+     *
+     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @see #onBarringInfoChanged
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public static final int LISTEN_BARRING_INFO = 0x80000000;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -716,7 +762,8 @@
      *
      */
     @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
-    public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+    public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
+            int preciseDisconnectCause) {
         // default implementation empty
     }
 
@@ -845,16 +892,16 @@
 
     /**
      * Callback invoked when the display info has changed on the registered subscription.
-     * <p> The {@link DisplayInfo} contains status information shown to the user based on
+     * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user based on
      * carrier policy.
      *
      * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @param displayInfo The display information.
+     * @param telephonyDisplayInfo The display information.
      */
     @RequiresPermission((android.Manifest.permission.READ_PHONE_STATE))
-    public void onDisplayInfoChanged(@NonNull DisplayInfo displayInfo) {
+    public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
         // default implementation empty
     }
 
@@ -1036,6 +1083,20 @@
     }
 
     /**
+     * Report updated barring information for the current camped/registered cell.
+     *
+     * <p>Barring info is provided for all services applicable to the current camped/registered
+     * cell, for the registered PLMN and current access class/access category.
+     *
+     * @param barringInfo for all services on the current cell.
+     *
+     * @see android.telephony.BarringInfo
+     */
+    public void onBarringInfoChanged(@NonNull BarringInfo barringInfo) {
+        // default implementation empty
+    }
+
+    /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
      *
@@ -1223,13 +1284,13 @@
                             () -> psl.onUserMobileDataStateChanged(enabled)));
         }
 
-        public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+        public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
 
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
-                            () -> psl.onDisplayInfoChanged(displayInfo)));
+                            () -> psl.onDisplayInfoChanged(telephonyDisplayInfo)));
         }
 
         public void onOemHookRawEvent(byte[] rawData) {
@@ -1328,6 +1389,14 @@
                             cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
             // default implementation empty
         }
+
+        public void onBarringInfoChanged(BarringInfo barringInfo) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onBarringInfoChanged(barringInfo)));
+        }
     }
 
 
diff --git a/core/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java
index 28a5c20..d5ac436 100644
--- a/core/java/android/telephony/SubscriptionPlan.java
+++ b/core/java/android/telephony/SubscriptionPlan.java
@@ -91,10 +91,11 @@
     private long dataUsageBytes = BYTES_UNKNOWN;
     private long dataUsageTime = TIME_UNKNOWN;
     private @NetworkType int[] networkTypes;
-    private long networkTypesBitMask;
 
     private SubscriptionPlan(RecurrenceRule cycleRule) {
         this.cycleRule = Preconditions.checkNotNull(cycleRule);
+        this.networkTypes = Arrays.copyOf(TelephonyManager.getAllNetworkTypes(),
+                TelephonyManager.getAllNetworkTypes().length);
     }
 
     private SubscriptionPlan(Parcel source) {
@@ -221,28 +222,10 @@
 
     /**
      * Return an array containing all {@link NetworkType}s this SubscriptionPlan applies to.
-     * A null value means this SubscriptionPlan applies to all network types.
+     * @see TelephonyManager for network types values
      */
-    public @Nullable @NetworkType int[] getNetworkTypes() {
-        return networkTypes;
-    }
-
-    /**
-     * Return the networkTypes array converted to a {@link TelephonyManager.NetworkTypeBitMask}
-     * @hide
-     */
-    public long getNetworkTypesBitMask() {
-        // calculate bitmask the first time and save for future calls
-        if (networkTypesBitMask == 0) {
-            if (networkTypes == null) {
-                networkTypesBitMask = ~0;
-            } else {
-                for (int networkType : networkTypes) {
-                    networkTypesBitMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
-                }
-            }
-        }
-        return networkTypesBitMask;
+    public @NonNull @NetworkType int[] getNetworkTypes() {
+        return Arrays.copyOf(networkTypes, networkTypes.length);
     }
 
     /**
@@ -379,14 +362,24 @@
         }
 
         /**
-         * Set the network types this SubscriptionPlan applies to.
+         * Set the network types this SubscriptionPlan applies to. By default the plan will apply
+         * to all network types. An empty array means this plan applies to no network types.
          *
-         * @param networkTypes a set of all {@link NetworkType}s that apply to this plan.
-         *            A null value means the plan applies to all network types,
-         *            and an empty array means the plan applies to no network types.
+         * @param networkTypes an array of all {@link NetworkType}s that apply to this plan.
+         * @see TelephonyManager for network type values
          */
-        public @NonNull Builder setNetworkTypes(@Nullable @NetworkType int[] networkTypes) {
-            plan.networkTypes = networkTypes;
+        public @NonNull Builder setNetworkTypes(@NonNull @NetworkType int[] networkTypes) {
+            plan.networkTypes = Arrays.copyOf(networkTypes, networkTypes.length);
+            return this;
+        }
+
+        /**
+         * Reset any network types that were set with {@link #setNetworkTypes(int[])}.
+         * This will make the SubscriptionPlan apply to all network types.
+         */
+        public @NonNull Builder resetNetworkTypes() {
+            plan.networkTypes = Arrays.copyOf(TelephonyManager.getAllNetworkTypes(),
+                    TelephonyManager.getAllNetworkTypes().length);
             return this;
         }
     }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index c4b4c43..5924421 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -26,8 +26,10 @@
 import android.telephony.Annotation.CallState;
 import android.telephony.Annotation.DataActivityType;
 import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.DisconnectCauses;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.Annotation.PreciseCallStates;
+import android.telephony.Annotation.PreciseDisconnectCauses;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SimActivationState;
 import android.telephony.Annotation.SrvccState;
@@ -229,11 +231,9 @@
      * invalid.
      * @param state latest call state. e.g, offhook, ringing
      * @param incomingNumber incoming phone number.
-     *
-     * @hide
      */
     public void notifyCallStateChanged(int subId, int slotIndex, @CallState int state,
-            String incomingNumber) {
+            @Nullable String incomingNumber) {
         try {
             sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber);
         } catch (RemoteException ex) {
@@ -266,10 +266,8 @@
      * @param slotIndex for which the service state changed. Can be derived from subId except
      * subId is invalid.
      * @param state service state e.g, in service, out of service or roaming status.
-     *
-     * @hide
      */
-    public void notifyServiceStateChanged(int subId, int slotIndex, ServiceState state) {
+    public void notifyServiceStateChanged(int subId, int slotIndex, @NonNull ServiceState state) {
         try {
             sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
         } catch (RemoteException ex) {
@@ -284,11 +282,9 @@
      * @param slotIndex for which the signalstrength changed. Can be derived from subId except when
      * subId is invalid.
      * @param signalStrength e.g, signalstrength level {@see SignalStrength#getLevel()}
-     *
-     * @hide
      */
     public void notifySignalStrengthChanged(int subId, int slotIndex,
-        SignalStrength signalStrength) {
+            @NonNull SignalStrength signalStrength) {
         try {
             sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
         } catch (RemoteException ex) {
@@ -305,8 +301,6 @@
      * except when subId is invalid.
      * @param msgWaitingInd {@code true} indicates there is message-waiting indicator, {@code false}
      * otherwise.
-     *
-     * @hide
      */
     public void notifyMessageWaitingChanged(int subId, int slotIndex, boolean msgWaitingInd) {
         try {
@@ -322,8 +316,6 @@
      * @param subId for which call forwarding status changed.
      * @param callForwardInd {@code true} indicates there is call forwarding, {@code false}
      * otherwise.
-     *
-     * @hide
      */
     public void notifyCallForwardingChanged(int subId, boolean callForwardInd) {
         try {
@@ -339,8 +331,6 @@
      * @param subId for which data activity state changed.
      * @param dataActivityType indicates the latest data activity type e.g, {@link
      * TelephonyManager#DATA_ACTIVITY_IN}
-     *
-     * @hide
      */
     public void notifyDataActivityChanged(int subId, @DataActivityType int dataActivityType) {
         try {
@@ -361,10 +351,9 @@
      *
      * @see android.telephony.PreciseDataConnection
      * @see TelephonyManager#DATA_DISCONNECTED
-     * @hide
      */
     public void notifyDataConnectionForSubscriber(int slotIndex, int subId,
-            String apnType, PreciseDataConnectionState preciseState) {
+            String apnType, @Nullable PreciseDataConnectionState preciseState) {
         try {
             sRegistry.notifyDataConnectionForSubscriber(
                     slotIndex, subId, apnType, preciseState);
@@ -381,10 +370,8 @@
      * subId is invalid.
      * @param callQuality Information about call quality e.g, call quality level
      * @param networkType associated with this data connection. e.g, LTE
-     *
-     * @hide
      */
-    public void notifyCallQualityChanged(int subId, int slotIndex, CallQuality callQuality,
+    public void notifyCallQualityChanged(int subId, int slotIndex, @NonNull CallQuality callQuality,
         @NetworkType int networkType) {
         try {
             sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
@@ -399,8 +386,6 @@
      * @param subId for which emergency number list changed.
      * @param slotIndex for which emergency number list changed. Can be derived from subId except
      * when subId is invalid.
-     *
-     * @hide
      */
     public void notifyEmergencyNumberList(int subId, int slotIndex) {
         try {
@@ -417,8 +402,6 @@
      * @param slotIndex for which radio power state changed. Can be derived from subId except when
      * subId is invalid.
      * @param radioPowerState the current modem radio state.
-     *
-     * @hide
      */
     public void notifyRadioPowerStateChanged(int subId, int slotIndex,
         @RadioPowerState int radioPowerState) {
@@ -433,10 +416,8 @@
      * Notify {@link PhoneCapability} changed.
      *
      * @param phoneCapability the capability of the modem group.
-     *
-     * @hide
      */
-    public void notifyPhoneCapabilityChanged(PhoneCapability phoneCapability) {
+    public void notifyPhoneCapabilityChanged(@NonNull PhoneCapability phoneCapability) {
         try {
             sRegistry.notifyPhoneCapabilityChanged(phoneCapability);
         } catch (RemoteException ex) {
@@ -465,8 +446,6 @@
      * @param slotIndex for which data activation state changed. Can be derived from subId except
      * when subId is invalid.
      * @param activationState sim activation state e.g, activated.
-     *
-     * @hide
      */
     public void notifyDataActivationStateChanged(int subId, int slotIndex,
         @SimActivationState int activationState) {
@@ -486,8 +465,6 @@
      * @param slotIndex for which voice activation state changed. Can be derived from subId except
      * subId is invalid.
      * @param activationState sim activation state e.g, activated.
-     *
-     * @hide
      */
     public void notifyVoiceActivationStateChanged(int subId, int slotIndex,
         @SimActivationState int activationState) {
@@ -507,8 +484,6 @@
      * @param slotIndex for which mobile data state has changed. Can be derived from subId except
      * when subId is invalid.
      * @param state {@code true} indicates mobile data is enabled/on. {@code false} otherwise.
-     *
-     * @hide
      */
     public void notifyUserMobileDataStateChanged(int slotIndex, int subId, boolean state) {
         try {
@@ -519,35 +494,18 @@
     }
 
     /**
-     * TODO: this is marked as deprecated, can we move this one safely?
-     *
-     * @param subId
-     * @param slotIndex
-     * @param rawData
-     *
-     * @hide
-     */
-    public void notifyOemHookRawEventForSubscriber(int subId, int slotIndex, byte[] rawData) {
-        try {
-            sRegistry.notifyOemHookRawEventForSubscriber(slotIndex, subId, rawData);
-        } catch (RemoteException ex) {
-            // system process is dead
-        }
-    }
-
-    /**
      * Notify display info changed.
      *
      * @param slotIndex The SIM slot index for which display info has changed. Can be
      * derived from {@code subscriptionId} except when {@code subscriptionId} is invalid, such as
      * when the device is in emergency-only mode.
      * @param subscriptionId Subscription id for which display network info has changed.
-     * @param displayInfo The display info.
+     * @param telephonyDisplayInfo The display info.
      */
     public void notifyDisplayInfoChanged(int slotIndex, int subscriptionId,
-            @NonNull DisplayInfo displayInfo) {
+                                         @NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
         try {
-            sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, displayInfo);
+            sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo);
         } catch (RemoteException ex) {
             // system process is dead
         }
@@ -558,10 +516,8 @@
      *
      * @param subId for which ims call disconnect.
      * @param imsReasonInfo the reason for ims call disconnect.
-     *
-     * @hide
      */
-    public void notifyImsDisconnectCause(int subId, ImsReasonInfo imsReasonInfo) {
+    public void notifyImsDisconnectCause(int subId, @NonNull ImsReasonInfo imsReasonInfo) {
         try {
             sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo);
         } catch (RemoteException ex) {
@@ -578,11 +534,9 @@
      * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN.
      * @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
      * @param failCause data fail cause.
-     *
-     * @hide
      */
     public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, String apnType,
-        String apn, @DataFailureCause int failCause) {
+            @Nullable String apn, @DataFailureCause int failCause) {
         try {
             sRegistry.notifyPreciseDataConnectionFailed(slotIndex, subId, apnType, apn, failCause);
         } catch (RemoteException ex) {
@@ -596,8 +550,6 @@
      *
      * @param subId for which srvcc state changed.
      * @param state srvcc state
-     *
-     * @hide
      */
     public void notifySrvccStateChanged(int subId, @SrvccState int state) {
         try {
@@ -617,8 +569,6 @@
      * @param ringCallPreciseState ringCall state.
      * @param foregroundCallPreciseState foreground call state.
      * @param backgroundCallPreciseState background call state.
-     *
-     * @hide
      */
     public void notifyPreciseCallState(int subId, int slotIndex,
         @PreciseCallStates int ringCallPreciseState,
@@ -642,10 +592,9 @@
      * @param cause {@link DisconnectCause} for the disconnected call.
      * @param preciseCause {@link android.telephony.PreciseDisconnectCause} for the disconnected
      * call.
-     *
-     * @hide
      */
-    public void notifyDisconnectCause(int slotIndex, int subId, int cause, int preciseCause) {
+    public void notifyDisconnectCause(int slotIndex, int subId, @DisconnectCauses int cause,
+            @PreciseDisconnectCauses int preciseCause) {
         try {
             sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause);
         } catch (RemoteException ex) {
@@ -658,10 +607,8 @@
      *
      * <p>To be compatible with {@link TelephonyRegistry}, use {@link CellIdentity} which is
      * parcelable, and convert to CellLocation in client code.
-     *
-     * @hide
      */
-    public void notifyCellLocation(int subId, CellIdentity cellLocation) {
+    public void notifyCellLocation(int subId, @NonNull CellIdentity cellLocation) {
         try {
             sRegistry.notifyCellLocationForSubscriber(subId, cellLocation);
         } catch (RemoteException ex) {
@@ -675,10 +622,8 @@
      *
      * @param subId for which cellinfo changed.
      * @param cellInfo A list of cellInfo associated with the given subscription.
-     *
-     * @hide
      */
-    public void notifyCellInfoChanged(int subId, List<CellInfo> cellInfo) {
+    public void notifyCellInfoChanged(int subId, @NonNull List<CellInfo> cellInfo) {
         try {
             sRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
         } catch (RemoteException ex) {
@@ -687,8 +632,8 @@
     }
 
     /**
-     * @param activeDataSubId
-     * @hide
+     * Notify that the active data subscription ID has changed.
+     * @param activeDataSubId The new subscription ID for active data
      */
     public void notifyActiveDataSubIdChanged(int activeDataSubId) {
         try {
@@ -729,4 +674,21 @@
         } catch (RemoteException ex) {
         }
     }
+
+    /**
+     * Notify {@link BarringInfo} has changed for a specific subscription.
+     *
+     * @param slotIndex for the phone object that got updated barring info.
+     * @param subId for which the BarringInfo changed.
+     * @param barringInfo updated BarringInfo.
+     */
+    public void notifyBarringInfoChanged(
+            int slotIndex, int subId, @NonNull BarringInfo barringInfo) {
+        try {
+            sRegistry.notifyBarringInfoChanged(slotIndex, subId, barringInfo);
+        } catch (RemoteException ex) {
+            // system server crash
+        }
+    }
+
 }
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 248e321..8e8409d 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -21,7 +21,6 @@
 import libcore.timezone.ZoneInfoDb;
 import libcore.util.ZoneInfo;
 
-import java.io.IOException;
 import java.util.Locale;
 import java.util.TimeZone;
 
@@ -1106,19 +1105,14 @@
         }
 
         private static ZoneInfo lookupZoneInfo(String timezoneId) {
-            try {
-                ZoneInfo zoneInfo = ZoneInfoDb.getInstance().makeTimeZone(timezoneId);
-                if (zoneInfo == null) {
-                    zoneInfo = ZoneInfoDb.getInstance().makeTimeZone("GMT");
-                }
-                if (zoneInfo == null) {
-                    throw new AssertionError("GMT not found: \"" + timezoneId + "\"");
-                }
-                return zoneInfo;
-            } catch (IOException e) {
-                // This should not ever be thrown.
-                throw new AssertionError("Error loading timezone: \"" + timezoneId + "\"", e);
+            ZoneInfo zoneInfo = ZoneInfoDb.getInstance().makeTimeZone(timezoneId);
+            if (zoneInfo == null) {
+                zoneInfo = ZoneInfoDb.getInstance().makeTimeZone("GMT");
             }
+            if (zoneInfo == null) {
+                throw new AssertionError("GMT not found: \"" + timezoneId + "\"");
+            }
+            return zoneInfo;
         }
 
         public void switchTimeZone(String timezone) {
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 6931a32..aa53468 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -303,7 +303,7 @@
 
         if ((mask & WEB_URLS) != 0) {
             gatherLinks(links, text, Patterns.AUTOLINK_WEB_URL,
-                new String[] { "http://", "https://", "rtsp://" },
+                new String[] { "http://", "https://", "rtsp://", "ftp://" },
                 sUrlMatchFilter, null);
         }
 
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 50cd7b1..7ad16ff 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -301,7 +301,7 @@
     private static final String DOMAIN_NAME_STR = "(" + HOST_NAME + "|" + IP_ADDRESS_STRING + ")";
     public static final Pattern DOMAIN_NAME = Pattern.compile(DOMAIN_NAME_STR);
 
-    private static final String PROTOCOL = "(?i:http|https|rtsp)://";
+    private static final String PROTOCOL = "(?i:http|https|rtsp|ftp)://";
 
     /* A word boundary or end of input.  This is to stop foo.sure from matching as foo.su */
     private static final String WORD_BOUNDARY = "(?:\\b|$|^)";
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 3a6c8dd..489307b 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -189,12 +189,12 @@
     }
 
     /**
-     * Create a Surface assosciated with a given {@link SurfaceControl}. Buffers submitted to this
+     * Create a Surface associated with a given {@link SurfaceControl}. Buffers submitted to this
      * surface will be displayed by the system compositor according to the parameters
      * specified by the control. Multiple surfaces may be constructed from one SurfaceControl,
      * but only one can be connected (e.g. have an active EGL context) at a time.
      *
-     * @param from The SurfaceControl to assosciate this Surface with
+     * @param from The SurfaceControl to associate this Surface with
      */
     public Surface(@NonNull SurfaceControl from) {
         copyFrom(from);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index fcd8127..4f7beda 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2259,7 +2259,7 @@
         }
 
         /**
-         * Specify how the buffer assosciated with this Surface is mapped in to the
+         * Specify how the buffer associated with this Surface is mapped in to the
          * parent coordinate space. The source frame will be scaled to fit the destination
          * frame, after being rotated according to the orientation parameter.
          *
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fe60bba..40dfcfc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25801,9 +25801,9 @@
 
         /**
          * Returns the View object that had been passed to the
-         * {@link #View.DragShadowBuilder(View)}
+         * {@link #DragShadowBuilder(View)}
          * constructor.  If that View parameter was {@code null} or if the
-         * {@link #View.DragShadowBuilder()}
+         * {@link #DragShadowBuilder()}
          * constructor was used to instantiate the builder object, this method will return
          * null.
          *
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d9d9278..859b137 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -614,7 +614,6 @@
          * @see #TYPE_TOAST
          * @see #TYPE_SYSTEM_OVERLAY
          * @see #TYPE_PRIORITY_PHONE
-         * @see #TYPE_STATUS_BAR_PANEL
          * @see #TYPE_SYSTEM_DIALOG
          * @see #TYPE_KEYGUARD_DIALOG
          * @see #TYPE_SYSTEM_ERROR
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e671708..d395f52 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -92,10 +92,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -425,13 +422,6 @@
     int mCursorCandEnd;
 
     /**
-     * Initial startInput with {@link StartInputReason.WINDOW_FOCUS_GAIN} is executed
-     * in a background thread. Later, if there is an actual startInput it will wait on
-     * main thread till the background thread completes.
-     */
-    private CompletableFuture<Void> mWindowFocusGainFuture;
-
-    /**
      * The instance that has previously been sent to the input method.
      */
     private CursorAnchorInfo mCursorAnchorInfo = null;
@@ -655,14 +645,14 @@
                             } catch (RemoteException e) {
                             }
                         }
-                    }
-                    // Check focus again in case that "onWindowFocus" is called before
-                    // handling this message.
-                    if (mServedView != null && canStartInput(mServedView)) {
-                        if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
-                            final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
-                                    : StartInputReason.DEACTIVATED_BY_IMMS;
-                            startInputInner(reason, null, 0, 0, 0);
+                        // Check focus again in case that "onWindowFocus" is called before
+                        // handling this message.
+                        if (mServedView != null && canStartInput(mServedView)) {
+                            if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
+                                final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
+                                        : StartInputReason.DEACTIVATED_BY_IMMS;
+                                startInputInner(reason, null, 0, 0, 0);
+                            }
                         }
                     }
                     return;
@@ -1225,10 +1215,6 @@
      */
     void clearBindingLocked() {
         if (DEBUG) Log.v(TAG, "Clearing binding!");
-        if (mWindowFocusGainFuture != null) {
-            mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
-            mWindowFocusGainFuture = null;
-        }
         clearConnectionLocked();
         setInputChannelLocked(null);
         mBindSequence = -1;
@@ -1612,18 +1598,6 @@
     boolean startInputInner(@StartInputReason int startInputReason,
             @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags,
             @SoftInputModeFlags int softInputMode, int windowFlags) {
-        if (startInputReason != StartInputReason.WINDOW_FOCUS_GAIN
-                && mWindowFocusGainFuture != null) {
-            try {
-                mWindowFocusGainFuture.get();
-            } catch (ExecutionException | InterruptedException e) {
-                // do nothing
-            } catch (CancellationException e) {
-                // window no longer has focus.
-                return true;
-            }
-        }
-
         final View view;
         synchronized (mH) {
             view = mServedView;
@@ -1977,38 +1951,31 @@
             startInputFlags |= StartInputFlags.FIRST_WINDOW_FOCUS_GAIN;
         }
 
-        final boolean forceNewFocus1 = forceNewFocus;
-        final int startInputFlags1 = startInputFlags;
-        if (mWindowFocusGainFuture != null) {
-            mWindowFocusGainFuture.cancel(false/* mayInterruptIfRunning */);
+        if (checkFocusNoStartInput(forceNewFocus)) {
+            // We need to restart input on the current focus view.  This
+            // should be done in conjunction with telling the system service
+            // about the window gaining focus, to help make the transition
+            // smooth.
+            if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
+                    startInputFlags, softInputMode, windowFlags)) {
+                return;
+            }
         }
-        mWindowFocusGainFuture = CompletableFuture.runAsync(() -> {
-            if (checkFocusNoStartInput(forceNewFocus1)) {
-                // We need to restart input on the current focus view.  This
-                // should be done in conjunction with telling the system service
-                // about the window gaining focus, to help make the transition
-                // smooth.
-                if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
-                        startInputFlags1, softInputMode, windowFlags)) {
-                    return;
-                }
-            }
 
-            // For some reason we didn't do a startInput + windowFocusGain, so
-            // we'll just do a window focus gain and call it a day.
-            synchronized (mH) {
-                try {
-                    if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
-                    mService.startInputOrWindowGainedFocus(
-                            StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
-                            rootView.getWindowToken(), startInputFlags1, softInputMode, windowFlags,
-                            null, null, 0 /* missingMethodFlags */,
-                            rootView.getContext().getApplicationInfo().targetSdkVersion);
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
+        // For some reason we didn't do a startInput + windowFocusGain, so
+        // we'll just do a window focus gain and call it a day.
+        synchronized (mH) {
+            try {
+                if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
+                mService.startInputOrWindowGainedFocus(
+                        StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+                        rootView.getWindowToken(), startInputFlags, softInputMode, windowFlags,
+                        null, null, 0 /* missingMethodFlags */,
+                        rootView.getContext().getApplicationInfo().targetSdkVersion);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
             }
-        });
+        }
     }
 
     /** @hide */
@@ -2023,10 +1990,6 @@
                 // If the mCurRootView is losing window focus, release the strong reference to it
                 // so as not to prevent it from being garbage-collected.
                 mCurRootView = null;
-                if (mWindowFocusGainFuture != null) {
-                    mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
-                    mWindowFocusGainFuture = null;
-                }
             } else {
                 if (DEBUG) {
                     Log.v(TAG, "Ignoring onPreWindowFocus()."
diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS
new file mode 100644
index 0000000..244cc30
--- /dev/null
+++ b/core/java/android/view/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include ../../../../../services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/android/view/inspector/StaticInspectionCompanionProvider.java b/core/java/android/view/inspector/StaticInspectionCompanionProvider.java
index 42a892d..903fc13 100644
--- a/core/java/android/view/inspector/StaticInspectionCompanionProvider.java
+++ b/core/java/android/view/inspector/StaticInspectionCompanionProvider.java
@@ -21,8 +21,6 @@
 
 /**
  * An inspection companion provider that finds companions as inner classes or generated code.
- *
- * @see android.processor.view.inspector.PlatformInspectableProcessor
  */
 public class StaticInspectionCompanionProvider implements InspectionCompanionProvider {
     /**
diff --git a/core/java/android/webkit/DateSorter.java b/core/java/android/webkit/DateSorter.java
index fede244..90d44db 100644
--- a/core/java/android/webkit/DateSorter.java
+++ b/core/java/android/webkit/DateSorter.java
@@ -19,11 +19,11 @@
 import android.content.Context;
 import android.content.res.Resources;
 
+import com.android.icu.text.DateSorterBridge;
+
 import java.util.Calendar;
 import java.util.Locale;
 
-import libcore.icu.LocaleData;
-
 /**
  * Sorts dates into the following groups:
  *   Today
@@ -69,9 +69,9 @@
         if (locale == null) {
             locale = Locale.getDefault();
         }
-        LocaleData localeData = LocaleData.get(locale);
-        mLabels[0] = localeData.today;
-        mLabels[1] = localeData.yesterday;
+        DateSorterBridge dateSorterBridge = DateSorterBridge.createInstance(locale);
+        mLabels[0] = dateSorterBridge.getToday();
+        mLabels[1] = dateSorterBridge.getYesterday();
 
         int resId = com.android.internal.R.plurals.last_num_days;
         String format = resources.getQuantityString(resId, NUM_DAYS_AGO);
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 9cbb035..f44f24a 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.annotation.InterpolatorRes;
 import android.annotation.NonNull;
@@ -246,6 +248,8 @@
 
     private AccessibilityEventSender mAccessibilityEventSender;
 
+    private ObjectAnimator mLastProgressAnimator;
+
     /**
      * Create a new progress bar with range 0...100 and initial progress of 0.
      * @param context the application environment
@@ -1544,8 +1548,19 @@
             animator.setAutoCancel(true);
             animator.setDuration(PROGRESS_ANIM_DURATION);
             animator.setInterpolator(PROGRESS_ANIM_INTERPOLATOR);
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mLastProgressAnimator = null;
+                }
+            });
             animator.start();
+            mLastProgressAnimator = animator;
         } else {
+            if (isPrimary && mLastProgressAnimator != null) {
+                mLastProgressAnimator.cancel();
+                mLastProgressAnimator = null;
+            }
             setVisualProgress(id, scale);
         }
 
diff --git a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
index 9aee879f..ef8d018 100644
--- a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
+++ b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
@@ -50,6 +50,16 @@
     public static final String ACTION_UCE_SERVICE_DOWN =
                                         "com.android.ims.internal.uce.UCE_SERVICE_DOWN";
 
+    /**
+     * Uce Service status received in IUceListener.setStatus() callback
+     */
+    public static final int UCE_SERVICE_STATUS_FAILURE = 0;
+    /** indicate UI to call Presence/Options API.   */
+    public static final int UCE_SERVICE_STATUS_ON = 1;
+    /** Indicate UI destroy Presence/Options   */
+    public static final int UCE_SERVICE_STATUS_CLOSED = 2;
+    /** Service up and trying to register for network events  */
+    public static final int UCE_SERVICE_STATUS_READY = 3;
 
     /**
      * Gets the instance of UCE Manager
diff --git a/core/java/com/android/internal/accessibility/OWNERS b/core/java/com/android/internal/accessibility/OWNERS
new file mode 100644
index 0000000..b3c09e9
--- /dev/null
+++ b/core/java/com/android/internal/accessibility/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 44214
+svetoslavganov@google.com
+pweaver@google.com
+qasid@google.com
diff --git a/core/java/com/android/internal/app/SystemUserHomeActivity.java b/core/java/com/android/internal/app/SystemUserHomeActivity.java
index 26fbf6f..ee936a3 100644
--- a/core/java/com/android/internal/app/SystemUserHomeActivity.java
+++ b/core/java/com/android/internal/app/SystemUserHomeActivity.java
@@ -17,10 +17,27 @@
 package com.android.internal.app;
 
 import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.R;
 
 /**
  * Placeholder home activity, which is always installed on the system user. At least one home
  * activity must be present and enabled in order for the system to boot.
  */
 public class SystemUserHomeActivity extends Activity {
+    private static final String TAG = "SystemUserHome";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        setContentView(R.layout.system_user_home);
+    }
+
+    protected void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "onDestroy");
+    }
 }
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index ab890d2..9ba0259 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -93,6 +93,43 @@
         dest.writeString(mDescription);
     }
 
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("CompatibilityChangeInfo(")
+                .append(getId());
+        if (getName() != null) {
+            sb.append("; name=").append(getName());
+        }
+        if (getEnableAfterTargetSdk() != -1) {
+            sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk());
+        }
+        if (getDisabled()) {
+            sb.append("; disabled");
+        }
+        if (getLoggingOnly()) {
+            sb.append("; loggingOnly");
+        }
+        return sb.append(")").toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || !(o instanceof CompatibilityChangeInfo)) {
+            return false;
+        }
+        CompatibilityChangeInfo that = (CompatibilityChangeInfo) o;
+        return this.mChangeId == that.mChangeId
+                && this.mName.equals(that.mName)
+                && this.mEnableAfterTargetSdk == that.mEnableAfterTargetSdk
+                && this.mDisabled == that.mDisabled
+                && this.mLoggingOnly == that.mLoggingOnly
+                && this.mDescription.equals(that.mDescription);
+
+    }
+
     public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR =
             new Parcelable.Creator<CompatibilityChangeInfo>() {
 
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 523ed6f..6408def 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -222,6 +222,14 @@
     CompatibilityChangeInfo[] listAllChanges();
 
     /**
+    * List the compatibility changes that should be present in the UI.
+    * Filters out certain changes like e.g. logging only.
+    *
+    * @return An array of {@link CompatChangeInfo}.
+    */
+    CompatibilityChangeInfo[] listUIChanges();
+
+    /**
      * Get an instance that can determine whether a changeid can be overridden for a package name.
      */
     IOverrideValidator getOverrideValidator();
diff --git a/core/java/com/android/internal/compat/OWNERS b/core/java/com/android/internal/compat/OWNERS
index 2b7cdb0..cfd0a4b 100644
--- a/core/java/com/android/internal/compat/OWNERS
+++ b/core/java/com/android/internal/compat/OWNERS
@@ -2,6 +2,5 @@
 platform-compat-eng+reviews@google.com
 
 andreionea@google.com
-atrost@google.com
 mathewi@google.com
 satayev@google.com
diff --git a/core/java/com/android/internal/inputmethod/OWNERS b/core/java/com/android/internal/inputmethod/OWNERS
new file mode 100644
index 0000000..fc0e5d4
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include ../../../../../../services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 23b1ab5..8ea5aa8 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.Ikev2VpnProfile;
+import android.net.PlatformVpnProfile;
 import android.net.ProxyInfo;
 import android.os.Build;
 import android.os.Parcel;
@@ -131,17 +132,23 @@
      * delimiters) are not present in the algorithm names. See {@link #validateAllowedAlgorithms()}
      */
     private List<String> mAllowedAlgorithms = new ArrayList<>(); // 19
-    public boolean isBypassable = false;                       // 20
-    public boolean isMetered = false;                          // 21
-    public int maxMtu = 1400;                                  // 22
-    public boolean areAuthParamsInline = false;                   // 23
+    public boolean isBypassable = false;                         // 20
+    public boolean isMetered = false;                            // 21
+    public int maxMtu = PlatformVpnProfile.MAX_MTU_DEFAULT;      // 22
+    public boolean areAuthParamsInline = false;                  // 23
+    public final boolean isRestrictedToTestNetworks;             // 24
 
     // Helper fields.
     @UnsupportedAppUsage
     public transient boolean saveLogin = false;
 
     public VpnProfile(String key) {
+        this(key, false);
+    }
+
+    public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
         this.key = key;
+        this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
     }
 
     @UnsupportedAppUsage
@@ -170,6 +177,7 @@
         isMetered = in.readBoolean();
         maxMtu = in.readInt();
         areAuthParamsInline = in.readBoolean();
+        isRestrictedToTestNetworks = in.readBoolean();
     }
 
     /**
@@ -219,6 +227,7 @@
         out.writeBoolean(isMetered);
         out.writeInt(maxMtu);
         out.writeBoolean(areAuthParamsInline);
+        out.writeBoolean(isRestrictedToTestNetworks);
     }
 
     /**
@@ -236,12 +245,21 @@
             String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
             // Acceptable numbers of values are:
             // 14-19: Standard profile, with option for serverCert, proxy
-            // 24: Standard profile with serverCert, proxy and platform-VPN parameters.
-            if ((values.length < 14 || values.length > 19) && values.length != 24) {
+            // 24: Standard profile with serverCert, proxy and platform-VPN parameters
+            // 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks
+            if ((values.length < 14 || values.length > 19)
+                    && values.length != 24 && values.length != 25) {
                 return null;
             }
 
-            VpnProfile profile = new VpnProfile(key);
+            final boolean isRestrictedToTestNetworks;
+            if (values.length >= 25) {
+                isRestrictedToTestNetworks = Boolean.parseBoolean(values[24]);
+            } else {
+                isRestrictedToTestNetworks = false;
+            }
+
+            VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks);
             profile.name = values[0];
             profile.type = Integer.parseInt(values[1]);
             if (profile.type < 0 || profile.type > TYPE_MAX) {
@@ -282,6 +300,8 @@
                 profile.areAuthParamsInline = Boolean.parseBoolean(values[23]);
             }
 
+            // isRestrictedToTestNetworks (values[24]) assigned as part of the constructor
+
             profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
             return profile;
         } catch (Exception e) {
@@ -329,6 +349,7 @@
         builder.append(VALUE_DELIMITER).append(isMetered);
         builder.append(VALUE_DELIMITER).append(maxMtu);
         builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
+        builder.append(VALUE_DELIMITER).append(isRestrictedToTestNetworks);
 
         return builder.toString().getBytes(StandardCharsets.UTF_8);
     }
@@ -420,7 +441,8 @@
         return Objects.hash(
             key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
             l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
-            proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline);
+            proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline,
+            isRestrictedToTestNetworks);
     }
 
     /** Checks VPN profiles for interior equality. */
@@ -452,7 +474,8 @@
                 && isBypassable == other.isBypassable
                 && isMetered == other.isMetered
                 && maxMtu == other.maxMtu
-                && areAuthParamsInline == other.areAuthParamsInline;
+                && areAuthParamsInline == other.areAuthParamsInline
+                && isRestrictedToTestNetworks == other.isRestrictedToTestNetworks;
     }
 
     @NonNull
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index a35e42e..8a5b02a 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -108,6 +108,7 @@
                 NETWORK_STATUS,
                 context.getString(R.string.notification_channel_network_status),
                 NotificationManager.IMPORTANCE_LOW);
+        network.setBlockableSystem(true);
         channelsList.add(network);
 
         final NotificationChannel networkAlertsChannel = new NotificationChannel(
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5d50582..401933a 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5421,7 +5421,7 @@
         if (mVideoOnNesting > 0) {
             final long elapsedRealtime = mClocks.elapsedRealtime();
             final long uptime = mClocks.uptimeMillis();
-            mAudioOnNesting = 0;
+            mVideoOnNesting = 0;
             mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
                     + Integer.toHexString(mHistoryCur.states));
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index 8182d60..8b659f9 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -18,6 +18,7 @@
 
 import android.os.SystemProperties;
 import android.sysprop.CryptoProperties;
+import android.sysprop.HdmiProperties;
 
 /**
  * This is a cache of various ro.* properties so that they can be read just once
@@ -37,16 +38,7 @@
      * mode is off.
      */
     public static final boolean CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF =
-            SystemProperties.getBoolean(
-                    "ro.hdmi.cec_audio_device_forward_volume_keys_system_audio_mode_off", false);
-
-    /**
-     * Property to indicate if the current device is a cec switch device.
-     *
-     * <p> Default is false.
-     */
-    public static final String PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH =
-            "ro.hdmi.property_is_device_hdmi_cec_switch";
+            HdmiProperties.forward_volume_keys_when_system_audio_mode_off().orElse(false);
 
     // ------ ro.config.* -------- //
     public static final boolean CONFIG_AVOID_GFX_ACCEL =
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index d047415..f3c3ac1 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -983,4 +983,16 @@
      */
     @FastNative
     public static native int nativeParseSigChld(byte[] in, int length, int[] out);
+
+    /**
+     * Returns whether the hardware supports memory tagging (ARM MTE).
+     */
+    public static native boolean nativeSupportsMemoryTagging();
+
+    /**
+     * Returns whether the kernel supports tagged pointers. Present in the
+     * Android Common Kernel from 4.14 and up. By default, you should prefer
+     * fully-feature Memory Tagging, rather than the static Tagged Pointers.
+     */
+    public static native boolean nativeSupportsTaggedPointers();
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 300f71a..741a65e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -122,12 +122,6 @@
 
     private static boolean sPreloadComplete;
 
-    /**
-     * Cached classloader to use for the system server. Will only be populated in the system
-     * server process.
-     */
-    private static ClassLoader sCachedSystemServerClassLoader = null;
-
     static void preload(TimingsTraceLog bootTimingsTraceLog) {
         Log.d(TAG, "begin preload");
         bootTimingsTraceLog.traceBegin("BeginPreload");
@@ -499,13 +493,7 @@
 
         final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
         if (systemServerClasspath != null) {
-            if (performSystemServerDexOpt(systemServerClasspath)) {
-                // Throw away the cached classloader. If we compiled here, the classloader would
-                // not have had AoT-ed artifacts.
-                // Note: This only works in a very special environment where selinux enforcement is
-                // disabled, e.g., Mac builds.
-                sCachedSystemServerClassLoader = null;
-            }
+            performSystemServerDexOpt(systemServerClasspath);
             // Capturing profiles is only supported for debug or eng builds since selinux normally
             // prevents it.
             if (shouldProfileSystemServer() && (Build.IS_USERDEBUG || Build.IS_ENG)) {
@@ -537,9 +525,10 @@
 
             throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
         } else {
-            createSystemServerClassLoader();
-            ClassLoader cl = sCachedSystemServerClassLoader;
-            if (cl != null) {
+            ClassLoader cl = null;
+            if (systemServerClasspath != null) {
+                cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);
+
                 Thread.currentThread().setContextClassLoader(cl);
             }
 
@@ -555,24 +544,6 @@
     }
 
     /**
-     * Create the classloader for the system server and store it in
-     * {@link sCachedSystemServerClassLoader}. This function may be called through JNI in
-     * system server startup, when the runtime is in a critically low state. Do not do
-     * extended computation etc here.
-     */
-    private static void createSystemServerClassLoader() {
-        if (sCachedSystemServerClassLoader != null) {
-            return;
-        }
-        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
-        // TODO: Should we run optimization here?
-        if (systemServerClasspath != null) {
-            sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath,
-                    VMRuntime.SDK_VERSION_CUR_DEVELOPMENT);
-        }
-    }
-
-    /**
      * Note that preparing the profiles for system server does not require special selinux
      * permissions. From the installer perspective the system server is a regular package which can
      * capture profile information.
@@ -636,16 +607,15 @@
 
     /**
      * Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction
-     * set of the current runtime. If something was compiled, return true.
+     * set of the current runtime.
      */
-    private static boolean performSystemServerDexOpt(String classPath) {
+    private static void performSystemServerDexOpt(String classPath) {
         final String[] classPathElements = classPath.split(":");
         final IInstalld installd = IInstalld.Stub
                 .asInterface(ServiceManager.getService("installd"));
         final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
 
         String classPathForElement = "";
-        boolean compiledSomething = false;
         for (String classPathElement : classPathElements) {
             // We default to the verify filter because the compilation will happen on /data and
             // system server cannot load executable code outside /system.
@@ -686,7 +656,6 @@
                             uuid, classLoaderContext, seInfo, false /* downgrade */,
                             targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
                             "server-dexopt");
-                    compiledSomething = true;
                 } catch (RemoteException | ServiceSpecificException e) {
                     // Ignore (but log), we need this on the classpath for fallback mode.
                     Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -697,8 +666,6 @@
             classPathForElement = encodeSystemServerClassPath(
                     classPathForElement, classPathElement);
         }
-
-        return compiledSomething;
     }
 
     /**
@@ -781,9 +748,15 @@
             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 (Zygote.nativeSupportsMemoryTagging()) {
+                /* The system server is more privileged than regular app processes, so it has async
+                 * tag checks enabled on hardware that supports memory tagging. */
+                parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+            } else if (Zygote.nativeSupportsTaggedPointers()) {
+                /* Enable pointer tagging in the system server. Hardware support for this is present
+                 * in all ARMv8 CPUs. */
+                parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+            }
 
             if (shouldProfileSystemServer()) {
                 parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index d15f480..b2c5a99 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -16,11 +16,12 @@
 
 package com.android.internal.telephony;
 
+import android.telephony.BarringInfo;
 import android.telephony.CallAttributes;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.DataConnectionRealTimeInfo;
-import android.telephony.DisplayInfo;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
@@ -54,7 +55,7 @@
     void onOemHookRawEvent(in byte[] rawData);
     void onCarrierNetworkChange(in boolean active);
     void onUserMobileDataStateChanged(in boolean enabled);
-    void onDisplayInfoChanged(in DisplayInfo displayInfo);
+    void onDisplayInfoChanged(in TelephonyDisplayInfo telephonyDisplayInfo);
     void onPhoneCapabilityChanged(in PhoneCapability capability);
     void onActiveDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
@@ -66,4 +67,5 @@
     void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo);
     void onRegistrationFailed(in CellIdentity cellIdentity,
              String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
+    void onBarringInfoChanged(in BarringInfo barringInfo);
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 3176f96..6957ec6 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -19,10 +19,11 @@
 import android.content.Intent;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
+import android.telephony.BarringInfo;
 import android.telephony.CallQuality;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
-import android.telephony.DisplayInfo;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.PhoneCapability;
 import android.telephony.PhysicalChannelConfig;
@@ -88,7 +89,7 @@
     void notifyOpportunisticSubscriptionInfoChanged();
     void notifyCarrierNetworkChange(in boolean active);
     void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
-    void notifyDisplayInfoChanged(int slotIndex, int subId, in DisplayInfo displayInfo);
+    void notifyDisplayInfoChanged(int slotIndex, int subId, in TelephonyDisplayInfo telephonyDisplayInfo);
     void notifyPhoneCapabilityChanged(in PhoneCapability capability);
     void notifyActiveDataSubIdChanged(int activeDataSubId);
     void notifyRadioPowerStateChanged(in int phoneId, in int subId, in int state);
@@ -102,4 +103,5 @@
     void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo);
     void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity,
             String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
+    void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo);
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 872f26d1..9bc4adc 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -326,4 +326,10 @@
         // GraphicsJNI.h includes hwui headers
         "libhwui",
     ],
+
+    product_variables: {
+        experimental_mte: {
+            cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
+        },
+    },
 }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a2a6716..544340e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -342,6 +342,8 @@
 }
 
 void AndroidRuntime::setArgv0(const char* argv0, bool setProcName) {
+    // Set the kernel's task name, for as much of the name as we can fit.
+    // The kernel's TASK_COMM_LEN minus one for the terminating NUL == 15.
     if (setProcName) {
         int len = strlen(argv0);
         if (len < 15) {
@@ -350,8 +352,14 @@
             pthread_setname_np(pthread_self(), argv0 + len - 15);
         }
     }
+
+    // Directly change the memory pointed to by argv[0].
     memset(mArgBlockStart, 0, mArgBlockLength);
     strlcpy(mArgBlockStart, argv0, mArgBlockLength);
+
+    // Let bionic know that we just did that, because __progname points
+    // into argv[0] (https://issuetracker.google.com/152893281).
+    setprogname(mArgBlockStart);
 }
 
 status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
@@ -685,6 +693,7 @@
     char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX];
     char extraOptsBuf[PROPERTY_VALUE_MAX];
     char voldDecryptBuf[PROPERTY_VALUE_MAX];
+    char perfettoHprofOptBuf[sizeof("-XX:PerfettoHprof=") + PROPERTY_VALUE_MAX];
     enum {
       kEMDefault,
       kEMIntPortable,
@@ -799,6 +808,16 @@
     addOption("-verbose:gc");
     //addOption("-verbose:class");
 
+    // On Android, we always want to allow loading the PerfettoHprof plugin.
+    // Even with this option set, we will still only actually load the plugin
+    // if we are on a userdebug build or the app is debuggable or profileable.
+    // This is enforced in art/runtime/runtime.cc.
+    //
+    // We want to be able to disable this, because this does not work on host,
+    // and we do not want to enable it in tests.
+    parseRuntimeOption("dalvik.vm.perfetto_hprof", perfettoHprofOptBuf, "-XX:PerfettoHprof=",
+                       "true");
+
     if (primary_zygote) {
         addOption("-Xprimaryzygote");
     }
@@ -1285,12 +1304,11 @@
 {
     if (mExitWithoutCleanup) {
         ALOGI("VM exiting with result code %d, cleanup skipped.", code);
-        ::_exit(code);
     } else {
         ALOGI("VM exiting with result code %d.", code);
         onExit(code);
-        ::exit(code);
     }
+    ::_exit(code);
 }
 
 void AndroidRuntime::onVmCreated(JNIEnv* env)
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 3f05c3b..f040f11 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -1,5 +1,11 @@
 #define LOG_TAG "BitmapFactory"
 
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
 #include "BitmapFactory.h"
 #include "CreateJavaOutputStreamAdaptor.h"
 #include "GraphicsJNI.h"
@@ -21,10 +27,6 @@
 #include <androidfw/ResourceTypes.h>
 #include <cutils/compiler.h>
 #include <memory>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
 
 jfieldID gOptions_justBoundsFieldID;
 jfieldID gOptions_sampleSizeFieldID;
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 98162af..e7f123e 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
+#include <sys/stat.h>
+
 #include "Bitmap.h"
 #include "BitmapFactory.h"
 #include "ByteBufferStreamAdaptor.h"
@@ -33,7 +36,6 @@
 
 #include <androidfw/Asset.h>
 #include <jni.h>
-#include <sys/stat.h>
 
 using namespace android;
 
diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp
index 10c3026..913952f 100644
--- a/core/jni/android/graphics/pdf/PdfEditor.cpp
+++ b/core/jni/android/graphics/pdf/PdfEditor.cpp
@@ -111,7 +111,7 @@
         jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) {
     FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
 
-    FPDF_PAGE* page = (FPDF_PAGE*) FPDF_LoadPage(document, pageIndex);
+    FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
     if (!page) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "cannot open page");
diff --git a/core/jni/android_app_admin_SecurityLog.cpp b/core/jni/android_app_admin_SecurityLog.cpp
index b3bcaa0..e5a13db 100644
--- a/core/jni/android_app_admin_SecurityLog.cpp
+++ b/core/jni/android_app_admin_SecurityLog.cpp
@@ -41,7 +41,7 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out);
+    SLog::readEvents(env, ANDROID_LOG_NONBLOCK, 0, out);
 }
 
 static void android_app_admin_SecurityLog_readEventsSince(JNIEnv* env, jobject /* clazz */,
@@ -52,7 +52,7 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out);
+    SLog::readEvents(env, ANDROID_LOG_NONBLOCK, timestamp, out);
 }
 
 static void android_app_admin_SecurityLog_readPreviousEvents(JNIEnv* env, jobject /* clazz */,
@@ -62,7 +62,7 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out);
+    SLog::readEvents(env, ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out);
 }
 
 static void android_app_admin_SecurityLog_readEventsOnWrapping(JNIEnv* env, jobject /* clazz */,
@@ -72,8 +72,7 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp,
-            out);
+    SLog::readEvents(env, ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp, out);
 }
 
 /*
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index bd4862d..d4369d4 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -78,12 +78,13 @@
     return 0;
   }
 
+  auto dup_fd_id = dup_fd.get();
   std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd),
                                                                       friendly_name_utf8.c_str(),
                                                                       system, force_shared_lib);
   if (apk_assets == nullptr) {
     std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d",
-                                               friendly_name_utf8.c_str(), dup_fd.get());
+                                               friendly_name_utf8.c_str(), dup_fd_id);
     jniThrowException(env, "java/io/IOException", error_msg.c_str());
     return 0;
   }
diff --git a/core/jni/android_media_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp
index 17a02b2..34be2a5 100644
--- a/core/jni/android_media_AudioProductStrategies.cpp
+++ b/core/jni/android_media_AudioProductStrategies.cpp
@@ -85,10 +85,23 @@
     jStrategyId = static_cast<jint>(strategy.getId());
 
     // Audio Attributes Group array
-    std::map<int, std::vector<AudioAttributes> > groups;
+    int attrGroupIndex = 0;
+    std::map<int /**attributesGroupIndex*/, std::vector<AudioAttributes> > groups;
     for (const auto &attr : strategy.getAudioAttributes()) {
-        int attrGroupId = attr.getGroupId();
-        groups[attrGroupId].push_back(attr);
+        int groupId = attr.getGroupId();
+        int streamType = attr.getStreamType();
+        const auto &iter = std::find_if(begin(groups), end(groups),
+                                        [groupId, streamType](const auto &iter) {
+            const auto &frontAttr = iter.second.front();
+            return frontAttr.getGroupId() == groupId && frontAttr.getStreamType() == streamType;
+        });
+        // Same Volume Group Id and same stream type
+        if (iter != end(groups)) {
+             groups[iter->first].push_back(attr);
+        } else {
+            // Add a new Group of AudioAttributes for this product strategy
+            groups[attrGroupIndex++].push_back(attr);
+        }
     }
     numAttributesGroups = groups.size();
 
@@ -97,7 +110,7 @@
     for (const auto &iter : groups) {
         std::vector<AudioAttributes> audioAttributesGroups = iter.second;
         jint numAttributes = audioAttributesGroups.size();
-        jint jGroupId = iter.first;
+        jint jGroupId = audioAttributesGroups.front().getGroupId();
         jint jLegacyStreamType = audioAttributesGroups.front().getStreamType();
 
         jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes);
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 236ee61..7634547 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -15,6 +15,10 @@
  */
 
 #define LOG_TAG "SELinuxJNI"
+
+#include <fcntl.h>
+#include <errno.h>
+
 #include <utils/Log.h>
 
 #include <nativehelper/JNIHelp.h>
@@ -22,7 +26,6 @@
 #include "core_jni_helpers.h"
 #include "selinux/selinux.h"
 #include "selinux/android.h"
-#include <errno.h>
 #include <memory>
 #include <atomic>
 #include <nativehelper/ScopedLocalRef.h>
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index de30773..7e36b80 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 3b5a144..0a5e786 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -44,7 +44,7 @@
         return;
     }
 
-    ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out);
+    ELog::readEvents(env, ANDROID_LOG_NONBLOCK, tags, 0, out);
  }
 /*
  * In class android.util.EventLog:
@@ -60,8 +60,7 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, tags,
-            timestamp, out);
+    ELog::readEvents(env, ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, tags, timestamp, out);
 }
 
 /*
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index f929dde..66d2c37 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -641,7 +641,7 @@
         return -1;
     }
 
-    return si.totalram;
+    return static_cast<jlong>(si.totalram) * si.mem_unit;
 }
 
 /*
diff --git a/core/jni/com_android_internal_os_AtomicDirectory.cpp b/core/jni/com_android_internal_os_AtomicDirectory.cpp
index 76b0fc1..112aa78 100644
--- a/core/jni/com_android_internal_os_AtomicDirectory.cpp
+++ b/core/jni/com_android_internal_os_AtomicDirectory.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
+
 #include <nativehelper/ScopedUtfChars.h>
 #include "jni.h"
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 0ce61de..18be374 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -51,6 +51,7 @@
 #include <paths.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <sys/auxv.h>
 #include <sys/capability.h>
 #include <sys/cdefs.h>
 #include <sys/eventfd.h>
@@ -72,6 +73,8 @@
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <bionic/malloc.h>
+#include <bionic/mte.h>
+#include <bionic/mte_kernel.h>
 #include <cutils/fs.h>
 #include <cutils/multiuser.h>
 #include <private/android_filesystem_config.h>
@@ -113,15 +116,11 @@
 
 static pid_t gSystemServerPid = 0;
 
-static constexpr const char* kZygoteClassName = "com/android/internal/os/Zygote";
+static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
 static jclass gZygoteClass;
 static jmethodID gCallPostForkSystemServerHooks;
 static jmethodID gCallPostForkChildHooks;
 
-static constexpr const char* kZygoteInitClassName = "com/android/internal/os/ZygoteInit";
-static jclass gZygoteInitClass;
-static jmethodID gCreateSystemServerClassLoader;
-
 static bool gIsSecurityEnforced = true;
 
 /**
@@ -317,6 +316,8 @@
   PROFILE_FROM_SHELL = 1 << 15,
   MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
   MEMORY_TAG_LEVEL_TBI = 1 << 19,
+  MEMORY_TAG_LEVEL_ASYNC = 2 << 19,
+  MEMORY_TAG_LEVEL_SYNC = 3 << 19,
 };
 
 enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -1062,6 +1063,28 @@
   return pid;
 }
 
+#ifdef ANDROID_EXPERIMENTAL_MTE
+static void SetTagCheckingLevel(int level) {
+#ifdef __aarch64__
+  if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) {
+    return;
+  }
+
+  int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+  if (tagged_addr_ctrl < 0) {
+    ALOGE("prctl(PR_GET_TAGGED_ADDR_CTRL) failed: %s", strerror(errno));
+    return;
+  }
+
+  tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | level;
+  if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
+    ALOGE("prctl(PR_SET_TAGGED_ADDR_CTRL, %d) failed: %s", tagged_addr_ctrl,
+          strerror(errno));
+  }
+#endif
+}
+#endif
+
 // Utility routine to specialize a zygote child process.
 static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
                              jint runtime_flags, jobjectArray rlimits,
@@ -1177,7 +1200,23 @@
     case RuntimeFlags::MEMORY_TAG_LEVEL_TBI:
       heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
       break;
+    case RuntimeFlags::MEMORY_TAG_LEVEL_ASYNC:
+#ifdef ANDROID_EXPERIMENTAL_MTE
+      SetTagCheckingLevel(PR_MTE_TCF_ASYNC);
+#endif
+      heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
+      break;
+    case RuntimeFlags::MEMORY_TAG_LEVEL_SYNC:
+#ifdef ANDROID_EXPERIMENTAL_MTE
+      SetTagCheckingLevel(PR_MTE_TCF_SYNC);
+#endif
+      // TODO(pcc): Use SYNC here once the allocator supports it.
+      heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
+      break;
     default:
+#ifdef ANDROID_EXPERIMENTAL_MTE
+      SetTagCheckingLevel(PR_MTE_TCF_NONE);
+#endif
       heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
   }
   android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
@@ -1221,15 +1260,6 @@
       fail_fn("Error calling post fork system server hooks.");
     }
 
-    // Prefetch the classloader for the system server. This is done early to
-    // allow a tie-down of the proper system server selinux domain.
-    env->CallStaticVoidMethod(gZygoteInitClass, gCreateSystemServerClassLoader);
-    if (env->ExceptionCheck()) {
-      // Be robust here. The Java code will attempt to create the classloader
-      // at a later point (but may not have rights to use AoT artifacts).
-      env->ExceptionClear();
-    }
-
     // TODO(oth): Remove hardcoded label here (b/117874058).
     static const char* kSystemServerLabel = "u:r:system_server:s0";
     if (selinux_android_setcon(kSystemServerLabel) != 0) {
@@ -1861,6 +1891,23 @@
     return -1;
 }
 
+static jboolean com_android_internal_os_Zygote_nativeSupportsMemoryTagging(JNIEnv* env, jclass) {
+#if defined(__aarch64__)
+  return mte_supported();
+#else
+  return false;
+#endif
+}
+
+static jboolean com_android_internal_os_Zygote_nativeSupportsTaggedPointers(JNIEnv* env, jclass) {
+#ifdef __aarch64__
+  int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+  return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
+#else
+  return false;
+#endif
+}
+
 static const JNINativeMethod gMethods[] = {
     { "nativeForkAndSpecialize",
       "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z)I",
@@ -1898,6 +1945,10 @@
       (void* ) com_android_internal_os_Zygote_nativeBoostUsapPriority },
     {"nativeParseSigChld", "([BI[I)I",
       (void* ) com_android_internal_os_Zygote_nativeParseSigChld},
+    { "nativeSupportsMemoryTagging", "()Z",
+      (void *) com_android_internal_os_Zygote_nativeSupportsMemoryTagging },
+    {"nativeSupportsTaggedPointers", "()Z",
+     (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
@@ -1908,13 +1959,6 @@
   gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
                                                    "(IZZLjava/lang/String;)V");
 
-  gZygoteInitClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteInitClassName));
-  gCreateSystemServerClassLoader = GetStaticMethodIDOrDie(env, gZygoteInitClass,
-                                                          "createSystemServerClassLoader",
-                                                          "()V");
-
-  RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
-
-  return JNI_OK;
+  return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
 }
 }  // namespace android
diff --git a/core/jni/eventlog_helper.h b/core/jni/eventlog_helper.h
index 3a05195..29c023a 100644
--- a/core/jni/eventlog_helper.h
+++ b/core/jni/eventlog_helper.h
@@ -24,7 +24,7 @@
 #include <android-base/macros.h>
 #include <log/log_event_list.h>
 
-#include <log/log.h>
+#include <log/log_read.h>
 
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index ca4735e..8fc1758 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -35,6 +35,7 @@
 static const char* kPathWhitelist[] = {
         "/apex/com.android.conscrypt/javalib/conscrypt.jar",
         "/apex/com.android.ipsec/javalib/ike.jar",
+        "/apex/com.android.i18n/javalib/core-icu4j.jar",
         "/apex/com.android.media/javalib/updatable-media.jar",
         "/apex/com.android.sdkext/javalib/framework-sdkextensions.jar",
         "/apex/com.android.tethering/javalib/framework-tethering.jar",
@@ -81,11 +82,18 @@
   }
 
   // Framework jars are allowed.
-  static const char* kFrameworksPrefix = "/system/framework/";
+  static const char* kFrameworksPrefix[] = {
+          "/system/framework/",
+          "/system_ext/framework/",
+  };
+
   static const char* kJarSuffix = ".jar";
-  if (android::base::StartsWith(path, kFrameworksPrefix)
-      && android::base::EndsWith(path, kJarSuffix)) {
-    return true;
+
+  for (const auto& frameworks_prefix : kFrameworksPrefix) {
+    if (android::base::StartsWith(path, frameworks_prefix)
+        && android::base::EndsWith(path, kJarSuffix)) {
+      return true;
+    }
   }
 
   // Jars from the ART APEX are allowed.
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 3d99b72..829e1f7 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -905,13 +905,8 @@
         optional SettingProto storage_full_threshold_bytes = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto storage_cache_percentage = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto storage_cache_max_bytes = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        // System VDSO global setting. This links to the "sys.vdso" system property.
-        // The following values are supported:
-        // false  -> both 32 and 64 bit vdso disabled
-        // 32     -> 32 bit vdso enabled
-        // 64     -> 64 bit vdso enabled
-        // Any other value defaults to both 32 bit and 64 bit true.
-        optional SettingProto vdso = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Used to be sys_vdso
+        reserved 7;
         // UidCpuPower global setting. This links the sys.uidcpupower system property.
         // The following values are supported:
         // 0 -> /proc/uid_cpupower/* are disabled
diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto
index 15ef122..b17d12c 100644
--- a/core/proto/android/stats/dnsresolver/dns_resolver.proto
+++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto
@@ -43,6 +43,7 @@
     RC_EAI_BADHINTS = 12;
     RC_EAI_PROTOCOL = 13;
     RC_EAI_OVERFLOW = 14;
+    RC_RESOLV_INTERNAL_ERROR = 254;
     RC_RESOLV_TIMEOUT = 255;
     RC_EAI_MAX = 256;
 }
@@ -61,6 +62,13 @@
     NS_R_NOTAUTH = 9;   // Not authoritative for zone
     NS_R_NOTZONE = 10;  // Zone of record different from zone section
     NS_R_MAX = 11;
+    // Define rcode=12~15(UNASSIGNED) in rcode enum type.
+    // Some DNS Servers might return undefined code to devices.
+    // Without the enum definition, that would be noise for our dashboard.
+    NS_R_UNASSIGNED12 = 12; // Unassigned
+    NS_R_UNASSIGNED13 = 13; // Unassigned
+    NS_R_UNASSIGNED14 = 14; // Unassigned
+    NS_R_UNASSIGNED15 = 15; // Unassigned
     // The following are EDNS extended rcodes
     NS_R_BADVERS = 16;
     // The following are TSIG errors
@@ -183,6 +191,8 @@
     NT_BLUETOOTH_VPN = 10;
     // Indicates this network uses an Ethernet+VPN transport.
     NT_ETHERNET_VPN = 11;
+    // Indicates this network uses a Wi-Fi+Cellular+VPN transport.
+    NT_WIFI_CELLULAR_VPN = 12;
 }
 
 enum CacheStatus{
@@ -197,6 +207,144 @@
     CS_SKIP = 3;
 }
 
+// The enum LinuxErrno is defined in the following 2 files.
+// 1. bionic/libc/kernel/uapi/asm-generic/errno-base.h
+// 2. bionic/libc/kernel/uapi/asm-generic/errno.h
+enum LinuxErrno {
+    SYS_NO_ERROR = 0;
+    SYS_EPERM = 1;              // Not super-user
+    SYS_ENOENT = 2;             // No such file or directory
+    SYS_ESRCH = 3;              // No such process
+    SYS_EINTR = 4;              // Interrupted system call
+    SYS_EIO = 5;                // I/O error
+    SYS_ENXIO = 6;              // No such device or address
+    SYS_E2BIG = 7;              // Arg list too long
+    SYS_ENOEXEC = 8;            // Exec format error
+    SYS_EBADF = 9;              // Bad file number
+    SYS_ECHILD = 10;            // No children
+    SYS_EAGAIN = 11;            // No more processes
+    SYS_ENOMEM = 12;            // Not enough core
+    SYS_EACCES = 13;            // Permission denied
+    SYS_EFAULT = 14;            // Bad address
+    SYS_ENOTBLK = 15;           // Block device required
+    SYS_EBUSY = 16;             // Mount device busy
+    SYS_EEXIST = 17;            // File exists
+    SYS_EXDEV = 18;             // Cross-device link
+    SYS_ENODEV = 19;            // No such device
+    SYS_ENOTDIR = 20;           // Not a directory
+    SYS_EISDIR = 21;            // Is a directory
+    SYS_EINVAL = 22;            // Invalid argument
+    SYS_ENFILE = 23;            // Too many open files in system
+    SYS_EMFILE = 24;            // Too many open files
+    SYS_ENOTTY = 25;            // Not a typewriter
+    SYS_ETXTBSY = 26;           // Text file busy
+    SYS_EFBIG = 27;             // File too large
+    SYS_ENOSPC = 28;            // No space left on device
+    SYS_ESPIPE = 29;            // Illegal seek
+    SYS_EROFS = 30;             // Read only file system
+    SYS_EMLINK = 31;            // Too many links
+    SYS_EPIPE = 32;             // Broken pipe
+    SYS_EDOM = 33;              // Math arg out of domain of func
+    SYS_ERANGE = 34;            // Math result not representable
+    SYS_EDEADLOCK = 35;         // File locking deadlock error
+    SYS_ENAMETOOLONG = 36;      // File or path name too long
+    SYS_ENOLCK = 37;            // No record locks available
+    SYS_ENOSYS = 38;            // Function not implemented
+    SYS_ENOTEMPTY = 39;         // Directory not empty
+    SYS_ELOOP = 40;             // Too many symbolic links
+    SYS_ENOMSG = 42;            // No message of desired type
+    SYS_EIDRM = 43;             // Identifier removed
+    SYS_ECHRNG = 44;            // Channel number out of range
+    SYS_EL2NSYNC = 45;          // Level 2 not synchronized
+    SYS_EL3HLT = 46;            // Level 3 halted
+    SYS_EL3RST = 47;            // Level 3 reset
+    SYS_ELNRNG = 48;            // Link number out of range
+    SYS_EUNATCH = 49;           // rotocol driver not attached
+    SYS_ENOCSI = 50;            // No CSI structure available
+    SYS_EL2HLT = 51;            // Level 2 halted
+    SYS_EBADE = 52;             // Invalid exchange
+    SYS_EBADR = 53;             // Invalid request descriptor
+    SYS_EXFULL = 54;            // Exchange full
+    SYS_ENOANO = 55;            // No anode
+    SYS_EBADRQC = 56;           // Invalid request code
+    SYS_EBADSLT = 57;           // Invalid slot
+    SYS_EBFONT = 59;            // Bad font file fmt
+    SYS_ENOSTR = 60;            // Device not a stream
+    SYS_ENODATA = 61;           // No data (for no delay io)
+    SYS_ETIME = 62;             // Timer expired
+    SYS_ENOSR = 63;             // Out of streams resources
+    SYS_ENONET = 64;            // Machine is not on the network
+    SYS_ENOPKG = 65;            // Package not installed
+    SYS_EREMOTE = 66;           // The object is remote
+    SYS_ENOLINK = 67;           // The link has been severed
+    SYS_EADV = 68;              // Advertise error
+    SYS_ESRMNT = 69;            // Srmount error
+    SYS_ECOMM = 70;             // Communication error on send
+    SYS_EPROTO = 71;            // Protocol error
+    SYS_EMULTIHOP = 72;         // Multihop attempted
+    SYS_EDOTDOT = 73;           // Cross mount point (not really error)
+    SYS_EBADMSG = 74;           // Trying to read unreadable message
+    SYS_EOVERFLOW = 75;         // Value too large for defined data type
+    SYS_ENOTUNIQ = 76;          // Given log. name not unique
+    SYS_EBADFD = 77;            // f.d. invalid for this operation
+    SYS_EREMCHG = 78;           // Remote address changed
+    SYS_ELIBACC = 79;           // Can't access a needed shared lib
+    SYS_ELIBBAD = 80;           // Accessing a corrupted shared lib
+    SYS_ELIBSCN = 81;           // .lib section in a.out corrupted
+    SYS_ELIBMAX = 82;           // Attempting to link in too many libs
+    SYS_ELIBEXEC = 83;          // Attempting to exec a shared library
+    SYS_EILSEQ = 84;
+    SYS_ERESTART = 85;
+    SYS_ESTRPIPE = 86;
+    SYS_EUSERS = 87;
+    SYS_ENOTSOCK = 88;          // Socket operation on non-socket
+    SYS_EDESTADDRREQ = 89;      // Destination address required
+    SYS_EMSGSIZE = 90;          // Message too long
+    SYS_EPROTOTYPE = 91;        // Protocol wrong type for socket
+    SYS_ENOPROTOOPT = 92;       // Protocol not available
+    SYS_EPROTONOSUPPORT = 93;   // Unknown protocol
+    SYS_ESOCKTNOSUPPORT = 94;   // Socket type not supported
+    SYS_EOPNOTSUPP = 95;        // Operation not supported on transport endpoint
+    SYS_EPFNOSUPPORT = 96;      // Protocol family not supported
+    SYS_EAFNOSUPPORT = 97;      // Address family not supported by protocol family
+    SYS_EADDRINUSE = 98;        // Address already in use
+    SYS_EADDRNOTAVAIL = 99;     // Address not available
+    SYS_ENETDOWN = 100;         // Network interface is not configured
+    SYS_ENETUNREACH = 101;      // Network is unreachable
+    SYS_ENETRESET = 102;
+    SYS_ECONNABORTED = 103;     // Connection aborted
+    SYS_ECONNRESET = 104;       // Connection reset by peer
+    SYS_ENOBUFS = 105;          // No buffer space available
+    SYS_EISCONN = 106;          // Socket is already connected
+    SYS_ENOTCONN = 107;         // Socket is not connected
+    SYS_ESHUTDOWN = 108;        // Can't send after socket shutdown
+    SYS_ETOOMANYREFS = 109;
+    SYS_ETIMEDOUT = 110;        // Connection timed out
+    SYS_ECONNREFUSED = 111;     // Connection refused
+    SYS_EHOSTDOWN = 112;        // Host is down
+    SYS_EHOSTUNREACH = 113;     // Host is unreachable
+    SYS_EALREADY = 114;         // Socket already connected
+    SYS_EINPROGRESS = 115;      // Connection already in progress
+    SYS_ESTALE = 116;
+    SYS_EUCLEAN = 117;
+    SYS_ENOTNAM = 118;
+    SYS_ENAVAIL = 119;
+    SYS_EISNAM = 120;
+    SYS_EREMOTEIO = 121;
+    SYS_EDQUOT = 122;
+    SYS_ENOMEDIUM = 123;        // No medium (in tape drive)
+    SYS_EMEDIUMTYPE = 124;
+    SYS_ECANCELED = 125;
+    SYS_ENOKEY = 126;
+    SYS_EKEYEXPIRED = 127;
+    SYS_EKEYREVOKED = 128;
+    SYS_EKEYREJECTED = 129;
+    SYS_EOWNERDEAD = 130;
+    SYS_ENOTRECOVERABLE = 131;
+    SYS_ERFKILL = 132;
+    SYS_EHWPOISON = 133;
+}
+
 message DnsQueryEvent {
     optional android.stats.dnsresolver.NsRcode rcode = 1;
 
@@ -218,6 +366,8 @@
     optional bool connected = 8;
 
     optional int32 latency_micros = 9;
+
+    optional android.stats.dnsresolver.LinuxErrno linux_errno = 10;
 }
 
 message DnsQueryEvents {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9817ceb..1a2dfce 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1101,13 +1101,12 @@
          grants your app this permission. If you don't need this permission, be sure your <a
          href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
          targetSdkVersion}</a> is 4 or higher.
-         <p>Protection level: dangerous
+         <p>Protection level: normal
     -->
     <permission android:name="android.permission.READ_PHONE_STATE"
-        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_readPhoneState"
         android:description="@string/permdesc_readPhoneState"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="normal" />
 
     <!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
          granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
@@ -1645,6 +1644,10 @@
     <permission android:name="android.permission.NETWORK_FACTORY"
                 android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Allows applications to access network stats provider -->
+    <permission android:name="android.permission.NETWORK_STATS_PROVIDER"
+                android:protectionLevel="signature" />
+
     <!-- Allows Settings and SystemUI to call methods in Networking services
          <p>Not for use by third-party or privileged applications.
          @SystemApi @TestApi
@@ -1794,7 +1797,7 @@
          and bypass OMAPI AccessControlEnforcer.
          <p>Not for use by third-party applications.
          @hide -->
-    <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED"
+    <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION"
         android:protectionLevel="signature|privileged" />
 
     <!-- @deprecated This permission used to allow too broad access to sensitive methods and all its
@@ -2093,7 +2096,7 @@
     <permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi Allows listen permission to always reported signal strength.
+    <!-- Allows listen permission to always reported signal strength.
          @hide Used internally. -->
     <permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH"
         android:protectionLevel="signature|telephony" />
diff --git a/packages/Shell/res/layout/null_home_finishing_boot.xml b/core/res/res/layout/system_user_home.xml
similarity index 93%
rename from packages/Shell/res/layout/null_home_finishing_boot.xml
rename to core/res/res/layout/system_user_home.xml
index 5f9563a..8afa423 100644
--- a/packages/Shell/res/layout/null_home_finishing_boot.xml
+++ b/core/res/res/layout/system_user_home.xml
@@ -30,9 +30,9 @@
         <TextView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textSize="40sp"
+            android:textSize="20sp"
             android:textColor="?android:attr/textColorPrimary"
-            android:text="@*android:string/android_start_title"/>
+            android:text="Framework Fallback Home"/>
         <ProgressBar
             style="@android:style/Widget.Material.ProgressBar.Horizontal"
             android:layout_width="match_parent"
diff --git a/core/res/res/values-mcc334-mnc020/config.xml b/core/res/res/values-mcc334-mnc020/config.xml
index 0970517..82b3ee6 100644
--- a/core/res/res/values-mcc334-mnc020/config.xml
+++ b/core/res/res/values-mcc334-mnc020/config.xml
@@ -18,4 +18,7 @@
 -->
 <resources>
     <bool name="config_use_sim_language_file">false</bool>
-</resources>
\ No newline at end of file
+
+    <bool name="config_pdp_reject_enable_retry">true</bool>
+    <integer name="config_pdp_reject_retry_delay_ms">45000</integer>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020/strings.xml b/core/res/res/values-mcc334-mnc020/strings.xml
new file mode 100644
index 0000000..91b560a
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="config_pdp_reject_dialog_title"></string>
+    <string name="config_pdp_reject_user_authentication_failed">AUTHENTICATION FAILURE -29-.</string>
+    <string name="config_pdp_reject_service_not_subscribed">NOT SUBSCRIBED TO SERVICE -33-.</string>
+    <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed">Multiple PDN connections for a given APN not allowed -55-.</string>
+</resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 6707947..dca9c72 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -165,7 +165,7 @@
         <item>中文 (繁體)</item>
     </string-array>
 
-    <array name="simColors">
+    <array name="sim_colors">
         <item>@color/Teal_700</item>
         <item>@color/Blue_700</item>
         <item>@color/Indigo_700</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2f30194..b908ab8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4742,7 +4742,7 @@
              May be a string value, which is a comma-separated language tag list, such as "ja-JP,zh-CN".
              When not specified or an empty string is given, it will fallback to the default one.
              {@see android.os.LocaleList#forLanguageTags(String)}
-             {@see android.text.TextView#setTextLocales(android.os.LocaleList)} -->
+             {@see android.widget.TextView#setTextLocales(android.os.LocaleList)} -->
         <attr name="textLocale" format="string" />
         <!-- Text color for links. -->
         <attr name="textColorLink" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d48c56c..6551b28b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -419,13 +419,14 @@
     <!-- Regex of wired ethernet ifaces -->
     <string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
 
-
-
     <!-- Configuration of Ethernet interfaces in the following format:
          <interface name|mac address>;[Network Capabilities];[IP config];[Override Transport]
          Where
                [Network Capabilities] Optional. A comma seprated list of network capabilities.
-                   Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.
+                   Values must be from NetworkCapabilities#NET_CAPABILITY_* constants.
+                   The NOT_ROAMING, NOT_CONGESTED and NOT_SUSPENDED capabilities are always
+                   added automatically because this configuration provides no way to update
+                   them dynamically.
                [IP config] Optional. If empty or not specified - DHCP will be used, otherwise
                    use the following format to specify static IP configuration:
                        ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
@@ -451,10 +452,6 @@
         -->
     </string-array>
 
-    <!-- Package name for the default CellBroadcastService module [DO NOT TRANSLATE] -->
-    <string name="cellbroadcast_default_package" translatable="false">com.android.cellbroadcastservice
-    </string>
-
     <!-- If the mobile hotspot feature requires provisioning, a package name and class name
         can be provided to launch a supported application that provisions the devices.
 
@@ -3049,6 +3046,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" />
 
@@ -3574,10 +3575,9 @@
 
     <!-- Do not translate. Mcc codes whose existence trigger the presence of emergency
          affordances-->
-    <integer-array name="config_emergency_mcc_codes" translatable="false">
-        <item>404</item>
-        <item>405</item>
-    </integer-array>
+    <string-array name="config_emergency_iso_country_codes" translatable="false">
+        <item>in</item>
+    </string-array>
 
     <!-- Package name for the device provisioning package. -->
     <string name="config_deviceProvisioningPackage"></string>
@@ -4364,4 +4364,9 @@
     <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
          check after reboot or airplane mode toggling -->
     <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>
+
+    <!-- pdp data retry for cause 29, 33 and 55-->
+    <bool name="config_pdp_reject_enable_retry">false</bool>
+    <!--pdp data reject retry delay in ms-->
+    <integer name="config_pdp_reject_retry_delay_ms">-1</integer>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 93080e9..fb54566 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3010,8 +3010,6 @@
     </public-group>
 
     <public-group type="string" first-id="0x01040025">
-      <!-- @hide @SystemApi -->
-      <public name="low_memory" />
     </public-group>
 
     <public-group type="bool" first-id="0x01110005">
@@ -3023,17 +3021,6 @@
     <public-group type="color" first-id="0x0106001d">
     </public-group>
 
-    <public-group type="array" first-id="0x01070006">
-      <!-- @hide @SystemApi -->
-      <public name="simColors" />
-      <!-- @hide @SystemApi -->
-      <public name="config_restrictedPreinstalledCarrierApps" />
-      <!-- @hide @SystemApi -->
-      <public name="config_sms_enabled_single_shift_tables" />
-      <!-- @hide @SystemApi -->
-      <public name="config_sms_enabled_locking_shift_tables" />
-    </public-group>
-
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8810ac4..12fc1bb 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3450,8 +3450,6 @@
     <!-- A notification is shown when connected network without internet due to private dns validation failed. This is the notification's message. [CHAR LIMIT=NONE] -->
     <string name="private_dns_broken_detailed">Private DNS server cannot be accessed</string>
 
-    <!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] -->
-    <string name="captive_portal_logged_in_detailed">Connected</string>
     <!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's title. [CHAR LIMIT=50] -->
     <string name="network_partial_connectivity"><xliff:g id="network_ssid" example="GoogleGuest">%1$s</xliff:g> has limited connectivity</string>
 
@@ -5423,4 +5421,242 @@
     <!-- ChooserActivity - Alphabetically sorted apps list label. [CHAR LIMIT=NONE] -->
     <string name="chooser_all_apps_button_label">Apps list</string>
 
+    <!-- Icc depersonalization related strings -->
+    <!-- Label text for PIN entry widget on SIM Network Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY">SIM network unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM Network Subset Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY">SIM network subset unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM Corporate Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY">SIM corporate unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM Service Provider Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY">SIM service provider unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM SIM Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SIM_ENTRY">SIM unlock PIN</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for Subset PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for Corporate PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for SIM service provider PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for SIM PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SIM_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PIN entry widget on RUIM Network1 Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_ENTRY">RUIM network1 unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM Network2 Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_ENTRY">RUIM network2 unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM Hrpd Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_ENTRY">RUIM hrpd unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM Corporate Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY">RUIM corporate unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM Service Provider Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY">RUIM service provider unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM RUIM Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY">RUIM unlock PIN</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ENTRY">Enter PUK</string>
+
+    <!-- Label text for PIN entry widget on SIM SPN Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SPN_ENTRY">SPN unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM SP EHPLMN Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY">SP Equivalent Home PLMN unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM ICCID Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_ICCID_ENTRY">ICCID unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM IMPI Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_IMPI_ENTRY">IMPI unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM NS_SP Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NS_SP_ENTRY">Network subset service provider unlock PIN</string>
+
+    <!-- Status message displayed on SIM Network Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_IN_PROGRESS">Requesting SIM network unlock\u2026</string>
+    <!-- Status message displayed on SIM Network Subset Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_IN_PROGRESS">Requesting SIM network subset unlock
+\u2026</string>
+    <!-- Status message displayed on SIM Service Provider Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_IN_PROGRESS">Requesting SIM service provider un
+lock\u2026</string>
+    <!-- Status message displayed on SIM Corporate Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_IN_PROGRESS">Requesting SIM corporate unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on Corporate PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on SIM Service provider PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on SIM PUK entry widget on Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SIM_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on SIM SIM Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SIM_IN_PROGRESS">Requesting SIM unlock\u2026</string>
+    <!-- Status message displayed on RUIM Network1 Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_IN_PROGRESS">Requesting RUIM network1 unlock\u2026</string>
+    <!-- Status message displayed on RUIM Network2 Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_IN_PROGRESS">Requesting RUIM network2 unlock\u2026</string>
+    <!-- Status message displayed on RUIM Hrpd Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_IN_PROGRESS">Requesting RUIM hrpd unlock\u2026</string>
+    <!-- Status message displayed on RUIM Service Provider Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_IN_PROGRESS">Requesting RUIM service provider
+unlock\u2026</string>
+    <!-- Status message displayed on RUIM Corporate Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_IN_PROGRESS">Requesting RUIM corporate unlock\u2026</string>
+
+    <!-- Status message displayed on SIM SPN Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SPN_IN_PROGRESS">Requesting SPN unlock\u2026</string>
+    <!-- Status message displayed on SIM SP EHPLMN Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_IN_PROGRESS">Requesting SP Equivalent Home PLMN unlock\u2026</string>
+    <!-- Status message displayed on SIM ICCID Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_ICCID_IN_PROGRESS">Requesting ICCID unlock\u2026</string>
+    <!-- Status message displayed on SIM IMPI Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_IMPI_IN_PROGRESS">Requesting IMPI unlock\u2026</string>
+    <!-- Status message displayed on SIM NS_SP Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NS_SP_IN_PROGRESS">Requesting Network subset service provider unlock\u2026</string>
+
+    <!-- Status message displayed on RUIM RUIM Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_IN_PROGRESS">Requesting RUIM unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Error message displayed on SIM Network Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_ERROR">SIM Network unlock request unsuccessful.</string>
+    <!-- Error message displayed on SIM Network Subset Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR">SIM Network Subset unlock request unsucces
+sful.</string>
+    <!-- Error message displayed on SIM Service Provider Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR">SIM Service Provider unlock request unsu
+ccessful.</string>
+    <!-- Error message displayed on SIM Corporate Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_ERROR">SIM Corporate unlock request unsuccessful.</string>
+    <!-- Error message displayed on SIM SIM Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SIM_ERROR">SIM unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Network1 Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_ERROR">RUIM Network1 unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Network2 Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_ERROR">RUIM Network2 unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Hrpd Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_ERROR">RUIM Hrpd unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Corporate Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_ERROR">RUIM Corporate unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Service Provider Depersonalization panel  [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR">RUIM Service Provider unlock request un
+successful.</string>
+    <!-- Error message displayed on RUIM RUIM Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_ERROR">RUIM unlock request unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SIM_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ERROR">PUK unlock unsuccessful.</string>
+
+    <!--  Error message displayed on SIM SPN Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SPN_ERROR">SPN unlock request unsuccessful.</string>
+    <!--  Error message displayed on SIM SP EHPLMN Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR">SP Equivalent Home PLMN unlock request unsuccessful.</string>
+    <!--  Error message displayed on SIM ICCID Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_ICCID_ERROR">ICCID unlock request unsuccessful.</string>
+    <!--  Error message displayed on SIM IMPI Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_IMPI_ERROR">IMPI unlock request unsuccessful.</string>
+    <!--  Error message displayed on SIM NS_SP Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR">Network subset service provider unlock request unsuccessful.</string>
+
+    <!-- Success message displayed on SIM Network Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUCCESS">SIM Network unlock successful.</string>
+    <!-- Success message displayed on SIM Network Subset Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_SUCCESS">SIM Network Subset unlock successful.</string>
+    <!-- Success message displayed on SIM Service Provider Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_SUCCESS">SIM Service Provider unlock successful
+.</string>
+    <!-- Success message displayed on SIM Corporate Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_SUCCESS">SIM Corporate unlock successful.</string>
+    <!-- Success message displayed on SIM SIM Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SIM_SUCCESS">SIM unlock successful.</string>
+    <!-- Success message displayed on RUIM Network1 Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_SUCCESS">RUIM Network1 unlock successful.</string>
+    <!-- Success message displayed on RUIM Network2 Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_SUCCESS">RUIM Network2 unlock successful.</string>
+    <!-- Success message displayed on RUIM Hrpd Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_SUCCESS">RUIM Hrpd unlock successful.</string>
+    <!-- Success message displayed on RUIM Service Provider Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_SUCCESS">RUIM Service Provider unlock successf
+ul.</string>
+    <!-- Success message displayed on RUIM Corporate Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS">RUIM Corporate unlock successful.</string>
+    <!-- Success message displayed on RUIM RUIM Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_SUCCESS">RUIM unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SIM_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_SUCCESS">PUK unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_SUCCESS">PUK unlock successful.</string>
+
+    <!-- Success message displayed on SIM SPN Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SPN_SUCCESS">SPN unlock successful.</string>
+    <!-- Success message displayed on SIM SP EHPLMN Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_SUCCESS">SP Equivalent Home PLMN unlock successful.</string>
+    <!-- Success message displayed on SIM ICCID Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_ICCID_SUCCESS">ICCID unlock successful.</string>
+    <!-- Success message displayed on SIM IMPI Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_IMPI_SUCCESS">IMPI unlock successful.</string>
+    <!-- Success message displayed on SIM NS_SP Depersonalization panel [CHAR LIMIT=none] -->
+    <string name="PERSOSUBSTATE_SIM_NS_SP_SUCCESS">Network subset service provider unlock successful.</string>
+
+     <!-- pdp data reject dialog string for cause 29, 33 and 55-->
+    <string name="config_pdp_reject_dialog_title"></string>
+    <string name="config_pdp_reject_user_authentication_failed"></string>
+    <string name="config_pdp_reject_service_not_subscribed"></string>
+    <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed"></string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0768856..3cee340 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -735,7 +735,6 @@
   <java-symbol type="string" name="capability_title_canControlMagnification" />
   <java-symbol type="string" name="capability_desc_canPerformGestures" />
   <java-symbol type="string" name="capability_title_canPerformGestures" />
-  <java-symbol type="string" name="captive_portal_logged_in_detailed" />
   <java-symbol type="string" name="cfTemplateForwarded" />
   <java-symbol type="string" name="cfTemplateForwardedTime" />
   <java-symbol type="string" name="cfTemplateNotForwarded" />
@@ -746,7 +745,6 @@
   <java-symbol type="string" name="config_ethernet_iface_regex" />
   <java-symbol type="array" name="config_ethernet_interfaces" />
   <java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
-  <java-symbol type="string" name="cellbroadcast_default_package" />
   <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
   <java-symbol type="string" name="config_mms_user_agent" />
   <java-symbol type="string" name="config_mms_user_agent_profile_url" />
@@ -1304,7 +1302,7 @@
   <java-symbol type="array" name="preloaded_color_state_lists" />
   <java-symbol type="array" name="preloaded_drawables" />
   <java-symbol type="array" name="preloaded_freeform_multi_window_drawables" />
-  <java-symbol type="array" name="simColors" />
+  <java-symbol type="array" name="sim_colors" />
   <java-symbol type="array" name="special_locale_codes" />
   <java-symbol type="array" name="special_locale_names" />
   <java-symbol type="array" name="supported_locales" />
@@ -1547,6 +1545,7 @@
   <java-symbol type="layout" name="select_dialog" />
   <java-symbol type="layout" name="simple_dropdown_hint" />
   <java-symbol type="layout" name="status_bar_latest_event_content" />
+  <java-symbol type="layout" name="system_user_home" />
   <java-symbol type="layout" name="text_edit_action_popup_text" />
   <java-symbol type="layout" name="text_drag_thumbnail" />
   <java-symbol type="layout" name="typing_filter" />
@@ -2635,6 +2634,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" />
@@ -3168,7 +3168,7 @@
   <java-symbol type="string" name="global_action_emergency" />
   <java-symbol type="string" name="config_emergency_call_number" />
   <java-symbol type="string" name="config_emergency_dialer_package" />
-  <java-symbol type="array" name="config_emergency_mcc_codes" />
+  <java-symbol type="array" name="config_emergency_iso_country_codes" />
 
   <java-symbol type="string" name="config_dozeDoubleTapSensorType" />
   <java-symbol type="string" name="config_dozeTapSensorType" />
@@ -3871,4 +3871,12 @@
 
   <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
   <java-symbol type="bool" name="reset_geo_fencing_check_after_boot_or_apm" />
+
+  <!-- For Pdn throttle feature -->
+  <java-symbol type="bool" name="config_pdp_reject_enable_retry" />
+  <java-symbol type="integer" name="config_pdp_reject_retry_delay_ms" />
+  <java-symbol type="string" name="config_pdp_reject_dialog_title" />
+  <java-symbol type="string" name="config_pdp_reject_user_authentication_failed" />
+  <java-symbol type="string" name="config_pdp_reject_service_not_subscribed" />
+  <java-symbol type="string" name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" />
 </resources>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 70917e7..d5733e3 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -39,7 +39,7 @@
     <!-- Albania: 5 digits, known short codes listed -->
     <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
 
-    <!-- Argentia: 5 digits, known short codes listed -->
+    <!-- Argentina: 5 digits, known short codes listed -->
     <shortcode country="ar" pattern="\\d{5}" free="11711|28291" />
 
     <!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
@@ -70,7 +70,7 @@
     <shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
 
     <!-- Canada: 5-6 digits -->
-    <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" />
+    <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" free="455677" />
 
     <!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
     <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075|30047" />
diff --git a/core/tests/PlatformCompatFramework/OWNERS b/core/tests/PlatformCompatFramework/OWNERS
index 2b7cdb0..cfd0a4b 100644
--- a/core/tests/PlatformCompatFramework/OWNERS
+++ b/core/tests/PlatformCompatFramework/OWNERS
@@ -2,6 +2,5 @@
 platform-compat-eng+reviews@google.com
 
 andreionea@google.com
-atrost@google.com
 mathewi@google.com
 satayev@google.com
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 5d42915..4b42f4ae 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -268,7 +268,7 @@
         File snd_stat = new File (root_filepath + "tcp_snd");
         int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat);
         NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
-        stats.addEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
+        stats.insertEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
                 NetworkStats.TAG_NONE, rx, 0, tx, 0, 0);
         return stats;
     }
diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
index 239f971..3ebe103 100644
--- a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
+++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
@@ -54,7 +54,7 @@
             recycle.txBytes = 150000;
             recycle.txPackets = 1500;
             recycle.operations = 0;
-            mNetworkStats.addEntry(recycle);
+            mNetworkStats.insertEntry(recycle);
             if (recycle.set == 1) {
                 uid++;
             }
@@ -70,7 +70,7 @@
             recycle.txBytes = 180000 * mSize;
             recycle.txPackets = 1200 * mSize;
             recycle.operations = 0;
-            mNetworkStats.addEntry(recycle);
+            mNetworkStats.insertEntry(recycle);
         }
     }
 
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
index c4ff9be..28009d4 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
@@ -129,7 +129,7 @@
     }
 
     @Test(expected = RuntimeException.class)
-    public void testBuilderValidates_emptyZone_badMatchType() {
+    public void testBuilderValidates_nullZone_badMatchType() {
         TelephonyTimeZoneSuggestion.Builder builder =
                 new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX);
         // No zone ID, so match type should be left unset.
diff --git a/core/tests/coretests/src/android/os/ExternalVibrationTest.java b/core/tests/coretests/src/android/os/ExternalVibrationTest.java
new file mode 100644
index 0000000..3b872d5
--- /dev/null
+++ b/core/tests/coretests/src/android/os/ExternalVibrationTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.os;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+
+import android.media.AudioAttributes;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ExternalVibrationTest {
+    @Test
+    public void testSerialization() {
+        AudioAttributes audio = new AudioAttributes.Builder().build();
+        IExternalVibrationController controller = mock(IExternalVibrationController.class);
+        ExternalVibration original = new ExternalVibration(
+                123, // uid
+                "pkg",
+                audio,
+                controller);
+        Parcel p = Parcel.obtain();
+        original.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        ExternalVibration restored = ExternalVibration.CREATOR.createFromParcel(p);
+        assertEquals(original, restored);
+    }
+}
+
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 7d752cd..cb91db1 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -346,6 +346,7 @@
                     Settings.Global.NETSTATS_POLL_INTERVAL,
                     Settings.Global.NETSTATS_SAMPLE_ENABLED,
                     Settings.Global.NETSTATS_AUGMENT_ENABLED,
+                    Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED,
                     Settings.Global.NETSTATS_TIME_CACHE_MAX_AGE,
                     Settings.Global.NETSTATS_UID_BUCKET_DURATION,
                     Settings.Global.NETSTATS_UID_DELETE_AGE,
@@ -453,7 +454,6 @@
                     Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                     Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES,
                     Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
-                    Settings.Global.SYS_VDSO,
                     Settings.Global.SYS_UIDCPUPOWER,
                     Settings.Global.SYS_TRACED,
                     Settings.Global.FPS_DEVISOR,
diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp
new file mode 100644
index 0000000..2194d4b
--- /dev/null
+++ b/core/tests/hdmitests/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+
+android_test {
+    name: "HdmiCecTests",
+    // Include all test java files
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "frameworks-base-testutils",
+        "truth-prebuilt",
+    ],
+    libs: ["android.test.runner"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/core/tests/hdmitests/Android.mk b/core/tests/hdmitests/Android.mk
deleted file mode 100644
index f155feb..0000000
--- a/core/tests/hdmitests/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules frameworks-base-testutils truth-prebuilt
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_PACKAGE_NAME := HdmiCecTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/packagemanagertests/Android.bp b/core/tests/packagemanagertests/Android.bp
new file mode 100644
index 0000000..6f39af8
--- /dev/null
+++ b/core/tests/packagemanagertests/Android.bp
@@ -0,0 +1,14 @@
+android_test {
+    name: "FrameworksCorePackageManagerTests",
+    // We only want this apk build for tests.
+    // Include all test java files.
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "frameworks-base-testutils",
+        "mockito-target-minus-junit4",
+    ],
+    libs: ["android.test.runner"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
deleted file mode 100644
index 8c00d14..0000000
--- a/core/tests/packagemanagertests/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.rules \
-    frameworks-base-testutils \
-    mockito-target-minus-junit4
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/privacytests/Android.bp b/core/tests/privacytests/Android.bp
new file mode 100644
index 0000000..7f56992
--- /dev/null
+++ b/core/tests/privacytests/Android.bp
@@ -0,0 +1,14 @@
+android_test {
+    name: "FrameworksPrivacyLibraryTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "junit",
+        "rappor-tests",
+        "androidx.test.rules",
+        "truth-prebuilt",
+    ],
+    libs: ["android.test.runner"],
+    platform_apis: true,
+    certificate: "platform",
+    test_suites: ["device-tests"],
+}
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
deleted file mode 100644
index 7765977..0000000
--- a/core/tests/privacytests/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests androidx.test.rules truth-prebuilt
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
new file mode 100644
index 0000000..a9b9c41
--- /dev/null
+++ b/core/tests/utiltests/Android.bp
@@ -0,0 +1,41 @@
+//########################################################################
+// Build FrameworksUtilTests package
+//########################################################################
+
+android_test {
+    name: "FrameworksUtilTests",
+
+    // We only want this apk build for tests.
+
+    // Include all test java files.
+    srcs: [
+        "src/**/*.java",
+        "src/android/util/IRemoteMemoryIntArray.aidl",
+    ],
+
+    jni_libs: [
+        "libmemoryintarraytest",
+        "libcutils",
+        "libc++",
+    ],
+
+    static_libs: [
+        "androidx.test.rules",
+        "frameworks-base-testutils",
+        "mockito-target-minus-junit4",
+        "androidx.test.ext.junit",
+    ],
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+    ],
+
+    platform_apis: true,
+
+    certificate: "platform",
+
+    test_suites: ["device-tests"],
+
+}
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
deleted file mode 100644
index 9ef73e9..0000000
--- a/core/tests/utiltests/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#########################################################################
-# Build FrameworksUtilTests package
-#########################################################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES += src/android/util/IRemoteMemoryIntArray.aidl
-
-LOCAL_JNI_SHARED_LIBRARIES := libmemoryintarraytest libcutils libc++
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.rules \
-    frameworks-base-testutils \
-    mockito-target-minus-junit4 \
-    androidx.test.ext.junit
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
-
-LOCAL_PACKAGE_NAME := FrameworksUtilTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
-
diff --git a/core/tests/utiltests/jni/Android.bp b/core/tests/utiltests/jni/Android.bp
index b0b09c2..6b75471 100644
--- a/core/tests/utiltests/jni/Android.bp
+++ b/core/tests/utiltests/jni/Android.bp
@@ -14,6 +14,7 @@
 

 cc_library_shared {

     name: "libmemoryintarraytest",

+    header_libs: ["jni_headers"],

     shared_libs: [

         "libcutils",

     ],

@@ -23,4 +24,4 @@
         "android_util_MemoryIntArrayTest.cpp",

     ],

     cflags: ["-Werror"],

-}
\ No newline at end of file
+}

diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp
index a2a2168..4b43b41 100644
--- a/core/xsd/vts/Android.bp
+++ b/core/xsd/vts/Android.bp
@@ -36,7 +36,11 @@
     ],
     test_suites: [
         "general-tests",
-        "vts-core"
+        "vts"
     ],
     test_config: "vts_permission_validate_test.xml",
 }
+
+vts_config {
+    name: "VtsValidatePermission",
+}
diff --git a/core/xsd/vts/Android.mk b/core/xsd/vts/Android.mk
deleted file mode 100644
index a5754a4..0000000
--- a/core/xsd/vts/Android.mk
+++ /dev/null
@@ -1,22 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsValidatePermission
-include test/vts/tools/build/Android.host_config.mk
diff --git a/data/etc/framework-sysconfig.xml b/data/etc/framework-sysconfig.xml
index b0d2de1..fc80b37 100644
--- a/data/etc/framework-sysconfig.xml
+++ b/data/etc/framework-sysconfig.xml
@@ -19,11 +19,30 @@
 
     <!-- Broadcast actions that are currently exempted from O+ background
          delivery restrictions. -->
-    <allow-implicit-broadcast action="android.intent.action.SIM_STATE_CHANGED" />
-    <allow-implicit-broadcast action="android.intent.action.PACKAGE_CHANGED" />
+    <allow-implicit-broadcast action="android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED" />
+    <allow-implicit-broadcast action="android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED" />
+    <allow-implicit-broadcast action="android.intent.action.DATA_SMS_RECEIVED" />
     <allow-implicit-broadcast action="android.intent.action.MEDIA_SCANNER_SCAN_FILE" />
-    <allow-implicit-broadcast action="android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION" />
+    <allow-implicit-broadcast action="android.intent.action.PACKAGE_CHANGED" />
+    <allow-implicit-broadcast action="android.intent.action.SIM_STATE_CHANGED" />
     <allow-implicit-broadcast action="android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION" />
+    <allow-implicit-broadcast action="android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION" />
+    <allow-implicit-broadcast action="android.provider.Telephony.SECRET_CODE" />
+    <allow-implicit-broadcast action="android.provider.Telephony.SMS_CB_RECEIVED" />
+    <allow-implicit-broadcast action="android.provider.Telephony.SMS_DELIVER" />
+    <allow-implicit-broadcast action="android.provider.Telephony.SMS_RECEIVED" />
+    <allow-implicit-broadcast action="android.provider.Telephony.SMS_REJECTED" />
+    <allow-implicit-broadcast action="android.provider.Telephony.WAP_PUSH_DELIVER" />
+    <allow-implicit-broadcast action="android.provider.Telephony.WAP_PUSH_RECEIVED" />
+    <allow-implicit-broadcast action="android.telephony.action.CARRIER_CONFIG_CHANGED" />
+    <allow-implicit-broadcast action="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
+    <allow-implicit-broadcast action="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
+    <allow-implicit-broadcast action="android.telephony.action.MULTI_SIM_CONFIG_CHANGED" />
+    <allow-implicit-broadcast action="android.telephony.action.SECRET_CODE" />
+    <allow-implicit-broadcast action="android.telephony.action.SIM_APPLICATION_STATE_CHANGED" />
+    <allow-implicit-broadcast action="android.telephony.action.SIM_CARD_STATE_CHANGED" />
+    <allow-implicit-broadcast action="android.telephony.action.SIM_SLOT_STATUS_CHANGED" />
+
 
     <!-- Whitelist of what components are permitted as backup data transports.  The
          'service' attribute here is a flattened ComponentName string. -->
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index d282744..e9de02e 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -57,7 +57,6 @@
   <hidden-api-whitelisted-app package="com.android.terminal" />
   <hidden-api-whitelisted-app package="com.android.wallpaper" />
   <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
-  <!-- STOPSHIP: Remove this when fixing all @hide usage for tethering.-->
-  <hidden-api-whitelisted-app package="com.android.networkstack.tethering" />
+  <!-- TODO: Remove NetworkStack whitelisting -->
   <hidden-api-whitelisted-app package="com.android.networkstack" />
 </config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index e007023..5b0ef8e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -39,11 +39,6 @@
         <permission name="android.permission.CRYPT_KEEPER"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.captiveportallogin">
-        <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
-        <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.cellbroadcastreceiver">
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
@@ -238,6 +233,7 @@
     </privapp-permissions>
 
     <privapp-permissions package="com.android.networkstack.tethering">
+        <permission name="android.permission.BLUETOOTH_PRIVILEGED" />
         <permission name="android.permission.MANAGE_USB"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
@@ -295,6 +291,7 @@
         <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
         <permission name="android.permission.MANAGE_USB"/>
         <permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
+        <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/>
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
@@ -332,6 +329,7 @@
         <permission name="android.permission.SUSPEND_APPS" />
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
         <permission name="android.permission.USE_RESERVED_DISK"/>
+        <permission name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
         <permission name="android.permission.STATUS_BAR_SERVICE"/>
@@ -358,6 +356,8 @@
         <!-- Permission required to test lights control APIs. -->
         <permission name="android.permission.CONTROL_DEVICE_LIGHTS" />
         <permission name="android.permission.REBOOT"/>
+        <!-- Permission required for testing system audio effect APIs. -->
+        <permission name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp
new file mode 100644
index 0000000..94e6a80
--- /dev/null
+++ b/data/fonts/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2011 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+prebuilt_font {
+    name: "DroidSansMono.ttf",
+    src: "DroidSansMono.ttf",
+    required: [
+        "DroidSans.ttf",
+        "DroidSans-Bold.ttf",
+    ],
+}
+
+prebuilt_font {
+    name: "AndroidClock.ttf",
+    src: "AndroidClock.ttf",
+}
+
+/////////////////////////////////
+// Copies the font configuration file into system/etc for the product as fonts.xml.
+// Additional fonts should be installed to /product/fonts/ alongside a corresponding
+// fonts_customiztion.xml in /product/etc/
+prebuilt_etc {
+    name: "fonts.xml",
+    src: "fonts.xml",
+}
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 4226e08..e22b723 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -12,9 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# We have to use BUILD_PREBUILT instead of PRODUCT_COPY_FIES,
-# because MINIMAL_FONT_FOOTPRINT is only available in Android.mks.
-
 LOCAL_PATH := $(call my-dir)
 
 ##########################################
@@ -37,61 +34,7 @@
 ##########################################
 $(eval $(call create-font-symlink,DroidSans.ttf,Roboto-Regular.ttf))
 $(eval $(call create-font-symlink,DroidSans-Bold.ttf,Roboto-Bold.ttf))
-$(eval $(call create-font-symlink,DroidSerif-Regular.ttf,NotoSerif-Regular.ttf))
-$(eval $(call create-font-symlink,DroidSerif-Bold.ttf,NotoSerif-Bold.ttf))
-$(eval $(call create-font-symlink,DroidSerif-Italic.ttf,NotoSerif-Italic.ttf))
-$(eval $(call create-font-symlink,DroidSerif-BoldItalic.ttf,NotoSerif-BoldItalic.ttf))
 
-extra_font_files := \
-    DroidSans.ttf \
-    DroidSans-Bold.ttf
-
-################################
-# Use DroidSansMono to hang extra_font_files on
-include $(CLEAR_VARS)
-LOCAL_MODULE := DroidSansMono.ttf
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts
-LOCAL_REQUIRED_MODULES := $(extra_font_files)
-include $(BUILD_PREBUILT)
-extra_font_files :=
-
-################################
-# Build the rest of font files as prebuilt.
-
-# $(1): The source file name in LOCAL_PATH.
-#       It also serves as the module name and the dest file name.
-define build-one-font-module
-$(eval include $(CLEAR_VARS))\
-$(eval LOCAL_MODULE := $(1))\
-$(eval LOCAL_SRC_FILES := $(1))\
-$(eval LOCAL_MODULE_CLASS := ETC)\
-$(eval LOCAL_MODULE_TAGS := optional)\
-$(eval LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts)\
-$(eval include $(BUILD_PREBUILT))
-endef
-
-font_src_files := \
-    AndroidClock.ttf
-
-$(foreach f, $(font_src_files), $(call build-one-font-module, $(f)))
-
-build-one-font-module :=
-font_src_files :=
-
-################################
-# Copies the font configuration file into system/etc for the product as fonts.xml.
-# Additional fonts should be installed to /product/fonts/ alongside a corresponding
-# fonts_customiztion.xml in /product/etc/
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := fonts.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_PREBUILT_MODULE_FILE := frameworks/base/data/fonts/fonts.xml
-
-include $(BUILD_PREBUILT)
 
 # Run sanity tests on fonts on checkbuild
 checkbuild: fontchain_lint
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index ae7fe6c..c3c56db 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -998,7 +998,7 @@
      * Sets the rotation value for the display list around the Z axis.
      *
      * @param rotation The rotation value of the display list, in degrees
-     * @see View#setRotationZ(float)
+     * @see View#setRotation(float)
      * @see #getRotationZ()
      * @return True if the value changed, false if the new value was the same as the previous value.
      */
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index e70529b..9cf12f1 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -617,7 +617,7 @@
      * {@link #setTintList(ColorStateList) tint}.
      * </p>
      *
-     * @see {@link #setColorFilter(ColorFilter)} }
+     * @see #setColorFilter(ColorFilter)
      * @deprecated use {@link #setColorFilter(ColorFilter)} with an instance
      * of {@link android.graphics.BlendModeColorFilter}
      */
diff --git a/graphics/java/android/graphics/fonts/FontStyle.java b/graphics/java/android/graphics/fonts/FontStyle.java
index af517d6..09799fd 100644
--- a/graphics/java/android/graphics/fonts/FontStyle.java
+++ b/graphics/java/android/graphics/fonts/FontStyle.java
@@ -217,7 +217,7 @@
      /**
       * Gets the weight value
       *
-      * @see FontStyle#setWeight(int)
+      * @see #FontStyle(int, int)
       * @return a weight value
       */
     public @IntRange(from = 0, to = 1000) int getWeight() {
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 54622c5..babcfc3 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -320,7 +320,7 @@
         /**
          * Returns the array of tab stops in pixels.
          *
-         * @see #setTabStops(float[], int)
+         * @see #setTabStops
          */
         public @Nullable float[] getTabStops() {
             return mVariableTabStops;
@@ -329,7 +329,7 @@
         /**
          * Returns the default tab stops in pixels.
          *
-         * @see #setTabStop(float[], int)
+         * @see #setTabStops
          */
         public @Px @FloatRange(from = 0) float getDefaultTabStop() {
             return mDefaultTabStop;
diff --git a/identity/java/android/security/identity/AccessControlProfileId.java b/identity/java/android/security/identity/AccessControlProfileId.java
index 3d59450..6caac0a 100644
--- a/identity/java/android/security/identity/AccessControlProfileId.java
+++ b/identity/java/android/security/identity/AccessControlProfileId.java
@@ -25,6 +25,8 @@
     /**
      * Constructs a new object holding a numerical identifier.
      *
+     * <p>The identifier must be a non-negative number and less than 32.
+     *
      * @param id the identifier.
      */
     public AccessControlProfileId(int id) {
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 1db2f63..b351b3d 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -95,9 +95,7 @@
     /**
      * Sets whether to allow using an authentication key which use count has been exceeded if no
      * other key is available. This must be called prior to calling
-     * {@link #getEntries(byte[], Map, byte[], byte[])} or using a
-     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
-     * object.
+     * {@link #getEntries(byte[], Map, byte[], byte[])}.
      *
      * By default this is set to true.
      *
@@ -123,13 +121,14 @@
      * entries.
      *
      * <p>It is the responsibility of the calling application to know if authentication is needed
-     * and use e.g. {@link android.hardware.biometrics.BiometricPrompt}) to make the user
+     * and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user
      * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which
      * references this object. If needed, this must be done before calling
      * {@link #getEntries(byte[], Map, byte[], byte[])}.
      *
-     * <p>If this method returns successfully (i.e. without throwing an exception), it must not be
-     * called again on this instance.
+     * <p>It is permissible to call this method multiple times using the same instance but if this
+     * is done, the {@code sessionTranscript} parameter must be identical for each call. If this is
+     * not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
      *
      * <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
      * from the verifier. The content can be defined in the way appropriate for the credential, byt
@@ -141,6 +140,9 @@
      *     the example below.</li>
      * </ul>
      *
+     * <p>If these requirements are not met the {@link InvalidRequestMessageException} exception
+     * is thrown.
+     *
      * <p>Here's an example of CBOR which conforms to this requirement:
      * <pre>
      *   ItemsRequest = {
@@ -149,6 +151,8 @@
      *     ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide
      *   }
      *
+     *   DocType = tstr
+     *
      *   NameSpaces = {
      *     + NameSpace => DataElements    ; Requested data elements for each NameSpace
      *   }
@@ -172,16 +176,18 @@
      *     EReaderKeyBytes
      *   ]
      *
-     *   DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
-     *   EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
+     *   DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)  ; Bytes of DeviceEngagement
+     *   EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)  ; Bytes of EReaderKey.pub
+     *
+     *   EReaderKey.Pub = COSE_Key    ; Ephemeral public key provided by reader
      * </pre>
      *
-     * <p>If the SessionTranscript is not empty, a COSE_Key structure for the public part
-     * of the key-pair previously generated by {@link #createEphemeralKeyPair()} must appear
-     * somewhere in {@code DeviceEngagement} and the X and Y coordinates must both be present
+     * <p>where a {@code COSE_Key} structure for the public part of the key-pair previously
+     * generated by {@link #createEphemeralKeyPair()} must appear somewhere in
+     * {@code DeviceEngagement} and the X and Y coordinates must both be present
      * in uncompressed form.
      *
-     * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a COSE_Sign1
+     * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1}
      * structure as defined in RFC 8152. For the payload nil shall be used and the
      * detached payload is the ReaderAuthentication CBOR described below.
      * <pre>
@@ -194,20 +200,23 @@
      *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)   ; Bytes of ItemsRequest
      * </pre>
      *
-     * <p>The public key corresponding to the key used to made signature, can be
-     * found in the {@code x5chain} unprotected header element of the COSE_Sign1
-     * structure (as as described in 'draft-ietf-cose-x509-04'). There will be at
-     * least one certificate in said element and there may be more (and if so,
+     * <p>where {@code ItemsRequestBytes} are the bytes in the {@code requestMessage} parameter.
+     *
+     * <p>The public key corresponding to the key used to make the signature, can be found in the
+     * {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as
+     * described in
+     * <a href="https://tools.ietf.org/html/draft-ietf-cose-x509-04">draft-ietf-cose-x509-04</a>).
+     * There will be at least one certificate in said element and there may be more (and if so,
      * each certificate must be signed by its successor).
      *
-     * <p>Data elements protected by reader authentication is returned if, and only if, they are
+     * <p>Data elements protected by reader authentication are returned if, and only if, they are
      * mentioned in {@code requestMessage}, {@code requestMessage} is signed by the top-most
-     * certificate in {@code readerCertificateChain}, and the data element is configured
-     * with an {@link AccessControlProfile} with a {@link X509Certificate} in
-     * {@code readerCertificateChain}.
+     * certificate in the reader's certificate chain, and the data element is configured
+     * with an {@link AccessControlProfile} configured with an X.509 certificate which appears
+     * in the certificate chain.
      *
      * <p>Note that only items referenced in {@code entriesToRequest} are returned - the
-     * {@code requestMessage} parameter is only used to for enforcing reader authentication.
+     * {@code requestMessage} parameter is used only for enforcing reader authentication.
      *
      * <p>The reason for having {@code requestMessage} and {@code entriesToRequest} as separate
      * parameters is that the former represents a request from the remote verifier device
@@ -219,13 +228,12 @@
      * @param entriesToRequest       The entries to request, organized as a map of namespace
      *                               names with each value being a collection of data elements
      *                               in the given namespace.
-     * @param readerSignature        COSE_Sign1 structure as described above or {@code null}
-     *                               if reader authentication is not being used.
+     * @param readerSignature        A {@code COSE_Sign1} structure as described above or
+     *                               {@code null} if reader authentication is not being used.
      * @return A {@link ResultData} object containing entry data organized by namespace and a
      *         cryptographically authenticated representation of the same data.
      * @throws SessionTranscriptMismatchException     Thrown when trying use multiple different
-     *                                                session transcripts in the same presentation
-     *                                                session.
+     *                                                session transcripts.
      * @throws NoAuthenticationKeyAvailableException  if authentication keys were never
      *                                                provisioned, the method
      *                                             {@link #setAvailableAuthenticationKeys(int, int)}
@@ -255,8 +263,8 @@
      * Sets the number of dynamic authentication keys the {@code IdentityCredential} will maintain,
      * and the number of times each should be used.
      *
-     * <p>{@code IdentityCredential}s will select the least-used dynamic authentication key each
-     * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. {@code IdentityCredential}s
+     * <p>The Identity Credential system will select the least-used dynamic authentication key each
+     * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. Identity Credentials
      * for which this method has not been called behave as though it had been called wit
      * {@code keyCount} 0 and {@code maxUsesPerKey} 1.
      *
@@ -274,9 +282,10 @@
      * <p>When there aren't enough certified dynamic authentication keys, either because the key
      * count has been increased or because one or more keys have reached their usage count, this
      * method will generate replacement keys and certificates and return them for issuer
-     * certification. The issuer certificates and associated static authentication data must then
-     * be provided back to the {@code IdentityCredential} using
-     * {@link #storeStaticAuthenticationData(X509Certificate, byte[])}.
+     * certification.  The issuer certificates and associated static authentication data must then
+     * be provided back to the Identity Credential using
+     * {@link #storeStaticAuthenticationData(X509Certificate, byte[])}.  The private part of
+     * each authentication key never leaves secure hardware.
      *
      * <p>Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey
      * can be obtained using the {@link #getCredentialKeyCertificateChain()} method.
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index a1dfc77..3843d92 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -46,7 +46,7 @@
  * access control profile IDs.  Names are strings and values are typed and can be any
  * value supported by <a href="http://cbor.io/">CBOR</a>.</li>
  *
- * <li>A set of access control profiles, each with a profile ID and a specification
+ * <li>A set of access control profiles (up to 32), each with a profile ID and a specification
  * of the conditions which satisfy the profile's requirements.</li>
  *
  * <li>An asymmetric key pair which is used to authenticate the credential to the Issuing
@@ -78,17 +78,21 @@
 
     /**
      * Specifies that the cipher suite that will be used to secure communications between the reader
-     * is:
+     * and the prover is using the following primitives
      *
      * <ul>
-     * <li>ECDHE with HKDF-SHA-256 for key agreement.</li>
-     * <li>AES-256 with GCM block mode for authenticated encryption (nonces are incremented by one
-     * for every message).</li>
-     * <li>ECDSA with SHA-256 for signing (used for signing session transcripts to defeat
-     * man-in-the-middle attacks), signing keys are not ephemeral. See {@link IdentityCredential}
-     * for details on reader and prover signing keys.</li>
+     * <li>ECKA-DH (Elliptic Curve Key Agreement Algorithm - Diffie-Hellman, see BSI TR-03111).</li>
+     *
+     * <li>HKDF-SHA-256 (see RFC 5869).</li>
+     *
+     * <li>AES-256-GCM (see NIST SP 800-38D).</li>
+     *
+     * <li>HMAC-SHA-256 (see RFC 2104).</li>
      * </ul>
      *
+     * <p>The exact way these primitives are combined to derive the session key is specified in
+     * section 9.2.1.4 of ISO/IEC 18013-5 (see description of cipher suite '1').<p>
+     *
      * <p>
      * At present this is the only supported cipher suite.
      */
@@ -135,9 +139,20 @@
     /**
      * Creates a new credential.
      *
+     * <p>When a credential is created, a cryptographic key-pair - CredentialKey - is created which
+     * is used to authenticate the store to the Issuing Authority.  The private part of this
+     * key-pair never leaves secure hardware and the public part can be obtained using
+     * {@link WritableIdentityCredential#getCredentialKeyCertificateChain(byte[])} on the
+     * returned object.
+     *
+     * <p>In addition, all of the Credential data content is imported and a certificate for the
+     * CredentialKey and a signature produced with the CredentialKey are created.  These latter
+     * values may be checked by an issuing authority to verify that the data was imported into
+     * secure hardware and that it was imported unmodified.
+     *
      * @param credentialName The name used to identify the credential.
      * @param docType        The document type for the credential.
-     * @return A @{link WritableIdentityCredential} that can be used to create a new credential.
+     * @return A {@link WritableIdentityCredential} that can be used to create a new credential.
      * @throws AlreadyPersonalizedException if a credential with the given name already exists.
      * @throws DocTypeNotSupportedException if the given document type isn't supported by the store.
      */
@@ -148,6 +163,10 @@
     /**
      * Retrieve a named credential.
      *
+     * <p>The cipher suite used to communicate with the remote verifier must also be specified.
+     * Currently only a single cipher-suite is supported. Support for other cipher suites may be
+     * added in a future version of this API.
+     *
      * @param credentialName the name of the credential to retrieve.
      * @param cipherSuite    the cipher suite to use for communicating with the verifier.
      * @return The named credential, or null if not found.
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index 13552d6..37de2c4 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -34,23 +34,23 @@
     /** Value was successfully retrieved. */
     public static final int STATUS_OK = 0;
 
-    /** Requested entry does not exist. */
+    /** The entry does not exist. */
     public static final int STATUS_NO_SUCH_ENTRY = 1;
 
-    /** Requested entry was not requested. */
+    /** The entry was not requested. */
     public static final int STATUS_NOT_REQUESTED = 2;
 
-    /** Requested entry wasn't in the request message. */
+    /** The entry wasn't in the request message. */
     public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
 
-    /** The requested entry was not retrieved because user authentication wasn't performed. */
+    /** The entry was not retrieved because user authentication failed. */
     public static final int STATUS_USER_AUTHENTICATION_FAILED = 4;
 
-    /** The requested entry was not retrieved because reader authentication wasn't performed. */
+    /** The entry was not retrieved because reader authentication failed. */
     public static final int STATUS_READER_AUTHENTICATION_FAILED = 5;
 
     /**
-     * The requested entry was not retrieved because it was configured without any access
+     * The entry was not retrieved because it was configured without any access
      * control profile.
      */
     public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
@@ -88,11 +88,10 @@
      *
      *   DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
      *   EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
-     *
      *   DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
      * </pre>
      *
-     * where
+     * <p>where
      *
      * <pre>
      *   DeviceNameSpaces = {
@@ -116,15 +115,16 @@
     public abstract @NonNull byte[] getAuthenticatedData();
 
     /**
-     * Returns a message authentication code over the data returned by
-     * {@link #getAuthenticatedData}, to prove to the reader that the data is from a trusted
-     * credential.
+     * Returns a message authentication code over the {@code DeviceAuthentication} CBOR
+     * specified in {@link #getAuthenticatedData()}, to prove to the reader that the data
+     * is from a trusted credential.
      *
      * <p>The MAC proves to the reader that the data is from a trusted credential. This code is
      * produced by using the key agreement and key derivation function from the ciphersuite
      * with the authentication private key and the reader ephemeral public key to compute a
      * shared message authentication code (MAC) key, then using the MAC function from the
-     * ciphersuite to compute a MAC of the authenticated data.
+     * ciphersuite to compute a MAC of the authenticated data. See section 9.2.3.5 of
+     * ISO/IEC 18013-5 for details of this operation.
      *
      * <p>If the {@code sessionTranscript} parameter passed to
      * {@link IdentityCredential#getEntries(byte[], Map, byte[], byte[])} was {@code null}
@@ -157,7 +157,7 @@
     /**
      * Get the names of all entries.
      *
-     * This includes the name of entries that wasn't successfully retrieved.
+     * <p>This includes the name of entries that wasn't successfully retrieved.
      *
      * @param namespaceName the namespace name to get entries for.
      * @return A collection of names or {@code null} if there are no entries for the given
@@ -168,7 +168,7 @@
     /**
      * Get the names of all entries that was successfully retrieved.
      *
-     * This only return entries for which {@link #getStatus(String, String)} will return
+     * <p>This only return entries for which {@link #getStatus(String, String)} will return
      * {@link #STATUS_OK}.
      *
      * @param namespaceName the namespace name to get entries for.
@@ -181,16 +181,15 @@
     /**
      * Gets the status of an entry.
      *
-     * This returns {@link #STATUS_OK} if the value was retrieved, {@link #STATUS_NO_SUCH_ENTRY}
+     * <p>This returns {@link #STATUS_OK} if the value was retrieved, {@link #STATUS_NO_SUCH_ENTRY}
      * if the given entry wasn't retrieved, {@link #STATUS_NOT_REQUESTED} if it wasn't requested,
      * {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if the request message was set but the entry wasn't
-     * present in the request message,
-     * {@link #STATUS_USER_AUTHENTICATION_FAILED} if the value
+     * present in the request message, {@link #STATUS_USER_AUTHENTICATION_FAILED} if the value
      * wasn't retrieved because the necessary user authentication wasn't performed,
-     * {@link #STATUS_READER_AUTHENTICATION_FAILED} if the supplied reader certificate chain
-     * didn't match the set of certificates the entry was provisioned with, or
-     * {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was configured without any
-     * access control profiles.
+     * {@link #STATUS_READER_AUTHENTICATION_FAILED} if the supplied reader certificate chain didn't
+     * match the set of certificates the entry was provisioned with, or
+     * {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was configured without any access
+     * control profiles.
      *
      * @param namespaceName the namespace name of the entry.
      * @param name the name of the entry to get the value for.
@@ -201,7 +200,7 @@
     /**
      * Gets the raw CBOR data for the value of an entry.
      *
-     * This should only be called on an entry for which the {@link #getStatus(String, String)}
+     * <p>This should only be called on an entry for which the {@link #getStatus(String, String)}
      * method returns {@link #STATUS_OK}.
      *
      * @param namespaceName the namespace name of the entry.
diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java
index e2a389b..c7aa328 100644
--- a/identity/java/android/security/identity/WritableIdentityCredential.java
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -41,15 +41,16 @@
      * <a href="https://source.android.com/security/keystore/attestation">Android Keystore</a>
      * attestation extension which describes the key and the security hardware in which it lives.
      *
-     * <p>Additionally, the attestation extension will contain the tag TODO_IC_KEY which indicates
-     * it is an Identity Credential key (which can only sign/MAC very specific messages) and not
-     * an Android Keystore key (which can be used to sign/MAC anything).
+     * <p>Additionally, the attestation extension will contain the tag Tag::IDENTITY_CREDENTIAL_KEY
+     * which indicates it is an Identity Credential key (which can only sign/MAC very specific
+     * messages) and not an Android Keystore key (which can be used to sign/MAC anything).
      *
      * <p>The issuer <b>MUST</b> carefully examine this certificate chain including (but not
-     * limited to) checking that the root certificate is well-known, the tag TODO_IC_KEY is
-     * present, the passed in challenge is present, the device has verified boot enabled, that each
-     * certificate in the chain is signed by its successor, that none of the certificates have been
-     * revoked and so on.
+     * limited to) checking that the root certificate is well-known, the tag
+     * Tag::IDENTITY_CREDENTIAL_KEY present, the passed in challenge is present, the tag
+     * Tag::ATTESTATION_APPLICATION_ID is set to the expected Android application, the device
+     * has verified boot enabled, each certificate in the chain is signed by its successor,
+     * none of the certificates have been revoked, and so on.
      *
      * <p>It is not strictly necessary to use this method to provision a credential if the issuing
      * authority doesn't care about the nature of the security hardware. If called, however, this
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index e9bc802..d35642e 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -1067,6 +1067,17 @@
         return onUserPasswordChanged(UserHandle.getUserId(Process.myUid()), newPassword);
     }
 
+    /**
+     * Notify keystore about the latest user locked state. This is to support keyguard-bound key.
+     */
+    public void onUserLockedStateChanged(int userHandle, boolean locked) {
+        try {
+            mBinder.onKeyguardVisibilityChanged(locked, userHandle);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to update user locked state " + userHandle, e);
+        }
+    }
+
     private class KeyAttestationCallbackResult {
         private KeystoreResponse keystoreResponse;
         private KeymasterCertificateChain certificateChain;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 91aac83..11775ca 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -17,11 +17,10 @@
 package android.security.keystore;
 
 import android.annotation.Nullable;
+import android.os.Build;
 import android.security.Credentials;
-import android.security.GateKeeper;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore;
-import android.security.KeyStoreException;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
 import android.security.keymaster.KeymasterCertificateChain;
@@ -52,6 +51,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
@@ -494,6 +494,20 @@
         if (challenge != null) {
             KeymasterArguments args = new KeymasterArguments();
             args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, challenge);
+
+            if (mSpec.isDevicePropertiesAttestationIncluded()) {
+                args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND,
+                        Build.BRAND.getBytes(StandardCharsets.UTF_8));
+                args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE,
+                        Build.DEVICE.getBytes(StandardCharsets.UTF_8));
+                args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT,
+                        Build.PRODUCT.getBytes(StandardCharsets.UTF_8));
+                args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER,
+                        Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8));
+                args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL,
+                        Build.MODEL.getBytes(StandardCharsets.UTF_8));
+            }
+
             return getAttestationChain(privateKeyAlias, keyPair, args);
         }
 
@@ -603,8 +617,14 @@
     private Iterable<byte[]> getAttestationChain(String privateKeyAlias,
             KeyPair keyPair, KeymasterArguments args)
                     throws ProviderException {
-        KeymasterCertificateChain outChain = new KeymasterCertificateChain();
-        int errorCode = mKeyStore.attestKey(privateKeyAlias, args, outChain);
+        final KeymasterCertificateChain outChain = new KeymasterCertificateChain();
+        final int errorCode;
+        if (mSpec.isDevicePropertiesAttestationIncluded()
+                && mSpec.getAttestationChallenge() == null) {
+            throw new ProviderException("An attestation challenge must be provided when requesting "
+                    + "device properties attestation.");
+        }
+        errorCode = mKeyStore.attestKey(privateKeyAlias, args, outChain);
         if (errorCode != KeyStore.NO_ERROR) {
             throw new ProviderException("Failed to generate attestation certificate chain",
                     KeyStore.getKeyStoreException(errorCode));
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 630a6dd..e7e410a 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -24,6 +24,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricPrompt;
+import android.os.Build;
 import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.text.TextUtils;
@@ -264,6 +265,7 @@
     private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mUserPresenceRequired;
     private final byte[] mAttestationChallenge;
+    private final boolean mDevicePropertiesAttestationIncluded;
     private final boolean mUniqueIdIncluded;
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mInvalidatedByBiometricEnrollment;
@@ -301,6 +303,7 @@
             int userAuthenticationValidityDurationSeconds,
             boolean userPresenceRequired,
             byte[] attestationChallenge,
+            boolean devicePropertiesAttestationIncluded,
             boolean uniqueIdIncluded,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
@@ -350,6 +353,7 @@
         mUserPresenceRequired = userPresenceRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
+        mDevicePropertiesAttestationIncluded = devicePropertiesAttestationIncluded;
         mUniqueIdIncluded = uniqueIdIncluded;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
@@ -644,6 +648,21 @@
     }
 
     /**
+     * Returns {@code true} if attestation for the base device properties ({@link Build#BRAND},
+     * {@link Build#DEVICE}, {@link Build#MANUFACTURER}, {@link Build#MODEL}, {@link Build#PRODUCT})
+     * was requested to be added in the attestation certificate for the generated key.
+     *
+     * {@link javax.crypto.KeyGenerator#generateKey()} will throw
+     * {@link java.security.ProviderException} if device properties attestation fails or is not
+     * supported.
+     *
+     * @see Builder#setDevicePropertiesAttestationIncluded(boolean)
+     */
+    public boolean isDevicePropertiesAttestationIncluded() {
+        return mDevicePropertiesAttestationIncluded;
+    }
+
+    /**
      * @hide This is a system-only API
      *
      * Returns {@code true} if the attestation certificate will contain a unique ID field.
@@ -734,6 +753,7 @@
         private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mUserPresenceRequired = false;
         private byte[] mAttestationChallenge = null;
+        private boolean mDevicePropertiesAttestationIncluded = false;
         private boolean mUniqueIdIncluded = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
@@ -797,6 +817,8 @@
                 sourceSpec.getUserAuthenticationValidityDurationSeconds();
             mUserPresenceRequired = sourceSpec.isUserPresenceRequired();
             mAttestationChallenge = sourceSpec.getAttestationChallenge();
+            mDevicePropertiesAttestationIncluded =
+                    sourceSpec.isDevicePropertiesAttestationIncluded();
             mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
             mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
             mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment();
@@ -1252,6 +1274,31 @@
         }
 
         /**
+         * Sets whether to include the base device properties in the attestation certificate.
+         *
+         * <p>If {@code attestationChallenge} is not {@code null}, the public key certificate for
+         * this key pair will contain an extension that describes the details of the key's
+         * configuration and authorizations, including the device properties values (brand, device,
+         * manufacturer, model, product). These should be the same as in ({@link Build#BRAND},
+         * {@link Build#DEVICE}, {@link Build#MANUFACTURER}, {@link Build#MODEL},
+         * {@link Build#PRODUCT}). The attestation certificate chain can
+         * be retrieved with {@link java.security.KeyStore#getCertificateChain(String)}.
+         *
+         * <p> If {@code attestationChallenge} is {@code null}, the public key certificate for
+         * this key pair will not contain the extension with the requested attested values.
+         *
+         * <p> {@link javax.crypto.KeyGenerator#generateKey()} will throw
+         * {@link java.security.ProviderException} if device properties attestation fails or is not
+         * supported.
+         */
+        @NonNull
+        public Builder setDevicePropertiesAttestationIncluded(
+                boolean devicePropertiesAttestationIncluded) {
+            mDevicePropertiesAttestationIncluded = devicePropertiesAttestationIncluded;
+            return this;
+        }
+
+        /**
          * @hide Only system apps can use this method.
          *
          * Sets whether to include a temporary unique ID field in the attestation certificate.
@@ -1360,6 +1407,7 @@
                     mUserAuthenticationValidityDurationSeconds,
                     mUserPresenceRequired,
                     mAttestationChallenge,
+                    mDevicePropertiesAttestationIncluded,
                     mUniqueIdIncluded,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index d8030fb..0518f45 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -16,8 +16,8 @@
 
 package android.security.keystore;
 
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import java.math.BigInteger;
 import java.security.spec.AlgorithmParameterSpec;
@@ -99,6 +99,7 @@
         out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
         out.writeBoolean(mSpec.isUserPresenceRequired());
         out.writeByteArray(mSpec.getAttestationChallenge());
+        out.writeBoolean(mSpec.isDevicePropertiesAttestationIncluded());
         out.writeBoolean(mSpec.isUniqueIdIncluded());
         out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
         out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
@@ -154,6 +155,7 @@
         final int userAuthenticationValidityDurationSeconds = in.readInt();
         final boolean userPresenceRequired = in.readBoolean();
         final byte[] attestationChallenge = in.createByteArray();
+        final boolean devicePropertiesAttestationIncluded = in.readBoolean();
         final boolean uniqueIdIncluded = in.readBoolean();
         final boolean userAuthenticationValidWhileOnBody = in.readBoolean();
         final boolean invalidatedByBiometricEnrollment = in.readBoolean();
@@ -185,6 +187,7 @@
                 userAuthenticationValidityDurationSeconds,
                 userPresenceRequired,
                 attestationChallenge,
+                devicePropertiesAttestationIncluded,
                 uniqueIdIncluded,
                 userAuthenticationValidWhileOnBody,
                 invalidatedByBiometricEnrollment,
diff --git a/keystore/tests/OWNERS b/keystore/tests/OWNERS
index 9e65f88..86c31f4 100644
--- a/keystore/tests/OWNERS
+++ b/keystore/tests/OWNERS
@@ -1,5 +1,4 @@
 # Android Enterprise security team
 eranm@google.com
-irinaid@google.com
 pgrafov@google.com
 rubinxu@google.com
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 237c1e9..09705e1 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -70,8 +70,11 @@
     LOG(ERROR) << "failed to load IDMAP " << idmap_path;
     return {};
   }
-  return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset),
-                  std::move(loaded_idmap), system, false /*load_as_shared_library*/);
+  auto apkPath = loaded_idmap->OverlayApkPath();
+  return LoadImpl({} /*fd*/, apkPath,
+                  std::move(idmap_asset),
+                  std::move(loaded_idmap),
+                  system, false /*load_as_shared_library*/);
 }
 
 std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index e748bd8..6c6c5c9 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -15,1474 +15,1478 @@
     /* 11 */ {'C', 'a', 'r', 'i'},
     /* 12 */ {'C', 'h', 'a', 'm'},
     /* 13 */ {'C', 'h', 'e', 'r'},
-    /* 14 */ {'C', 'o', 'p', 't'},
-    /* 15 */ {'C', 'p', 'r', 't'},
-    /* 16 */ {'C', 'y', 'r', 'l'},
-    /* 17 */ {'D', 'e', 'v', 'a'},
-    /* 18 */ {'E', 'g', 'y', 'p'},
-    /* 19 */ {'E', 't', 'h', 'i'},
-    /* 20 */ {'G', 'e', 'o', 'r'},
-    /* 21 */ {'G', 'o', 'n', 'g'},
-    /* 22 */ {'G', 'o', 'n', 'm'},
-    /* 23 */ {'G', 'o', 't', 'h'},
-    /* 24 */ {'G', 'r', 'e', 'k'},
-    /* 25 */ {'G', 'u', 'j', 'r'},
-    /* 26 */ {'G', 'u', 'r', 'u'},
-    /* 27 */ {'H', 'a', 'n', 's'},
-    /* 28 */ {'H', 'a', 'n', 't'},
-    /* 29 */ {'H', 'a', 't', 'r'},
-    /* 30 */ {'H', 'e', 'b', 'r'},
-    /* 31 */ {'H', 'l', 'u', 'w'},
-    /* 32 */ {'H', 'm', 'n', 'g'},
-    /* 33 */ {'H', 'm', 'n', 'p'},
-    /* 34 */ {'I', 't', 'a', 'l'},
-    /* 35 */ {'J', 'p', 'a', 'n'},
-    /* 36 */ {'K', 'a', 'l', 'i'},
-    /* 37 */ {'K', 'a', 'n', 'a'},
-    /* 38 */ {'K', 'h', 'a', 'r'},
-    /* 39 */ {'K', 'h', 'm', 'r'},
-    /* 40 */ {'K', 'n', 'd', 'a'},
-    /* 41 */ {'K', 'o', 'r', 'e'},
-    /* 42 */ {'L', 'a', 'n', 'a'},
-    /* 43 */ {'L', 'a', 'o', 'o'},
-    /* 44 */ {'L', 'a', 't', 'n'},
-    /* 45 */ {'L', 'e', 'p', 'c'},
-    /* 46 */ {'L', 'i', 'n', 'a'},
-    /* 47 */ {'L', 'i', 's', 'u'},
-    /* 48 */ {'L', 'y', 'c', 'i'},
-    /* 49 */ {'L', 'y', 'd', 'i'},
-    /* 50 */ {'M', 'a', 'n', 'd'},
-    /* 51 */ {'M', 'a', 'n', 'i'},
-    /* 52 */ {'M', 'e', 'r', 'c'},
-    /* 53 */ {'M', 'l', 'y', 'm'},
-    /* 54 */ {'M', 'o', 'n', 'g'},
-    /* 55 */ {'M', 'r', 'o', 'o'},
-    /* 56 */ {'M', 'y', 'm', 'r'},
-    /* 57 */ {'N', 'a', 'r', 'b'},
-    /* 58 */ {'N', 'k', 'o', 'o'},
-    /* 59 */ {'N', 's', 'h', 'u'},
-    /* 60 */ {'O', 'g', 'a', 'm'},
-    /* 61 */ {'O', 'r', 'k', 'h'},
-    /* 62 */ {'O', 'r', 'y', 'a'},
-    /* 63 */ {'O', 's', 'g', 'e'},
-    /* 64 */ {'P', 'a', 'u', 'c'},
-    /* 65 */ {'P', 'h', 'l', 'i'},
-    /* 66 */ {'P', 'h', 'n', 'x'},
-    /* 67 */ {'P', 'l', 'r', 'd'},
-    /* 68 */ {'P', 'r', 't', 'i'},
-    /* 69 */ {'R', 'u', 'n', 'r'},
-    /* 70 */ {'S', 'a', 'm', 'r'},
-    /* 71 */ {'S', 'a', 'r', 'b'},
-    /* 72 */ {'S', 'a', 'u', 'r'},
-    /* 73 */ {'S', 'g', 'n', 'w'},
-    /* 74 */ {'S', 'i', 'n', 'h'},
-    /* 75 */ {'S', 'o', 'g', 'd'},
-    /* 76 */ {'S', 'o', 'r', 'a'},
-    /* 77 */ {'S', 'o', 'y', 'o'},
-    /* 78 */ {'S', 'y', 'r', 'c'},
-    /* 79 */ {'T', 'a', 'l', 'e'},
-    /* 80 */ {'T', 'a', 'l', 'u'},
-    /* 81 */ {'T', 'a', 'm', 'l'},
-    /* 82 */ {'T', 'a', 'n', 'g'},
-    /* 83 */ {'T', 'a', 'v', 't'},
-    /* 84 */ {'T', 'e', 'l', 'u'},
-    /* 85 */ {'T', 'f', 'n', 'g'},
-    /* 86 */ {'T', 'h', 'a', 'a'},
-    /* 87 */ {'T', 'h', 'a', 'i'},
-    /* 88 */ {'T', 'i', 'b', 't'},
-    /* 89 */ {'U', 'g', 'a', 'r'},
-    /* 90 */ {'V', 'a', 'i', 'i'},
-    /* 91 */ {'W', 'c', 'h', 'o'},
-    /* 92 */ {'X', 'p', 'e', 'o'},
-    /* 93 */ {'X', 's', 'u', 'x'},
-    /* 94 */ {'Y', 'i', 'i', 'i'},
-    /* 95 */ {'~', '~', '~', 'A'},
-    /* 96 */ {'~', '~', '~', 'B'},
+    /* 14 */ {'C', 'h', 'r', 's'},
+    /* 15 */ {'C', 'o', 'p', 't'},
+    /* 16 */ {'C', 'p', 'r', 't'},
+    /* 17 */ {'C', 'y', 'r', 'l'},
+    /* 18 */ {'D', 'e', 'v', 'a'},
+    /* 19 */ {'E', 'g', 'y', 'p'},
+    /* 20 */ {'E', 't', 'h', 'i'},
+    /* 21 */ {'G', 'e', 'o', 'r'},
+    /* 22 */ {'G', 'o', 'n', 'g'},
+    /* 23 */ {'G', 'o', 'n', 'm'},
+    /* 24 */ {'G', 'o', 't', 'h'},
+    /* 25 */ {'G', 'r', 'e', 'k'},
+    /* 26 */ {'G', 'u', 'j', 'r'},
+    /* 27 */ {'G', 'u', 'r', 'u'},
+    /* 28 */ {'H', 'a', 'n', 's'},
+    /* 29 */ {'H', 'a', 'n', 't'},
+    /* 30 */ {'H', 'a', 't', 'r'},
+    /* 31 */ {'H', 'e', 'b', 'r'},
+    /* 32 */ {'H', 'l', 'u', 'w'},
+    /* 33 */ {'H', 'm', 'n', 'g'},
+    /* 34 */ {'H', 'm', 'n', 'p'},
+    /* 35 */ {'I', 't', 'a', 'l'},
+    /* 36 */ {'J', 'p', 'a', 'n'},
+    /* 37 */ {'K', 'a', 'l', 'i'},
+    /* 38 */ {'K', 'a', 'n', 'a'},
+    /* 39 */ {'K', 'h', 'a', 'r'},
+    /* 40 */ {'K', 'h', 'm', 'r'},
+    /* 41 */ {'K', 'i', 't', 's'},
+    /* 42 */ {'K', 'n', 'd', 'a'},
+    /* 43 */ {'K', 'o', 'r', 'e'},
+    /* 44 */ {'L', 'a', 'n', 'a'},
+    /* 45 */ {'L', 'a', 'o', 'o'},
+    /* 46 */ {'L', 'a', 't', 'n'},
+    /* 47 */ {'L', 'e', 'p', 'c'},
+    /* 48 */ {'L', 'i', 'n', 'a'},
+    /* 49 */ {'L', 'i', 's', 'u'},
+    /* 50 */ {'L', 'y', 'c', 'i'},
+    /* 51 */ {'L', 'y', 'd', 'i'},
+    /* 52 */ {'M', 'a', 'n', 'd'},
+    /* 53 */ {'M', 'a', 'n', 'i'},
+    /* 54 */ {'M', 'e', 'r', 'c'},
+    /* 55 */ {'M', 'l', 'y', 'm'},
+    /* 56 */ {'M', 'o', 'n', 'g'},
+    /* 57 */ {'M', 'r', 'o', 'o'},
+    /* 58 */ {'M', 'y', 'm', 'r'},
+    /* 59 */ {'N', 'a', 'r', 'b'},
+    /* 60 */ {'N', 'k', 'o', 'o'},
+    /* 61 */ {'N', 's', 'h', 'u'},
+    /* 62 */ {'O', 'g', 'a', 'm'},
+    /* 63 */ {'O', 'r', 'k', 'h'},
+    /* 64 */ {'O', 'r', 'y', 'a'},
+    /* 65 */ {'O', 's', 'g', 'e'},
+    /* 66 */ {'P', 'a', 'u', 'c'},
+    /* 67 */ {'P', 'h', 'l', 'i'},
+    /* 68 */ {'P', 'h', 'n', 'x'},
+    /* 69 */ {'P', 'l', 'r', 'd'},
+    /* 70 */ {'P', 'r', 't', 'i'},
+    /* 71 */ {'R', 'u', 'n', 'r'},
+    /* 72 */ {'S', 'a', 'm', 'r'},
+    /* 73 */ {'S', 'a', 'r', 'b'},
+    /* 74 */ {'S', 'a', 'u', 'r'},
+    /* 75 */ {'S', 'g', 'n', 'w'},
+    /* 76 */ {'S', 'i', 'n', 'h'},
+    /* 77 */ {'S', 'o', 'g', 'd'},
+    /* 78 */ {'S', 'o', 'r', 'a'},
+    /* 79 */ {'S', 'o', 'y', 'o'},
+    /* 80 */ {'S', 'y', 'r', 'c'},
+    /* 81 */ {'T', 'a', 'l', 'e'},
+    /* 82 */ {'T', 'a', 'l', 'u'},
+    /* 83 */ {'T', 'a', 'm', 'l'},
+    /* 84 */ {'T', 'a', 'n', 'g'},
+    /* 85 */ {'T', 'a', 'v', 't'},
+    /* 86 */ {'T', 'e', 'l', 'u'},
+    /* 87 */ {'T', 'f', 'n', 'g'},
+    /* 88 */ {'T', 'h', 'a', 'a'},
+    /* 89 */ {'T', 'h', 'a', 'i'},
+    /* 90 */ {'T', 'i', 'b', 't'},
+    /* 91 */ {'U', 'g', 'a', 'r'},
+    /* 92 */ {'V', 'a', 'i', 'i'},
+    /* 93 */ {'W', 'c', 'h', 'o'},
+    /* 94 */ {'X', 'p', 'e', 'o'},
+    /* 95 */ {'X', 's', 'u', 'x'},
+    /* 96 */ {'Y', 'i', 'i', 'i'},
+    /* 97 */ {'~', '~', '~', 'A'},
+    /* 98 */ {'~', '~', '~', 'B'},
 };
 
 
 const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
-    {0x61610000u, 44u}, // aa -> Latn
-    {0xA0000000u, 44u}, // aai -> Latn
-    {0xA8000000u, 44u}, // aak -> Latn
-    {0xD0000000u, 44u}, // aau -> Latn
-    {0x61620000u, 16u}, // ab -> Cyrl
-    {0xA0200000u, 44u}, // abi -> Latn
-    {0xC0200000u, 16u}, // abq -> Cyrl
-    {0xC4200000u, 44u}, // abr -> Latn
-    {0xCC200000u, 44u}, // abt -> Latn
-    {0xE0200000u, 44u}, // aby -> Latn
-    {0x8C400000u, 44u}, // acd -> Latn
-    {0x90400000u, 44u}, // ace -> Latn
-    {0x9C400000u, 44u}, // ach -> Latn
-    {0x80600000u, 44u}, // ada -> Latn
-    {0x90600000u, 44u}, // ade -> Latn
-    {0xA4600000u, 44u}, // adj -> Latn
-    {0xBC600000u, 88u}, // adp -> Tibt
-    {0xE0600000u, 16u}, // ady -> Cyrl
-    {0xE4600000u, 44u}, // adz -> Latn
+    {0x61610000u, 46u}, // aa -> Latn
+    {0xA0000000u, 46u}, // aai -> Latn
+    {0xA8000000u, 46u}, // aak -> Latn
+    {0xD0000000u, 46u}, // aau -> Latn
+    {0x61620000u, 17u}, // ab -> Cyrl
+    {0xA0200000u, 46u}, // abi -> Latn
+    {0xC0200000u, 17u}, // abq -> Cyrl
+    {0xC4200000u, 46u}, // abr -> Latn
+    {0xCC200000u, 46u}, // abt -> Latn
+    {0xE0200000u, 46u}, // aby -> Latn
+    {0x8C400000u, 46u}, // acd -> Latn
+    {0x90400000u, 46u}, // ace -> Latn
+    {0x9C400000u, 46u}, // ach -> Latn
+    {0x80600000u, 46u}, // ada -> Latn
+    {0x90600000u, 46u}, // ade -> Latn
+    {0xA4600000u, 46u}, // adj -> Latn
+    {0xBC600000u, 90u}, // adp -> Tibt
+    {0xE0600000u, 17u}, // ady -> Cyrl
+    {0xE4600000u, 46u}, // adz -> Latn
     {0x61650000u,  4u}, // ae -> Avst
     {0x84800000u,  1u}, // aeb -> Arab
-    {0xE0800000u, 44u}, // aey -> Latn
-    {0x61660000u, 44u}, // af -> Latn
-    {0x88C00000u, 44u}, // agc -> Latn
-    {0x8CC00000u, 44u}, // agd -> Latn
-    {0x98C00000u, 44u}, // agg -> Latn
-    {0xB0C00000u, 44u}, // agm -> Latn
-    {0xB8C00000u, 44u}, // ago -> Latn
-    {0xC0C00000u, 44u}, // agq -> Latn
-    {0x80E00000u, 44u}, // aha -> Latn
-    {0xACE00000u, 44u}, // ahl -> Latn
+    {0xE0800000u, 46u}, // aey -> Latn
+    {0x61660000u, 46u}, // af -> Latn
+    {0x88C00000u, 46u}, // agc -> Latn
+    {0x8CC00000u, 46u}, // agd -> Latn
+    {0x98C00000u, 46u}, // agg -> Latn
+    {0xB0C00000u, 46u}, // agm -> Latn
+    {0xB8C00000u, 46u}, // ago -> Latn
+    {0xC0C00000u, 46u}, // agq -> Latn
+    {0x80E00000u, 46u}, // aha -> Latn
+    {0xACE00000u, 46u}, // ahl -> Latn
     {0xB8E00000u,  0u}, // aho -> Ahom
-    {0x99200000u, 44u}, // ajg -> Latn
-    {0x616B0000u, 44u}, // ak -> Latn
-    {0xA9400000u, 93u}, // akk -> Xsux
-    {0x81600000u, 44u}, // ala -> Latn
-    {0xA1600000u, 44u}, // ali -> Latn
-    {0xB5600000u, 44u}, // aln -> Latn
-    {0xCD600000u, 16u}, // alt -> Cyrl
-    {0x616D0000u, 19u}, // am -> Ethi
-    {0xB1800000u, 44u}, // amm -> Latn
-    {0xB5800000u, 44u}, // amn -> Latn
-    {0xB9800000u, 44u}, // amo -> Latn
-    {0xBD800000u, 44u}, // amp -> Latn
-    {0x616E0000u, 44u}, // an -> Latn
-    {0x89A00000u, 44u}, // anc -> Latn
-    {0xA9A00000u, 44u}, // ank -> Latn
-    {0xB5A00000u, 44u}, // ann -> Latn
-    {0xE1A00000u, 44u}, // any -> Latn
-    {0xA5C00000u, 44u}, // aoj -> Latn
-    {0xB1C00000u, 44u}, // aom -> Latn
-    {0xE5C00000u, 44u}, // aoz -> Latn
+    {0x99200000u, 46u}, // ajg -> Latn
+    {0x616B0000u, 46u}, // ak -> Latn
+    {0xA9400000u, 95u}, // akk -> Xsux
+    {0x81600000u, 46u}, // ala -> Latn
+    {0xA1600000u, 46u}, // ali -> Latn
+    {0xB5600000u, 46u}, // aln -> Latn
+    {0xCD600000u, 17u}, // alt -> Cyrl
+    {0x616D0000u, 20u}, // am -> Ethi
+    {0xB1800000u, 46u}, // amm -> Latn
+    {0xB5800000u, 46u}, // amn -> Latn
+    {0xB9800000u, 46u}, // amo -> Latn
+    {0xBD800000u, 46u}, // amp -> Latn
+    {0x616E0000u, 46u}, // an -> Latn
+    {0x89A00000u, 46u}, // anc -> Latn
+    {0xA9A00000u, 46u}, // ank -> Latn
+    {0xB5A00000u, 46u}, // ann -> Latn
+    {0xE1A00000u, 46u}, // any -> Latn
+    {0xA5C00000u, 46u}, // aoj -> Latn
+    {0xB1C00000u, 46u}, // aom -> Latn
+    {0xE5C00000u, 46u}, // aoz -> Latn
     {0x89E00000u,  1u}, // apc -> Arab
     {0x8DE00000u,  1u}, // apd -> Arab
-    {0x91E00000u, 44u}, // ape -> Latn
-    {0xC5E00000u, 44u}, // apr -> Latn
-    {0xC9E00000u, 44u}, // aps -> Latn
-    {0xE5E00000u, 44u}, // apz -> Latn
+    {0x91E00000u, 46u}, // ape -> Latn
+    {0xC5E00000u, 46u}, // apr -> Latn
+    {0xC9E00000u, 46u}, // aps -> Latn
+    {0xE5E00000u, 46u}, // apz -> Latn
     {0x61720000u,  1u}, // ar -> Arab
-    {0x61725842u, 96u}, // ar-XB -> ~~~B
+    {0x61725842u, 98u}, // ar-XB -> ~~~B
     {0x8A200000u,  2u}, // arc -> Armi
-    {0x9E200000u, 44u}, // arh -> Latn
-    {0xB6200000u, 44u}, // arn -> Latn
-    {0xBA200000u, 44u}, // aro -> Latn
+    {0x9E200000u, 46u}, // arh -> Latn
+    {0xB6200000u, 46u}, // arn -> Latn
+    {0xBA200000u, 46u}, // aro -> Latn
     {0xC2200000u,  1u}, // arq -> Arab
     {0xCA200000u,  1u}, // ars -> Arab
     {0xE2200000u,  1u}, // ary -> Arab
     {0xE6200000u,  1u}, // arz -> Arab
     {0x61730000u,  7u}, // as -> Beng
-    {0x82400000u, 44u}, // asa -> Latn
-    {0x92400000u, 73u}, // ase -> Sgnw
-    {0x9A400000u, 44u}, // asg -> Latn
-    {0xBA400000u, 44u}, // aso -> Latn
-    {0xCE400000u, 44u}, // ast -> Latn
-    {0x82600000u, 44u}, // ata -> Latn
-    {0x9A600000u, 44u}, // atg -> Latn
-    {0xA6600000u, 44u}, // atj -> Latn
-    {0xE2800000u, 44u}, // auy -> Latn
-    {0x61760000u, 16u}, // av -> Cyrl
+    {0x82400000u, 46u}, // asa -> Latn
+    {0x92400000u, 75u}, // ase -> Sgnw
+    {0x9A400000u, 46u}, // asg -> Latn
+    {0xBA400000u, 46u}, // aso -> Latn
+    {0xCE400000u, 46u}, // ast -> Latn
+    {0x82600000u, 46u}, // ata -> Latn
+    {0x9A600000u, 46u}, // atg -> Latn
+    {0xA6600000u, 46u}, // atj -> Latn
+    {0xE2800000u, 46u}, // auy -> Latn
+    {0x61760000u, 17u}, // av -> Cyrl
     {0xAEA00000u,  1u}, // avl -> Arab
-    {0xB6A00000u, 44u}, // avn -> Latn
-    {0xCEA00000u, 44u}, // avt -> Latn
-    {0xD2A00000u, 44u}, // avu -> Latn
-    {0x82C00000u, 17u}, // awa -> Deva
-    {0x86C00000u, 44u}, // awb -> Latn
-    {0xBAC00000u, 44u}, // awo -> Latn
-    {0xDEC00000u, 44u}, // awx -> Latn
-    {0x61790000u, 44u}, // ay -> Latn
-    {0x87000000u, 44u}, // ayb -> Latn
-    {0x617A0000u, 44u}, // az -> Latn
+    {0xB6A00000u, 46u}, // avn -> Latn
+    {0xCEA00000u, 46u}, // avt -> Latn
+    {0xD2A00000u, 46u}, // avu -> Latn
+    {0x82C00000u, 18u}, // awa -> Deva
+    {0x86C00000u, 46u}, // awb -> Latn
+    {0xBAC00000u, 46u}, // awo -> Latn
+    {0xDEC00000u, 46u}, // awx -> Latn
+    {0x61790000u, 46u}, // ay -> Latn
+    {0x87000000u, 46u}, // ayb -> Latn
+    {0x617A0000u, 46u}, // az -> Latn
     {0x617A4951u,  1u}, // az-IQ -> Arab
     {0x617A4952u,  1u}, // az-IR -> Arab
-    {0x617A5255u, 16u}, // az-RU -> Cyrl
-    {0x62610000u, 16u}, // ba -> Cyrl
+    {0x617A5255u, 17u}, // az-RU -> Cyrl
+    {0x62610000u, 17u}, // ba -> Cyrl
     {0xAC010000u,  1u}, // bal -> Arab
-    {0xB4010000u, 44u}, // ban -> Latn
-    {0xBC010000u, 17u}, // bap -> Deva
-    {0xC4010000u, 44u}, // bar -> Latn
-    {0xC8010000u, 44u}, // bas -> Latn
-    {0xD4010000u, 44u}, // bav -> Latn
+    {0xB4010000u, 46u}, // ban -> Latn
+    {0xBC010000u, 18u}, // bap -> Deva
+    {0xC4010000u, 46u}, // bar -> Latn
+    {0xC8010000u, 46u}, // bas -> Latn
+    {0xD4010000u, 46u}, // bav -> Latn
     {0xDC010000u,  5u}, // bax -> Bamu
-    {0x80210000u, 44u}, // bba -> Latn
-    {0x84210000u, 44u}, // bbb -> Latn
-    {0x88210000u, 44u}, // bbc -> Latn
-    {0x8C210000u, 44u}, // bbd -> Latn
-    {0xA4210000u, 44u}, // bbj -> Latn
-    {0xBC210000u, 44u}, // bbp -> Latn
-    {0xC4210000u, 44u}, // bbr -> Latn
-    {0x94410000u, 44u}, // bcf -> Latn
-    {0x9C410000u, 44u}, // bch -> Latn
-    {0xA0410000u, 44u}, // bci -> Latn
-    {0xB0410000u, 44u}, // bcm -> Latn
-    {0xB4410000u, 44u}, // bcn -> Latn
-    {0xB8410000u, 44u}, // bco -> Latn
-    {0xC0410000u, 19u}, // bcq -> Ethi
-    {0xD0410000u, 44u}, // bcu -> Latn
-    {0x8C610000u, 44u}, // bdd -> Latn
-    {0x62650000u, 16u}, // be -> Cyrl
-    {0x94810000u, 44u}, // bef -> Latn
-    {0x9C810000u, 44u}, // beh -> Latn
+    {0x80210000u, 46u}, // bba -> Latn
+    {0x84210000u, 46u}, // bbb -> Latn
+    {0x88210000u, 46u}, // bbc -> Latn
+    {0x8C210000u, 46u}, // bbd -> Latn
+    {0xA4210000u, 46u}, // bbj -> Latn
+    {0xBC210000u, 46u}, // bbp -> Latn
+    {0xC4210000u, 46u}, // bbr -> Latn
+    {0x94410000u, 46u}, // bcf -> Latn
+    {0x9C410000u, 46u}, // bch -> Latn
+    {0xA0410000u, 46u}, // bci -> Latn
+    {0xB0410000u, 46u}, // bcm -> Latn
+    {0xB4410000u, 46u}, // bcn -> Latn
+    {0xB8410000u, 46u}, // bco -> Latn
+    {0xC0410000u, 20u}, // bcq -> Ethi
+    {0xD0410000u, 46u}, // bcu -> Latn
+    {0x8C610000u, 46u}, // bdd -> Latn
+    {0x62650000u, 17u}, // be -> Cyrl
+    {0x94810000u, 46u}, // bef -> Latn
+    {0x9C810000u, 46u}, // beh -> Latn
     {0xA4810000u,  1u}, // bej -> Arab
-    {0xB0810000u, 44u}, // bem -> Latn
-    {0xCC810000u, 44u}, // bet -> Latn
-    {0xD8810000u, 44u}, // bew -> Latn
-    {0xDC810000u, 44u}, // bex -> Latn
-    {0xE4810000u, 44u}, // bez -> Latn
-    {0x8CA10000u, 44u}, // bfd -> Latn
-    {0xC0A10000u, 81u}, // bfq -> Taml
+    {0xB0810000u, 46u}, // bem -> Latn
+    {0xCC810000u, 46u}, // bet -> Latn
+    {0xD8810000u, 46u}, // bew -> Latn
+    {0xDC810000u, 46u}, // bex -> Latn
+    {0xE4810000u, 46u}, // bez -> Latn
+    {0x8CA10000u, 46u}, // bfd -> Latn
+    {0xC0A10000u, 83u}, // bfq -> Taml
     {0xCCA10000u,  1u}, // bft -> Arab
-    {0xE0A10000u, 17u}, // bfy -> Deva
-    {0x62670000u, 16u}, // bg -> Cyrl
-    {0x88C10000u, 17u}, // bgc -> Deva
+    {0xE0A10000u, 18u}, // bfy -> Deva
+    {0x62670000u, 17u}, // bg -> Cyrl
+    {0x88C10000u, 18u}, // bgc -> Deva
     {0xB4C10000u,  1u}, // bgn -> Arab
-    {0xDCC10000u, 24u}, // bgx -> Grek
-    {0x84E10000u, 17u}, // bhb -> Deva
-    {0x98E10000u, 44u}, // bhg -> Latn
-    {0xA0E10000u, 17u}, // bhi -> Deva
-    {0xACE10000u, 44u}, // bhl -> Latn
-    {0xB8E10000u, 17u}, // bho -> Deva
-    {0xE0E10000u, 44u}, // bhy -> Latn
-    {0x62690000u, 44u}, // bi -> Latn
-    {0x85010000u, 44u}, // bib -> Latn
-    {0x99010000u, 44u}, // big -> Latn
-    {0xA9010000u, 44u}, // bik -> Latn
-    {0xB1010000u, 44u}, // bim -> Latn
-    {0xB5010000u, 44u}, // bin -> Latn
-    {0xB9010000u, 44u}, // bio -> Latn
-    {0xC1010000u, 44u}, // biq -> Latn
-    {0x9D210000u, 44u}, // bjh -> Latn
-    {0xA1210000u, 19u}, // bji -> Ethi
-    {0xA5210000u, 17u}, // bjj -> Deva
-    {0xB5210000u, 44u}, // bjn -> Latn
-    {0xB9210000u, 44u}, // bjo -> Latn
-    {0xC5210000u, 44u}, // bjr -> Latn
-    {0xCD210000u, 44u}, // bjt -> Latn
-    {0xE5210000u, 44u}, // bjz -> Latn
-    {0x89410000u, 44u}, // bkc -> Latn
-    {0xB1410000u, 44u}, // bkm -> Latn
-    {0xC1410000u, 44u}, // bkq -> Latn
-    {0xD1410000u, 44u}, // bku -> Latn
-    {0xD5410000u, 44u}, // bkv -> Latn
-    {0xCD610000u, 83u}, // blt -> Tavt
-    {0x626D0000u, 44u}, // bm -> Latn
-    {0x9D810000u, 44u}, // bmh -> Latn
-    {0xA9810000u, 44u}, // bmk -> Latn
-    {0xC1810000u, 44u}, // bmq -> Latn
-    {0xD1810000u, 44u}, // bmu -> Latn
+    {0xDCC10000u, 25u}, // bgx -> Grek
+    {0x84E10000u, 18u}, // bhb -> Deva
+    {0x98E10000u, 46u}, // bhg -> Latn
+    {0xA0E10000u, 18u}, // bhi -> Deva
+    {0xACE10000u, 46u}, // bhl -> Latn
+    {0xB8E10000u, 18u}, // bho -> Deva
+    {0xE0E10000u, 46u}, // bhy -> Latn
+    {0x62690000u, 46u}, // bi -> Latn
+    {0x85010000u, 46u}, // bib -> Latn
+    {0x99010000u, 46u}, // big -> Latn
+    {0xA9010000u, 46u}, // bik -> Latn
+    {0xB1010000u, 46u}, // bim -> Latn
+    {0xB5010000u, 46u}, // bin -> Latn
+    {0xB9010000u, 46u}, // bio -> Latn
+    {0xC1010000u, 46u}, // biq -> Latn
+    {0x9D210000u, 46u}, // bjh -> Latn
+    {0xA1210000u, 20u}, // bji -> Ethi
+    {0xA5210000u, 18u}, // bjj -> Deva
+    {0xB5210000u, 46u}, // bjn -> Latn
+    {0xB9210000u, 46u}, // bjo -> Latn
+    {0xC5210000u, 46u}, // bjr -> Latn
+    {0xCD210000u, 46u}, // bjt -> Latn
+    {0xE5210000u, 46u}, // bjz -> Latn
+    {0x89410000u, 46u}, // bkc -> Latn
+    {0xB1410000u, 46u}, // bkm -> Latn
+    {0xC1410000u, 46u}, // bkq -> Latn
+    {0xD1410000u, 46u}, // bku -> Latn
+    {0xD5410000u, 46u}, // bkv -> Latn
+    {0xCD610000u, 85u}, // blt -> Tavt
+    {0x626D0000u, 46u}, // bm -> Latn
+    {0x9D810000u, 46u}, // bmh -> Latn
+    {0xA9810000u, 46u}, // bmk -> Latn
+    {0xC1810000u, 46u}, // bmq -> Latn
+    {0xD1810000u, 46u}, // bmu -> Latn
     {0x626E0000u,  7u}, // bn -> Beng
-    {0x99A10000u, 44u}, // bng -> Latn
-    {0xB1A10000u, 44u}, // bnm -> Latn
-    {0xBDA10000u, 44u}, // bnp -> Latn
-    {0x626F0000u, 88u}, // bo -> Tibt
-    {0xA5C10000u, 44u}, // boj -> Latn
-    {0xB1C10000u, 44u}, // bom -> Latn
-    {0xB5C10000u, 44u}, // bon -> Latn
+    {0x99A10000u, 46u}, // bng -> Latn
+    {0xB1A10000u, 46u}, // bnm -> Latn
+    {0xBDA10000u, 46u}, // bnp -> Latn
+    {0x626F0000u, 90u}, // bo -> Tibt
+    {0xA5C10000u, 46u}, // boj -> Latn
+    {0xB1C10000u, 46u}, // bom -> Latn
+    {0xB5C10000u, 46u}, // bon -> Latn
     {0xE1E10000u,  7u}, // bpy -> Beng
-    {0x8A010000u, 44u}, // bqc -> Latn
+    {0x8A010000u, 46u}, // bqc -> Latn
     {0xA2010000u,  1u}, // bqi -> Arab
-    {0xBE010000u, 44u}, // bqp -> Latn
-    {0xD6010000u, 44u}, // bqv -> Latn
-    {0x62720000u, 44u}, // br -> Latn
-    {0x82210000u, 17u}, // bra -> Deva
+    {0xBE010000u, 46u}, // bqp -> Latn
+    {0xD6010000u, 46u}, // bqv -> Latn
+    {0x62720000u, 46u}, // br -> Latn
+    {0x82210000u, 18u}, // bra -> Deva
     {0x9E210000u,  1u}, // brh -> Arab
-    {0xDE210000u, 17u}, // brx -> Deva
-    {0xE6210000u, 44u}, // brz -> Latn
-    {0x62730000u, 44u}, // bs -> Latn
-    {0xA6410000u, 44u}, // bsj -> Latn
+    {0xDE210000u, 18u}, // brx -> Deva
+    {0xE6210000u, 46u}, // brz -> Latn
+    {0x62730000u, 46u}, // bs -> Latn
+    {0xA6410000u, 46u}, // bsj -> Latn
     {0xC2410000u,  6u}, // bsq -> Bass
-    {0xCA410000u, 44u}, // bss -> Latn
-    {0xCE410000u, 19u}, // bst -> Ethi
-    {0xBA610000u, 44u}, // bto -> Latn
-    {0xCE610000u, 44u}, // btt -> Latn
-    {0xD6610000u, 17u}, // btv -> Deva
-    {0x82810000u, 16u}, // bua -> Cyrl
-    {0x8A810000u, 44u}, // buc -> Latn
-    {0x8E810000u, 44u}, // bud -> Latn
-    {0x9A810000u, 44u}, // bug -> Latn
-    {0xAA810000u, 44u}, // buk -> Latn
-    {0xB2810000u, 44u}, // bum -> Latn
-    {0xBA810000u, 44u}, // buo -> Latn
-    {0xCA810000u, 44u}, // bus -> Latn
-    {0xD2810000u, 44u}, // buu -> Latn
-    {0x86A10000u, 44u}, // bvb -> Latn
-    {0x8EC10000u, 44u}, // bwd -> Latn
-    {0xC6C10000u, 44u}, // bwr -> Latn
-    {0x9EE10000u, 44u}, // bxh -> Latn
-    {0x93010000u, 44u}, // bye -> Latn
-    {0xB7010000u, 19u}, // byn -> Ethi
-    {0xC7010000u, 44u}, // byr -> Latn
-    {0xCB010000u, 44u}, // bys -> Latn
-    {0xD7010000u, 44u}, // byv -> Latn
-    {0xDF010000u, 44u}, // byx -> Latn
-    {0x83210000u, 44u}, // bza -> Latn
-    {0x93210000u, 44u}, // bze -> Latn
-    {0x97210000u, 44u}, // bzf -> Latn
-    {0x9F210000u, 44u}, // bzh -> Latn
-    {0xDB210000u, 44u}, // bzw -> Latn
-    {0x63610000u, 44u}, // ca -> Latn
-    {0xB4020000u, 44u}, // can -> Latn
-    {0xA4220000u, 44u}, // cbj -> Latn
-    {0x9C420000u, 44u}, // cch -> Latn
+    {0xCA410000u, 46u}, // bss -> Latn
+    {0xCE410000u, 20u}, // bst -> Ethi
+    {0xBA610000u, 46u}, // bto -> Latn
+    {0xCE610000u, 46u}, // btt -> Latn
+    {0xD6610000u, 18u}, // btv -> Deva
+    {0x82810000u, 17u}, // bua -> Cyrl
+    {0x8A810000u, 46u}, // buc -> Latn
+    {0x8E810000u, 46u}, // bud -> Latn
+    {0x9A810000u, 46u}, // bug -> Latn
+    {0xAA810000u, 46u}, // buk -> Latn
+    {0xB2810000u, 46u}, // bum -> Latn
+    {0xBA810000u, 46u}, // buo -> Latn
+    {0xCA810000u, 46u}, // bus -> Latn
+    {0xD2810000u, 46u}, // buu -> Latn
+    {0x86A10000u, 46u}, // bvb -> Latn
+    {0x8EC10000u, 46u}, // bwd -> Latn
+    {0xC6C10000u, 46u}, // bwr -> Latn
+    {0x9EE10000u, 46u}, // bxh -> Latn
+    {0x93010000u, 46u}, // bye -> Latn
+    {0xB7010000u, 20u}, // byn -> Ethi
+    {0xC7010000u, 46u}, // byr -> Latn
+    {0xCB010000u, 46u}, // bys -> Latn
+    {0xD7010000u, 46u}, // byv -> Latn
+    {0xDF010000u, 46u}, // byx -> Latn
+    {0x83210000u, 46u}, // bza -> Latn
+    {0x93210000u, 46u}, // bze -> Latn
+    {0x97210000u, 46u}, // bzf -> Latn
+    {0x9F210000u, 46u}, // bzh -> Latn
+    {0xDB210000u, 46u}, // bzw -> Latn
+    {0x63610000u, 46u}, // ca -> Latn
+    {0xB4020000u, 46u}, // can -> Latn
+    {0xA4220000u, 46u}, // cbj -> Latn
+    {0x9C420000u, 46u}, // cch -> Latn
     {0xBC420000u,  9u}, // ccp -> Cakm
-    {0x63650000u, 16u}, // ce -> Cyrl
-    {0x84820000u, 44u}, // ceb -> Latn
-    {0x80A20000u, 44u}, // cfa -> Latn
-    {0x98C20000u, 44u}, // cgg -> Latn
-    {0x63680000u, 44u}, // ch -> Latn
-    {0xA8E20000u, 44u}, // chk -> Latn
-    {0xB0E20000u, 16u}, // chm -> Cyrl
-    {0xB8E20000u, 44u}, // cho -> Latn
-    {0xBCE20000u, 44u}, // chp -> Latn
+    {0x63650000u, 17u}, // ce -> Cyrl
+    {0x84820000u, 46u}, // ceb -> Latn
+    {0x80A20000u, 46u}, // cfa -> Latn
+    {0x98C20000u, 46u}, // cgg -> Latn
+    {0x63680000u, 46u}, // ch -> Latn
+    {0xA8E20000u, 46u}, // chk -> Latn
+    {0xB0E20000u, 17u}, // chm -> Cyrl
+    {0xB8E20000u, 46u}, // cho -> Latn
+    {0xBCE20000u, 46u}, // chp -> Latn
     {0xC4E20000u, 13u}, // chr -> Cher
-    {0x89020000u, 44u}, // cic -> Latn
+    {0x89020000u, 46u}, // cic -> Latn
     {0x81220000u,  1u}, // cja -> Arab
     {0xB1220000u, 12u}, // cjm -> Cham
-    {0xD5220000u, 44u}, // cjv -> Latn
+    {0xD5220000u, 46u}, // cjv -> Latn
     {0x85420000u,  1u}, // ckb -> Arab
-    {0xAD420000u, 44u}, // ckl -> Latn
-    {0xB9420000u, 44u}, // cko -> Latn
-    {0xE1420000u, 44u}, // cky -> Latn
-    {0x81620000u, 44u}, // cla -> Latn
-    {0x91820000u, 44u}, // cme -> Latn
-    {0x99820000u, 77u}, // cmg -> Soyo
-    {0x636F0000u, 44u}, // co -> Latn
-    {0xBDC20000u, 14u}, // cop -> Copt
-    {0xC9E20000u, 44u}, // cps -> Latn
+    {0xAD420000u, 46u}, // ckl -> Latn
+    {0xB9420000u, 46u}, // cko -> Latn
+    {0xE1420000u, 46u}, // cky -> Latn
+    {0x81620000u, 46u}, // cla -> Latn
+    {0x91820000u, 46u}, // cme -> Latn
+    {0x99820000u, 79u}, // cmg -> Soyo
+    {0x636F0000u, 46u}, // co -> Latn
+    {0xBDC20000u, 15u}, // cop -> Copt
+    {0xC9E20000u, 46u}, // cps -> Latn
     {0x63720000u, 10u}, // cr -> Cans
-    {0x9E220000u, 16u}, // crh -> Cyrl
+    {0x9E220000u, 17u}, // crh -> Cyrl
     {0xA6220000u, 10u}, // crj -> Cans
     {0xAA220000u, 10u}, // crk -> Cans
     {0xAE220000u, 10u}, // crl -> Cans
     {0xB2220000u, 10u}, // crm -> Cans
-    {0xCA220000u, 44u}, // crs -> Latn
-    {0x63730000u, 44u}, // cs -> Latn
-    {0x86420000u, 44u}, // csb -> Latn
+    {0xCA220000u, 46u}, // crs -> Latn
+    {0x63730000u, 46u}, // cs -> Latn
+    {0x86420000u, 46u}, // csb -> Latn
     {0xDA420000u, 10u}, // csw -> Cans
-    {0x8E620000u, 64u}, // ctd -> Pauc
-    {0x63750000u, 16u}, // cu -> Cyrl
-    {0x63760000u, 16u}, // cv -> Cyrl
-    {0x63790000u, 44u}, // cy -> Latn
-    {0x64610000u, 44u}, // da -> Latn
-    {0x8C030000u, 44u}, // dad -> Latn
-    {0x94030000u, 44u}, // daf -> Latn
-    {0x98030000u, 44u}, // dag -> Latn
-    {0x9C030000u, 44u}, // dah -> Latn
-    {0xA8030000u, 44u}, // dak -> Latn
-    {0xC4030000u, 16u}, // dar -> Cyrl
-    {0xD4030000u, 44u}, // dav -> Latn
-    {0x8C230000u, 44u}, // dbd -> Latn
-    {0xC0230000u, 44u}, // dbq -> Latn
+    {0x8E620000u, 66u}, // ctd -> Pauc
+    {0x63750000u, 17u}, // cu -> Cyrl
+    {0x63760000u, 17u}, // cv -> Cyrl
+    {0x63790000u, 46u}, // cy -> Latn
+    {0x64610000u, 46u}, // da -> Latn
+    {0x8C030000u, 46u}, // dad -> Latn
+    {0x94030000u, 46u}, // daf -> Latn
+    {0x98030000u, 46u}, // dag -> Latn
+    {0x9C030000u, 46u}, // dah -> Latn
+    {0xA8030000u, 46u}, // dak -> Latn
+    {0xC4030000u, 17u}, // dar -> Cyrl
+    {0xD4030000u, 46u}, // dav -> Latn
+    {0x8C230000u, 46u}, // dbd -> Latn
+    {0xC0230000u, 46u}, // dbq -> Latn
     {0x88430000u,  1u}, // dcc -> Arab
-    {0xB4630000u, 44u}, // ddn -> Latn
-    {0x64650000u, 44u}, // de -> Latn
-    {0x8C830000u, 44u}, // ded -> Latn
-    {0xB4830000u, 44u}, // den -> Latn
-    {0x80C30000u, 44u}, // dga -> Latn
-    {0x9CC30000u, 44u}, // dgh -> Latn
-    {0xA0C30000u, 44u}, // dgi -> Latn
+    {0xB4630000u, 46u}, // ddn -> Latn
+    {0x64650000u, 46u}, // de -> Latn
+    {0x8C830000u, 46u}, // ded -> Latn
+    {0xB4830000u, 46u}, // den -> Latn
+    {0x80C30000u, 46u}, // dga -> Latn
+    {0x9CC30000u, 46u}, // dgh -> Latn
+    {0xA0C30000u, 46u}, // dgi -> Latn
     {0xACC30000u,  1u}, // dgl -> Arab
-    {0xC4C30000u, 44u}, // dgr -> Latn
-    {0xE4C30000u, 44u}, // dgz -> Latn
-    {0x81030000u, 44u}, // dia -> Latn
-    {0x91230000u, 44u}, // dje -> Latn
-    {0xA5A30000u, 44u}, // dnj -> Latn
-    {0x85C30000u, 44u}, // dob -> Latn
+    {0xC4C30000u, 46u}, // dgr -> Latn
+    {0xE4C30000u, 46u}, // dgz -> Latn
+    {0x81030000u, 46u}, // dia -> Latn
+    {0x91230000u, 46u}, // dje -> Latn
+    {0xA5A30000u, 46u}, // dnj -> Latn
+    {0x85C30000u, 46u}, // dob -> Latn
     {0xA1C30000u,  1u}, // doi -> Arab
-    {0xBDC30000u, 44u}, // dop -> Latn
-    {0xD9C30000u, 44u}, // dow -> Latn
-    {0x9E230000u, 54u}, // drh -> Mong
-    {0xA2230000u, 44u}, // dri -> Latn
-    {0xCA230000u, 19u}, // drs -> Ethi
-    {0x86430000u, 44u}, // dsb -> Latn
-    {0xB2630000u, 44u}, // dtm -> Latn
-    {0xBE630000u, 44u}, // dtp -> Latn
-    {0xCA630000u, 44u}, // dts -> Latn
-    {0xE2630000u, 17u}, // dty -> Deva
-    {0x82830000u, 44u}, // dua -> Latn
-    {0x8A830000u, 44u}, // duc -> Latn
-    {0x8E830000u, 44u}, // dud -> Latn
-    {0x9A830000u, 44u}, // dug -> Latn
-    {0x64760000u, 86u}, // dv -> Thaa
-    {0x82A30000u, 44u}, // dva -> Latn
-    {0xDAC30000u, 44u}, // dww -> Latn
-    {0xBB030000u, 44u}, // dyo -> Latn
-    {0xD3030000u, 44u}, // dyu -> Latn
-    {0x647A0000u, 88u}, // dz -> Tibt
-    {0x9B230000u, 44u}, // dzg -> Latn
-    {0xD0240000u, 44u}, // ebu -> Latn
-    {0x65650000u, 44u}, // ee -> Latn
-    {0xA0A40000u, 44u}, // efi -> Latn
-    {0xACC40000u, 44u}, // egl -> Latn
-    {0xE0C40000u, 18u}, // egy -> Egyp
-    {0x81440000u, 44u}, // eka -> Latn
-    {0xE1440000u, 36u}, // eky -> Kali
-    {0x656C0000u, 24u}, // el -> Grek
-    {0x81840000u, 44u}, // ema -> Latn
-    {0xA1840000u, 44u}, // emi -> Latn
-    {0x656E0000u, 44u}, // en -> Latn
-    {0x656E5841u, 95u}, // en-XA -> ~~~A
-    {0xB5A40000u, 44u}, // enn -> Latn
-    {0xC1A40000u, 44u}, // enq -> Latn
-    {0x656F0000u, 44u}, // eo -> Latn
-    {0xA2240000u, 44u}, // eri -> Latn
-    {0x65730000u, 44u}, // es -> Latn
-    {0x9A440000u, 22u}, // esg -> Gonm
-    {0xD2440000u, 44u}, // esu -> Latn
-    {0x65740000u, 44u}, // et -> Latn
-    {0xC6640000u, 44u}, // etr -> Latn
-    {0xCE640000u, 34u}, // ett -> Ital
-    {0xD2640000u, 44u}, // etu -> Latn
-    {0xDE640000u, 44u}, // etx -> Latn
-    {0x65750000u, 44u}, // eu -> Latn
-    {0xBAC40000u, 44u}, // ewo -> Latn
-    {0xCEE40000u, 44u}, // ext -> Latn
+    {0xBDC30000u, 46u}, // dop -> Latn
+    {0xD9C30000u, 46u}, // dow -> Latn
+    {0x9E230000u, 56u}, // drh -> Mong
+    {0xA2230000u, 46u}, // dri -> Latn
+    {0xCA230000u, 20u}, // drs -> Ethi
+    {0x86430000u, 46u}, // dsb -> Latn
+    {0xB2630000u, 46u}, // dtm -> Latn
+    {0xBE630000u, 46u}, // dtp -> Latn
+    {0xCA630000u, 46u}, // dts -> Latn
+    {0xE2630000u, 18u}, // dty -> Deva
+    {0x82830000u, 46u}, // dua -> Latn
+    {0x8A830000u, 46u}, // duc -> Latn
+    {0x8E830000u, 46u}, // dud -> Latn
+    {0x9A830000u, 46u}, // dug -> Latn
+    {0x64760000u, 88u}, // dv -> Thaa
+    {0x82A30000u, 46u}, // dva -> Latn
+    {0xDAC30000u, 46u}, // dww -> Latn
+    {0xBB030000u, 46u}, // dyo -> Latn
+    {0xD3030000u, 46u}, // dyu -> Latn
+    {0x647A0000u, 90u}, // dz -> Tibt
+    {0x9B230000u, 46u}, // dzg -> Latn
+    {0xD0240000u, 46u}, // ebu -> Latn
+    {0x65650000u, 46u}, // ee -> Latn
+    {0xA0A40000u, 46u}, // efi -> Latn
+    {0xACC40000u, 46u}, // egl -> Latn
+    {0xE0C40000u, 19u}, // egy -> Egyp
+    {0x81440000u, 46u}, // eka -> Latn
+    {0xE1440000u, 37u}, // eky -> Kali
+    {0x656C0000u, 25u}, // el -> Grek
+    {0x81840000u, 46u}, // ema -> Latn
+    {0xA1840000u, 46u}, // emi -> Latn
+    {0x656E0000u, 46u}, // en -> Latn
+    {0x656E5841u, 97u}, // en-XA -> ~~~A
+    {0xB5A40000u, 46u}, // enn -> Latn
+    {0xC1A40000u, 46u}, // enq -> Latn
+    {0x656F0000u, 46u}, // eo -> Latn
+    {0xA2240000u, 46u}, // eri -> Latn
+    {0x65730000u, 46u}, // es -> Latn
+    {0x9A440000u, 23u}, // esg -> Gonm
+    {0xD2440000u, 46u}, // esu -> Latn
+    {0x65740000u, 46u}, // et -> Latn
+    {0xC6640000u, 46u}, // etr -> Latn
+    {0xCE640000u, 35u}, // ett -> Ital
+    {0xD2640000u, 46u}, // etu -> Latn
+    {0xDE640000u, 46u}, // etx -> Latn
+    {0x65750000u, 46u}, // eu -> Latn
+    {0xBAC40000u, 46u}, // ewo -> Latn
+    {0xCEE40000u, 46u}, // ext -> Latn
     {0x66610000u,  1u}, // fa -> Arab
-    {0x80050000u, 44u}, // faa -> Latn
-    {0x84050000u, 44u}, // fab -> Latn
-    {0x98050000u, 44u}, // fag -> Latn
-    {0xA0050000u, 44u}, // fai -> Latn
-    {0xB4050000u, 44u}, // fan -> Latn
-    {0x66660000u, 44u}, // ff -> Latn
-    {0xA0A50000u, 44u}, // ffi -> Latn
-    {0xB0A50000u, 44u}, // ffm -> Latn
-    {0x66690000u, 44u}, // fi -> Latn
+    {0x80050000u, 46u}, // faa -> Latn
+    {0x84050000u, 46u}, // fab -> Latn
+    {0x98050000u, 46u}, // fag -> Latn
+    {0xA0050000u, 46u}, // fai -> Latn
+    {0xB4050000u, 46u}, // fan -> Latn
+    {0x66660000u, 46u}, // ff -> Latn
+    {0xA0A50000u, 46u}, // ffi -> Latn
+    {0xB0A50000u, 46u}, // ffm -> Latn
+    {0x66690000u, 46u}, // fi -> Latn
     {0x81050000u,  1u}, // fia -> Arab
-    {0xAD050000u, 44u}, // fil -> Latn
-    {0xCD050000u, 44u}, // fit -> Latn
-    {0x666A0000u, 44u}, // fj -> Latn
-    {0xC5650000u, 44u}, // flr -> Latn
-    {0xBD850000u, 44u}, // fmp -> Latn
-    {0x666F0000u, 44u}, // fo -> Latn
-    {0x8DC50000u, 44u}, // fod -> Latn
-    {0xB5C50000u, 44u}, // fon -> Latn
-    {0xC5C50000u, 44u}, // for -> Latn
-    {0x91E50000u, 44u}, // fpe -> Latn
-    {0xCA050000u, 44u}, // fqs -> Latn
-    {0x66720000u, 44u}, // fr -> Latn
-    {0x8A250000u, 44u}, // frc -> Latn
-    {0xBE250000u, 44u}, // frp -> Latn
-    {0xC6250000u, 44u}, // frr -> Latn
-    {0xCA250000u, 44u}, // frs -> Latn
+    {0xAD050000u, 46u}, // fil -> Latn
+    {0xCD050000u, 46u}, // fit -> Latn
+    {0x666A0000u, 46u}, // fj -> Latn
+    {0xC5650000u, 46u}, // flr -> Latn
+    {0xBD850000u, 46u}, // fmp -> Latn
+    {0x666F0000u, 46u}, // fo -> Latn
+    {0x8DC50000u, 46u}, // fod -> Latn
+    {0xB5C50000u, 46u}, // fon -> Latn
+    {0xC5C50000u, 46u}, // for -> Latn
+    {0x91E50000u, 46u}, // fpe -> Latn
+    {0xCA050000u, 46u}, // fqs -> Latn
+    {0x66720000u, 46u}, // fr -> Latn
+    {0x8A250000u, 46u}, // frc -> Latn
+    {0xBE250000u, 46u}, // frp -> Latn
+    {0xC6250000u, 46u}, // frr -> Latn
+    {0xCA250000u, 46u}, // frs -> Latn
     {0x86850000u,  1u}, // fub -> Arab
-    {0x8E850000u, 44u}, // fud -> Latn
-    {0x92850000u, 44u}, // fue -> Latn
-    {0x96850000u, 44u}, // fuf -> Latn
-    {0x9E850000u, 44u}, // fuh -> Latn
-    {0xC2850000u, 44u}, // fuq -> Latn
-    {0xC6850000u, 44u}, // fur -> Latn
-    {0xD6850000u, 44u}, // fuv -> Latn
-    {0xE2850000u, 44u}, // fuy -> Latn
-    {0xC6A50000u, 44u}, // fvr -> Latn
-    {0x66790000u, 44u}, // fy -> Latn
-    {0x67610000u, 44u}, // ga -> Latn
-    {0x80060000u, 44u}, // gaa -> Latn
-    {0x94060000u, 44u}, // gaf -> Latn
-    {0x98060000u, 44u}, // gag -> Latn
-    {0x9C060000u, 44u}, // gah -> Latn
-    {0xA4060000u, 44u}, // gaj -> Latn
-    {0xB0060000u, 44u}, // gam -> Latn
-    {0xB4060000u, 27u}, // gan -> Hans
-    {0xD8060000u, 44u}, // gaw -> Latn
-    {0xE0060000u, 44u}, // gay -> Latn
-    {0x80260000u, 44u}, // gba -> Latn
-    {0x94260000u, 44u}, // gbf -> Latn
-    {0xB0260000u, 17u}, // gbm -> Deva
-    {0xE0260000u, 44u}, // gby -> Latn
+    {0x8E850000u, 46u}, // fud -> Latn
+    {0x92850000u, 46u}, // fue -> Latn
+    {0x96850000u, 46u}, // fuf -> Latn
+    {0x9E850000u, 46u}, // fuh -> Latn
+    {0xC2850000u, 46u}, // fuq -> Latn
+    {0xC6850000u, 46u}, // fur -> Latn
+    {0xD6850000u, 46u}, // fuv -> Latn
+    {0xE2850000u, 46u}, // fuy -> Latn
+    {0xC6A50000u, 46u}, // fvr -> Latn
+    {0x66790000u, 46u}, // fy -> Latn
+    {0x67610000u, 46u}, // ga -> Latn
+    {0x80060000u, 46u}, // gaa -> Latn
+    {0x94060000u, 46u}, // gaf -> Latn
+    {0x98060000u, 46u}, // gag -> Latn
+    {0x9C060000u, 46u}, // gah -> Latn
+    {0xA4060000u, 46u}, // gaj -> Latn
+    {0xB0060000u, 46u}, // gam -> Latn
+    {0xB4060000u, 28u}, // gan -> Hans
+    {0xD8060000u, 46u}, // gaw -> Latn
+    {0xE0060000u, 46u}, // gay -> Latn
+    {0x80260000u, 46u}, // gba -> Latn
+    {0x94260000u, 46u}, // gbf -> Latn
+    {0xB0260000u, 18u}, // gbm -> Deva
+    {0xE0260000u, 46u}, // gby -> Latn
     {0xE4260000u,  1u}, // gbz -> Arab
-    {0xC4460000u, 44u}, // gcr -> Latn
-    {0x67640000u, 44u}, // gd -> Latn
-    {0x90660000u, 44u}, // gde -> Latn
-    {0xB4660000u, 44u}, // gdn -> Latn
-    {0xC4660000u, 44u}, // gdr -> Latn
-    {0x84860000u, 44u}, // geb -> Latn
-    {0xA4860000u, 44u}, // gej -> Latn
-    {0xAC860000u, 44u}, // gel -> Latn
-    {0xE4860000u, 19u}, // gez -> Ethi
-    {0xA8A60000u, 44u}, // gfk -> Latn
-    {0xB4C60000u, 17u}, // ggn -> Deva
-    {0xC8E60000u, 44u}, // ghs -> Latn
-    {0xAD060000u, 44u}, // gil -> Latn
-    {0xB1060000u, 44u}, // gim -> Latn
+    {0xC4460000u, 46u}, // gcr -> Latn
+    {0x67640000u, 46u}, // gd -> Latn
+    {0x90660000u, 46u}, // gde -> Latn
+    {0xB4660000u, 46u}, // gdn -> Latn
+    {0xC4660000u, 46u}, // gdr -> Latn
+    {0x84860000u, 46u}, // geb -> Latn
+    {0xA4860000u, 46u}, // gej -> Latn
+    {0xAC860000u, 46u}, // gel -> Latn
+    {0xE4860000u, 20u}, // gez -> Ethi
+    {0xA8A60000u, 46u}, // gfk -> Latn
+    {0xB4C60000u, 18u}, // ggn -> Deva
+    {0xC8E60000u, 46u}, // ghs -> Latn
+    {0xAD060000u, 46u}, // gil -> Latn
+    {0xB1060000u, 46u}, // gim -> Latn
     {0xA9260000u,  1u}, // gjk -> Arab
-    {0xB5260000u, 44u}, // gjn -> Latn
+    {0xB5260000u, 46u}, // gjn -> Latn
     {0xD1260000u,  1u}, // gju -> Arab
-    {0xB5460000u, 44u}, // gkn -> Latn
-    {0xBD460000u, 44u}, // gkp -> Latn
-    {0x676C0000u, 44u}, // gl -> Latn
+    {0xB5460000u, 46u}, // gkn -> Latn
+    {0xBD460000u, 46u}, // gkp -> Latn
+    {0x676C0000u, 46u}, // gl -> Latn
     {0xA9660000u,  1u}, // glk -> Arab
-    {0xB1860000u, 44u}, // gmm -> Latn
-    {0xD5860000u, 19u}, // gmv -> Ethi
-    {0x676E0000u, 44u}, // gn -> Latn
-    {0x8DA60000u, 44u}, // gnd -> Latn
-    {0x99A60000u, 44u}, // gng -> Latn
-    {0x8DC60000u, 44u}, // god -> Latn
-    {0x95C60000u, 19u}, // gof -> Ethi
-    {0xA1C60000u, 44u}, // goi -> Latn
-    {0xB1C60000u, 17u}, // gom -> Deva
-    {0xB5C60000u, 84u}, // gon -> Telu
-    {0xC5C60000u, 44u}, // gor -> Latn
-    {0xC9C60000u, 44u}, // gos -> Latn
-    {0xCDC60000u, 23u}, // got -> Goth
-    {0x86260000u, 44u}, // grb -> Latn
-    {0x8A260000u, 15u}, // grc -> Cprt
+    {0xB1860000u, 46u}, // gmm -> Latn
+    {0xD5860000u, 20u}, // gmv -> Ethi
+    {0x676E0000u, 46u}, // gn -> Latn
+    {0x8DA60000u, 46u}, // gnd -> Latn
+    {0x99A60000u, 46u}, // gng -> Latn
+    {0x8DC60000u, 46u}, // god -> Latn
+    {0x95C60000u, 20u}, // gof -> Ethi
+    {0xA1C60000u, 46u}, // goi -> Latn
+    {0xB1C60000u, 18u}, // gom -> Deva
+    {0xB5C60000u, 86u}, // gon -> Telu
+    {0xC5C60000u, 46u}, // gor -> Latn
+    {0xC9C60000u, 46u}, // gos -> Latn
+    {0xCDC60000u, 24u}, // got -> Goth
+    {0x86260000u, 46u}, // grb -> Latn
+    {0x8A260000u, 16u}, // grc -> Cprt
     {0xCE260000u,  7u}, // grt -> Beng
-    {0xDA260000u, 44u}, // grw -> Latn
-    {0xDA460000u, 44u}, // gsw -> Latn
-    {0x67750000u, 25u}, // gu -> Gujr
-    {0x86860000u, 44u}, // gub -> Latn
-    {0x8A860000u, 44u}, // guc -> Latn
-    {0x8E860000u, 44u}, // gud -> Latn
-    {0xC6860000u, 44u}, // gur -> Latn
-    {0xDA860000u, 44u}, // guw -> Latn
-    {0xDE860000u, 44u}, // gux -> Latn
-    {0xE6860000u, 44u}, // guz -> Latn
-    {0x67760000u, 44u}, // gv -> Latn
-    {0x96A60000u, 44u}, // gvf -> Latn
-    {0xC6A60000u, 17u}, // gvr -> Deva
-    {0xCAA60000u, 44u}, // gvs -> Latn
+    {0xDA260000u, 46u}, // grw -> Latn
+    {0xDA460000u, 46u}, // gsw -> Latn
+    {0x67750000u, 26u}, // gu -> Gujr
+    {0x86860000u, 46u}, // gub -> Latn
+    {0x8A860000u, 46u}, // guc -> Latn
+    {0x8E860000u, 46u}, // gud -> Latn
+    {0xC6860000u, 46u}, // gur -> Latn
+    {0xDA860000u, 46u}, // guw -> Latn
+    {0xDE860000u, 46u}, // gux -> Latn
+    {0xE6860000u, 46u}, // guz -> Latn
+    {0x67760000u, 46u}, // gv -> Latn
+    {0x96A60000u, 46u}, // gvf -> Latn
+    {0xC6A60000u, 18u}, // gvr -> Deva
+    {0xCAA60000u, 46u}, // gvs -> Latn
     {0x8AC60000u,  1u}, // gwc -> Arab
-    {0xA2C60000u, 44u}, // gwi -> Latn
+    {0xA2C60000u, 46u}, // gwi -> Latn
     {0xCEC60000u,  1u}, // gwt -> Arab
-    {0xA3060000u, 44u}, // gyi -> Latn
-    {0x68610000u, 44u}, // ha -> Latn
+    {0xA3060000u, 46u}, // gyi -> Latn
+    {0x68610000u, 46u}, // ha -> Latn
     {0x6861434Du,  1u}, // ha-CM -> Arab
     {0x68615344u,  1u}, // ha-SD -> Arab
-    {0x98070000u, 44u}, // hag -> Latn
-    {0xA8070000u, 27u}, // hak -> Hans
-    {0xB0070000u, 44u}, // ham -> Latn
-    {0xD8070000u, 44u}, // haw -> Latn
+    {0x98070000u, 46u}, // hag -> Latn
+    {0xA8070000u, 28u}, // hak -> Hans
+    {0xB0070000u, 46u}, // ham -> Latn
+    {0xD8070000u, 46u}, // haw -> Latn
     {0xE4070000u,  1u}, // haz -> Arab
-    {0x84270000u, 44u}, // hbb -> Latn
-    {0xE0670000u, 19u}, // hdy -> Ethi
-    {0x68650000u, 30u}, // he -> Hebr
-    {0xE0E70000u, 44u}, // hhy -> Latn
-    {0x68690000u, 17u}, // hi -> Deva
-    {0x81070000u, 44u}, // hia -> Latn
-    {0x95070000u, 44u}, // hif -> Latn
-    {0x99070000u, 44u}, // hig -> Latn
-    {0x9D070000u, 44u}, // hih -> Latn
-    {0xAD070000u, 44u}, // hil -> Latn
-    {0x81670000u, 44u}, // hla -> Latn
-    {0xD1670000u, 31u}, // hlu -> Hluw
-    {0x8D870000u, 67u}, // hmd -> Plrd
-    {0xCD870000u, 44u}, // hmt -> Latn
+    {0x84270000u, 46u}, // hbb -> Latn
+    {0xE0670000u, 20u}, // hdy -> Ethi
+    {0x68650000u, 31u}, // he -> Hebr
+    {0xE0E70000u, 46u}, // hhy -> Latn
+    {0x68690000u, 18u}, // hi -> Deva
+    {0x81070000u, 46u}, // hia -> Latn
+    {0x95070000u, 46u}, // hif -> Latn
+    {0x99070000u, 46u}, // hig -> Latn
+    {0x9D070000u, 46u}, // hih -> Latn
+    {0xAD070000u, 46u}, // hil -> Latn
+    {0x81670000u, 46u}, // hla -> Latn
+    {0xD1670000u, 32u}, // hlu -> Hluw
+    {0x8D870000u, 69u}, // hmd -> Plrd
+    {0xCD870000u, 46u}, // hmt -> Latn
     {0x8DA70000u,  1u}, // hnd -> Arab
-    {0x91A70000u, 17u}, // hne -> Deva
-    {0xA5A70000u, 32u}, // hnj -> Hmng
-    {0xB5A70000u, 44u}, // hnn -> Latn
+    {0x91A70000u, 18u}, // hne -> Deva
+    {0xA5A70000u, 33u}, // hnj -> Hmng
+    {0xB5A70000u, 46u}, // hnn -> Latn
     {0xB9A70000u,  1u}, // hno -> Arab
-    {0x686F0000u, 44u}, // ho -> Latn
-    {0x89C70000u, 17u}, // hoc -> Deva
-    {0xA5C70000u, 17u}, // hoj -> Deva
-    {0xCDC70000u, 44u}, // hot -> Latn
-    {0x68720000u, 44u}, // hr -> Latn
-    {0x86470000u, 44u}, // hsb -> Latn
-    {0xB6470000u, 27u}, // hsn -> Hans
-    {0x68740000u, 44u}, // ht -> Latn
-    {0x68750000u, 44u}, // hu -> Latn
-    {0xA2870000u, 44u}, // hui -> Latn
+    {0x686F0000u, 46u}, // ho -> Latn
+    {0x89C70000u, 18u}, // hoc -> Deva
+    {0xA5C70000u, 18u}, // hoj -> Deva
+    {0xCDC70000u, 46u}, // hot -> Latn
+    {0x68720000u, 46u}, // hr -> Latn
+    {0x86470000u, 46u}, // hsb -> Latn
+    {0xB6470000u, 28u}, // hsn -> Hans
+    {0x68740000u, 46u}, // ht -> Latn
+    {0x68750000u, 46u}, // hu -> Latn
+    {0xA2870000u, 46u}, // hui -> Latn
     {0x68790000u,  3u}, // hy -> Armn
-    {0x687A0000u, 44u}, // hz -> Latn
-    {0x69610000u, 44u}, // ia -> Latn
-    {0xB4080000u, 44u}, // ian -> Latn
-    {0xC4080000u, 44u}, // iar -> Latn
-    {0x80280000u, 44u}, // iba -> Latn
-    {0x84280000u, 44u}, // ibb -> Latn
-    {0xE0280000u, 44u}, // iby -> Latn
-    {0x80480000u, 44u}, // ica -> Latn
-    {0x9C480000u, 44u}, // ich -> Latn
-    {0x69640000u, 44u}, // id -> Latn
-    {0x8C680000u, 44u}, // idd -> Latn
-    {0xA0680000u, 44u}, // idi -> Latn
-    {0xD0680000u, 44u}, // idu -> Latn
-    {0x90A80000u, 44u}, // ife -> Latn
-    {0x69670000u, 44u}, // ig -> Latn
-    {0x84C80000u, 44u}, // igb -> Latn
-    {0x90C80000u, 44u}, // ige -> Latn
-    {0x69690000u, 94u}, // ii -> Yiii
-    {0xA5280000u, 44u}, // ijj -> Latn
-    {0x696B0000u, 44u}, // ik -> Latn
-    {0xA9480000u, 44u}, // ikk -> Latn
-    {0xCD480000u, 44u}, // ikt -> Latn
-    {0xD9480000u, 44u}, // ikw -> Latn
-    {0xDD480000u, 44u}, // ikx -> Latn
-    {0xB9680000u, 44u}, // ilo -> Latn
-    {0xB9880000u, 44u}, // imo -> Latn
-    {0x696E0000u, 44u}, // in -> Latn
-    {0x9DA80000u, 16u}, // inh -> Cyrl
-    {0x696F0000u, 44u}, // io -> Latn
-    {0xD1C80000u, 44u}, // iou -> Latn
-    {0xA2280000u, 44u}, // iri -> Latn
-    {0x69730000u, 44u}, // is -> Latn
-    {0x69740000u, 44u}, // it -> Latn
+    {0x687A0000u, 46u}, // hz -> Latn
+    {0x69610000u, 46u}, // ia -> Latn
+    {0xB4080000u, 46u}, // ian -> Latn
+    {0xC4080000u, 46u}, // iar -> Latn
+    {0x80280000u, 46u}, // iba -> Latn
+    {0x84280000u, 46u}, // ibb -> Latn
+    {0xE0280000u, 46u}, // iby -> Latn
+    {0x80480000u, 46u}, // ica -> Latn
+    {0x9C480000u, 46u}, // ich -> Latn
+    {0x69640000u, 46u}, // id -> Latn
+    {0x8C680000u, 46u}, // idd -> Latn
+    {0xA0680000u, 46u}, // idi -> Latn
+    {0xD0680000u, 46u}, // idu -> Latn
+    {0x90A80000u, 46u}, // ife -> Latn
+    {0x69670000u, 46u}, // ig -> Latn
+    {0x84C80000u, 46u}, // igb -> Latn
+    {0x90C80000u, 46u}, // ige -> Latn
+    {0x69690000u, 96u}, // ii -> Yiii
+    {0xA5280000u, 46u}, // ijj -> Latn
+    {0x696B0000u, 46u}, // ik -> Latn
+    {0xA9480000u, 46u}, // ikk -> Latn
+    {0xCD480000u, 46u}, // ikt -> Latn
+    {0xD9480000u, 46u}, // ikw -> Latn
+    {0xDD480000u, 46u}, // ikx -> Latn
+    {0xB9680000u, 46u}, // ilo -> Latn
+    {0xB9880000u, 46u}, // imo -> Latn
+    {0x696E0000u, 46u}, // in -> Latn
+    {0x9DA80000u, 17u}, // inh -> Cyrl
+    {0x696F0000u, 46u}, // io -> Latn
+    {0xD1C80000u, 46u}, // iou -> Latn
+    {0xA2280000u, 46u}, // iri -> Latn
+    {0x69730000u, 46u}, // is -> Latn
+    {0x69740000u, 46u}, // it -> Latn
     {0x69750000u, 10u}, // iu -> Cans
-    {0x69770000u, 30u}, // iw -> Hebr
-    {0xB2C80000u, 44u}, // iwm -> Latn
-    {0xCAC80000u, 44u}, // iws -> Latn
-    {0x9F280000u, 44u}, // izh -> Latn
-    {0xA3280000u, 44u}, // izi -> Latn
-    {0x6A610000u, 35u}, // ja -> Jpan
-    {0x84090000u, 44u}, // jab -> Latn
-    {0xB0090000u, 44u}, // jam -> Latn
-    {0xB8290000u, 44u}, // jbo -> Latn
-    {0xD0290000u, 44u}, // jbu -> Latn
-    {0xB4890000u, 44u}, // jen -> Latn
-    {0xA8C90000u, 44u}, // jgk -> Latn
-    {0xB8C90000u, 44u}, // jgo -> Latn
-    {0x6A690000u, 30u}, // ji -> Hebr
-    {0x85090000u, 44u}, // jib -> Latn
-    {0x89890000u, 44u}, // jmc -> Latn
-    {0xAD890000u, 17u}, // jml -> Deva
-    {0x82290000u, 44u}, // jra -> Latn
-    {0xCE890000u, 44u}, // jut -> Latn
-    {0x6A760000u, 44u}, // jv -> Latn
-    {0x6A770000u, 44u}, // jw -> Latn
-    {0x6B610000u, 20u}, // ka -> Geor
-    {0x800A0000u, 16u}, // kaa -> Cyrl
-    {0x840A0000u, 44u}, // kab -> Latn
-    {0x880A0000u, 44u}, // kac -> Latn
-    {0x8C0A0000u, 44u}, // kad -> Latn
-    {0xA00A0000u, 44u}, // kai -> Latn
-    {0xA40A0000u, 44u}, // kaj -> Latn
-    {0xB00A0000u, 44u}, // kam -> Latn
-    {0xB80A0000u, 44u}, // kao -> Latn
-    {0x8C2A0000u, 16u}, // kbd -> Cyrl
-    {0xB02A0000u, 44u}, // kbm -> Latn
-    {0xBC2A0000u, 44u}, // kbp -> Latn
-    {0xC02A0000u, 44u}, // kbq -> Latn
-    {0xDC2A0000u, 44u}, // kbx -> Latn
+    {0x69770000u, 31u}, // iw -> Hebr
+    {0xB2C80000u, 46u}, // iwm -> Latn
+    {0xCAC80000u, 46u}, // iws -> Latn
+    {0x9F280000u, 46u}, // izh -> Latn
+    {0xA3280000u, 46u}, // izi -> Latn
+    {0x6A610000u, 36u}, // ja -> Jpan
+    {0x84090000u, 46u}, // jab -> Latn
+    {0xB0090000u, 46u}, // jam -> Latn
+    {0xB8290000u, 46u}, // jbo -> Latn
+    {0xD0290000u, 46u}, // jbu -> Latn
+    {0xB4890000u, 46u}, // jen -> Latn
+    {0xA8C90000u, 46u}, // jgk -> Latn
+    {0xB8C90000u, 46u}, // jgo -> Latn
+    {0x6A690000u, 31u}, // ji -> Hebr
+    {0x85090000u, 46u}, // jib -> Latn
+    {0x89890000u, 46u}, // jmc -> Latn
+    {0xAD890000u, 18u}, // jml -> Deva
+    {0x82290000u, 46u}, // jra -> Latn
+    {0xCE890000u, 46u}, // jut -> Latn
+    {0x6A760000u, 46u}, // jv -> Latn
+    {0x6A770000u, 46u}, // jw -> Latn
+    {0x6B610000u, 21u}, // ka -> Geor
+    {0x800A0000u, 17u}, // kaa -> Cyrl
+    {0x840A0000u, 46u}, // kab -> Latn
+    {0x880A0000u, 46u}, // kac -> Latn
+    {0x8C0A0000u, 46u}, // kad -> Latn
+    {0xA00A0000u, 46u}, // kai -> Latn
+    {0xA40A0000u, 46u}, // kaj -> Latn
+    {0xB00A0000u, 46u}, // kam -> Latn
+    {0xB80A0000u, 46u}, // kao -> Latn
+    {0x8C2A0000u, 17u}, // kbd -> Cyrl
+    {0xB02A0000u, 46u}, // kbm -> Latn
+    {0xBC2A0000u, 46u}, // kbp -> Latn
+    {0xC02A0000u, 46u}, // kbq -> Latn
+    {0xDC2A0000u, 46u}, // kbx -> Latn
     {0xE02A0000u,  1u}, // kby -> Arab
-    {0x984A0000u, 44u}, // kcg -> Latn
-    {0xA84A0000u, 44u}, // kck -> Latn
-    {0xAC4A0000u, 44u}, // kcl -> Latn
-    {0xCC4A0000u, 44u}, // kct -> Latn
-    {0x906A0000u, 44u}, // kde -> Latn
+    {0x984A0000u, 46u}, // kcg -> Latn
+    {0xA84A0000u, 46u}, // kck -> Latn
+    {0xAC4A0000u, 46u}, // kcl -> Latn
+    {0xCC4A0000u, 46u}, // kct -> Latn
+    {0x906A0000u, 46u}, // kde -> Latn
     {0x9C6A0000u,  1u}, // kdh -> Arab
-    {0xAC6A0000u, 44u}, // kdl -> Latn
-    {0xCC6A0000u, 87u}, // kdt -> Thai
-    {0x808A0000u, 44u}, // kea -> Latn
-    {0xB48A0000u, 44u}, // ken -> Latn
-    {0xE48A0000u, 44u}, // kez -> Latn
-    {0xB8AA0000u, 44u}, // kfo -> Latn
-    {0xC4AA0000u, 17u}, // kfr -> Deva
-    {0xE0AA0000u, 17u}, // kfy -> Deva
-    {0x6B670000u, 44u}, // kg -> Latn
-    {0x90CA0000u, 44u}, // kge -> Latn
-    {0x94CA0000u, 44u}, // kgf -> Latn
-    {0xBCCA0000u, 44u}, // kgp -> Latn
-    {0x80EA0000u, 44u}, // kha -> Latn
-    {0x84EA0000u, 80u}, // khb -> Talu
-    {0xB4EA0000u, 17u}, // khn -> Deva
-    {0xC0EA0000u, 44u}, // khq -> Latn
-    {0xC8EA0000u, 44u}, // khs -> Latn
-    {0xCCEA0000u, 56u}, // kht -> Mymr
+    {0xAC6A0000u, 46u}, // kdl -> Latn
+    {0xCC6A0000u, 89u}, // kdt -> Thai
+    {0x808A0000u, 46u}, // kea -> Latn
+    {0xB48A0000u, 46u}, // ken -> Latn
+    {0xE48A0000u, 46u}, // kez -> Latn
+    {0xB8AA0000u, 46u}, // kfo -> Latn
+    {0xC4AA0000u, 18u}, // kfr -> Deva
+    {0xE0AA0000u, 18u}, // kfy -> Deva
+    {0x6B670000u, 46u}, // kg -> Latn
+    {0x90CA0000u, 46u}, // kge -> Latn
+    {0x94CA0000u, 46u}, // kgf -> Latn
+    {0xBCCA0000u, 46u}, // kgp -> Latn
+    {0x80EA0000u, 46u}, // kha -> Latn
+    {0x84EA0000u, 82u}, // khb -> Talu
+    {0xB4EA0000u, 18u}, // khn -> Deva
+    {0xC0EA0000u, 46u}, // khq -> Latn
+    {0xC8EA0000u, 46u}, // khs -> Latn
+    {0xCCEA0000u, 58u}, // kht -> Mymr
     {0xD8EA0000u,  1u}, // khw -> Arab
-    {0xE4EA0000u, 44u}, // khz -> Latn
-    {0x6B690000u, 44u}, // ki -> Latn
-    {0xA50A0000u, 44u}, // kij -> Latn
-    {0xD10A0000u, 44u}, // kiu -> Latn
-    {0xD90A0000u, 44u}, // kiw -> Latn
-    {0x6B6A0000u, 44u}, // kj -> Latn
-    {0x8D2A0000u, 44u}, // kjd -> Latn
-    {0x992A0000u, 43u}, // kjg -> Laoo
-    {0xC92A0000u, 44u}, // kjs -> Latn
-    {0xE12A0000u, 44u}, // kjy -> Latn
-    {0x6B6B0000u, 16u}, // kk -> Cyrl
+    {0xE4EA0000u, 46u}, // khz -> Latn
+    {0x6B690000u, 46u}, // ki -> Latn
+    {0xA50A0000u, 46u}, // kij -> Latn
+    {0xD10A0000u, 46u}, // kiu -> Latn
+    {0xD90A0000u, 46u}, // kiw -> Latn
+    {0x6B6A0000u, 46u}, // kj -> Latn
+    {0x8D2A0000u, 46u}, // kjd -> Latn
+    {0x992A0000u, 45u}, // kjg -> Laoo
+    {0xC92A0000u, 46u}, // kjs -> Latn
+    {0xE12A0000u, 46u}, // kjy -> Latn
+    {0x6B6B0000u, 17u}, // kk -> Cyrl
     {0x6B6B4146u,  1u}, // kk-AF -> Arab
     {0x6B6B434Eu,  1u}, // kk-CN -> Arab
     {0x6B6B4952u,  1u}, // kk-IR -> Arab
     {0x6B6B4D4Eu,  1u}, // kk-MN -> Arab
-    {0x894A0000u, 44u}, // kkc -> Latn
-    {0xA54A0000u, 44u}, // kkj -> Latn
-    {0x6B6C0000u, 44u}, // kl -> Latn
-    {0xB56A0000u, 44u}, // kln -> Latn
-    {0xC16A0000u, 44u}, // klq -> Latn
-    {0xCD6A0000u, 44u}, // klt -> Latn
-    {0xDD6A0000u, 44u}, // klx -> Latn
-    {0x6B6D0000u, 39u}, // km -> Khmr
-    {0x858A0000u, 44u}, // kmb -> Latn
-    {0x9D8A0000u, 44u}, // kmh -> Latn
-    {0xB98A0000u, 44u}, // kmo -> Latn
-    {0xC98A0000u, 44u}, // kms -> Latn
-    {0xD18A0000u, 44u}, // kmu -> Latn
-    {0xD98A0000u, 44u}, // kmw -> Latn
-    {0x6B6E0000u, 40u}, // kn -> Knda
-    {0x95AA0000u, 44u}, // knf -> Latn
-    {0xBDAA0000u, 44u}, // knp -> Latn
-    {0x6B6F0000u, 41u}, // ko -> Kore
-    {0xA1CA0000u, 16u}, // koi -> Cyrl
-    {0xA9CA0000u, 17u}, // kok -> Deva
-    {0xADCA0000u, 44u}, // kol -> Latn
-    {0xC9CA0000u, 44u}, // kos -> Latn
-    {0xE5CA0000u, 44u}, // koz -> Latn
-    {0x91EA0000u, 44u}, // kpe -> Latn
-    {0x95EA0000u, 44u}, // kpf -> Latn
-    {0xB9EA0000u, 44u}, // kpo -> Latn
-    {0xC5EA0000u, 44u}, // kpr -> Latn
-    {0xDDEA0000u, 44u}, // kpx -> Latn
-    {0x860A0000u, 44u}, // kqb -> Latn
-    {0x960A0000u, 44u}, // kqf -> Latn
-    {0xCA0A0000u, 44u}, // kqs -> Latn
-    {0xE20A0000u, 19u}, // kqy -> Ethi
-    {0x6B720000u, 44u}, // kr -> Latn
-    {0x8A2A0000u, 16u}, // krc -> Cyrl
-    {0xA22A0000u, 44u}, // kri -> Latn
-    {0xA62A0000u, 44u}, // krj -> Latn
-    {0xAE2A0000u, 44u}, // krl -> Latn
-    {0xCA2A0000u, 44u}, // krs -> Latn
-    {0xD22A0000u, 17u}, // kru -> Deva
+    {0x894A0000u, 46u}, // kkc -> Latn
+    {0xA54A0000u, 46u}, // kkj -> Latn
+    {0x6B6C0000u, 46u}, // kl -> Latn
+    {0xB56A0000u, 46u}, // kln -> Latn
+    {0xC16A0000u, 46u}, // klq -> Latn
+    {0xCD6A0000u, 46u}, // klt -> Latn
+    {0xDD6A0000u, 46u}, // klx -> Latn
+    {0x6B6D0000u, 40u}, // km -> Khmr
+    {0x858A0000u, 46u}, // kmb -> Latn
+    {0x9D8A0000u, 46u}, // kmh -> Latn
+    {0xB98A0000u, 46u}, // kmo -> Latn
+    {0xC98A0000u, 46u}, // kms -> Latn
+    {0xD18A0000u, 46u}, // kmu -> Latn
+    {0xD98A0000u, 46u}, // kmw -> Latn
+    {0x6B6E0000u, 42u}, // kn -> Knda
+    {0x95AA0000u, 46u}, // knf -> Latn
+    {0xBDAA0000u, 46u}, // knp -> Latn
+    {0x6B6F0000u, 43u}, // ko -> Kore
+    {0xA1CA0000u, 17u}, // koi -> Cyrl
+    {0xA9CA0000u, 18u}, // kok -> Deva
+    {0xADCA0000u, 46u}, // kol -> Latn
+    {0xC9CA0000u, 46u}, // kos -> Latn
+    {0xE5CA0000u, 46u}, // koz -> Latn
+    {0x91EA0000u, 46u}, // kpe -> Latn
+    {0x95EA0000u, 46u}, // kpf -> Latn
+    {0xB9EA0000u, 46u}, // kpo -> Latn
+    {0xC5EA0000u, 46u}, // kpr -> Latn
+    {0xDDEA0000u, 46u}, // kpx -> Latn
+    {0x860A0000u, 46u}, // kqb -> Latn
+    {0x960A0000u, 46u}, // kqf -> Latn
+    {0xCA0A0000u, 46u}, // kqs -> Latn
+    {0xE20A0000u, 20u}, // kqy -> Ethi
+    {0x6B720000u, 46u}, // kr -> Latn
+    {0x8A2A0000u, 17u}, // krc -> Cyrl
+    {0xA22A0000u, 46u}, // kri -> Latn
+    {0xA62A0000u, 46u}, // krj -> Latn
+    {0xAE2A0000u, 46u}, // krl -> Latn
+    {0xCA2A0000u, 46u}, // krs -> Latn
+    {0xD22A0000u, 18u}, // kru -> Deva
     {0x6B730000u,  1u}, // ks -> Arab
-    {0x864A0000u, 44u}, // ksb -> Latn
-    {0x8E4A0000u, 44u}, // ksd -> Latn
-    {0x964A0000u, 44u}, // ksf -> Latn
-    {0x9E4A0000u, 44u}, // ksh -> Latn
-    {0xA64A0000u, 44u}, // ksj -> Latn
-    {0xC64A0000u, 44u}, // ksr -> Latn
-    {0x866A0000u, 19u}, // ktb -> Ethi
-    {0xB26A0000u, 44u}, // ktm -> Latn
-    {0xBA6A0000u, 44u}, // kto -> Latn
-    {0xC66A0000u, 44u}, // ktr -> Latn
-    {0x6B750000u, 44u}, // ku -> Latn
+    {0x864A0000u, 46u}, // ksb -> Latn
+    {0x8E4A0000u, 46u}, // ksd -> Latn
+    {0x964A0000u, 46u}, // ksf -> Latn
+    {0x9E4A0000u, 46u}, // ksh -> Latn
+    {0xA64A0000u, 46u}, // ksj -> Latn
+    {0xC64A0000u, 46u}, // ksr -> Latn
+    {0x866A0000u, 20u}, // ktb -> Ethi
+    {0xB26A0000u, 46u}, // ktm -> Latn
+    {0xBA6A0000u, 46u}, // kto -> Latn
+    {0xC66A0000u, 46u}, // ktr -> Latn
+    {0x6B750000u, 46u}, // ku -> Latn
     {0x6B754952u,  1u}, // ku-IR -> Arab
     {0x6B754C42u,  1u}, // ku-LB -> Arab
-    {0x868A0000u, 44u}, // kub -> Latn
-    {0x8E8A0000u, 44u}, // kud -> Latn
-    {0x928A0000u, 44u}, // kue -> Latn
-    {0xA68A0000u, 44u}, // kuj -> Latn
-    {0xB28A0000u, 16u}, // kum -> Cyrl
-    {0xB68A0000u, 44u}, // kun -> Latn
-    {0xBE8A0000u, 44u}, // kup -> Latn
-    {0xCA8A0000u, 44u}, // kus -> Latn
-    {0x6B760000u, 16u}, // kv -> Cyrl
-    {0x9AAA0000u, 44u}, // kvg -> Latn
-    {0xC6AA0000u, 44u}, // kvr -> Latn
+    {0x868A0000u, 46u}, // kub -> Latn
+    {0x8E8A0000u, 46u}, // kud -> Latn
+    {0x928A0000u, 46u}, // kue -> Latn
+    {0xA68A0000u, 46u}, // kuj -> Latn
+    {0xB28A0000u, 17u}, // kum -> Cyrl
+    {0xB68A0000u, 46u}, // kun -> Latn
+    {0xBE8A0000u, 46u}, // kup -> Latn
+    {0xCA8A0000u, 46u}, // kus -> Latn
+    {0x6B760000u, 17u}, // kv -> Cyrl
+    {0x9AAA0000u, 46u}, // kvg -> Latn
+    {0xC6AA0000u, 46u}, // kvr -> Latn
     {0xDEAA0000u,  1u}, // kvx -> Arab
-    {0x6B770000u, 44u}, // kw -> Latn
-    {0xA6CA0000u, 44u}, // kwj -> Latn
-    {0xBACA0000u, 44u}, // kwo -> Latn
-    {0xC2CA0000u, 44u}, // kwq -> Latn
-    {0x82EA0000u, 44u}, // kxa -> Latn
-    {0x8AEA0000u, 19u}, // kxc -> Ethi
-    {0x92EA0000u, 44u}, // kxe -> Latn
-    {0xB2EA0000u, 87u}, // kxm -> Thai
+    {0x6B770000u, 46u}, // kw -> Latn
+    {0xA6CA0000u, 46u}, // kwj -> Latn
+    {0xBACA0000u, 46u}, // kwo -> Latn
+    {0xC2CA0000u, 46u}, // kwq -> Latn
+    {0x82EA0000u, 46u}, // kxa -> Latn
+    {0x8AEA0000u, 20u}, // kxc -> Ethi
+    {0x92EA0000u, 46u}, // kxe -> Latn
+    {0xB2EA0000u, 89u}, // kxm -> Thai
     {0xBEEA0000u,  1u}, // kxp -> Arab
-    {0xDAEA0000u, 44u}, // kxw -> Latn
-    {0xE6EA0000u, 44u}, // kxz -> Latn
-    {0x6B790000u, 16u}, // ky -> Cyrl
+    {0xDAEA0000u, 46u}, // kxw -> Latn
+    {0xE6EA0000u, 46u}, // kxz -> Latn
+    {0x6B790000u, 17u}, // ky -> Cyrl
     {0x6B79434Eu,  1u}, // ky-CN -> Arab
-    {0x6B795452u, 44u}, // ky-TR -> Latn
-    {0x930A0000u, 44u}, // kye -> Latn
-    {0xDF0A0000u, 44u}, // kyx -> Latn
-    {0xA72A0000u, 44u}, // kzj -> Latn
-    {0xC72A0000u, 44u}, // kzr -> Latn
-    {0xCF2A0000u, 44u}, // kzt -> Latn
-    {0x6C610000u, 44u}, // la -> Latn
-    {0x840B0000u, 46u}, // lab -> Lina
-    {0x8C0B0000u, 30u}, // lad -> Hebr
-    {0x980B0000u, 44u}, // lag -> Latn
+    {0x6B795452u, 46u}, // ky-TR -> Latn
+    {0x930A0000u, 46u}, // kye -> Latn
+    {0xDF0A0000u, 46u}, // kyx -> Latn
+    {0xA72A0000u, 46u}, // kzj -> Latn
+    {0xC72A0000u, 46u}, // kzr -> Latn
+    {0xCF2A0000u, 46u}, // kzt -> Latn
+    {0x6C610000u, 46u}, // la -> Latn
+    {0x840B0000u, 48u}, // lab -> Lina
+    {0x8C0B0000u, 31u}, // lad -> Hebr
+    {0x980B0000u, 46u}, // lag -> Latn
     {0x9C0B0000u,  1u}, // lah -> Arab
-    {0xA40B0000u, 44u}, // laj -> Latn
-    {0xC80B0000u, 44u}, // las -> Latn
-    {0x6C620000u, 44u}, // lb -> Latn
-    {0x902B0000u, 16u}, // lbe -> Cyrl
-    {0xD02B0000u, 44u}, // lbu -> Latn
-    {0xD82B0000u, 44u}, // lbw -> Latn
-    {0xB04B0000u, 44u}, // lcm -> Latn
-    {0xBC4B0000u, 87u}, // lcp -> Thai
-    {0x846B0000u, 44u}, // ldb -> Latn
-    {0x8C8B0000u, 44u}, // led -> Latn
-    {0x908B0000u, 44u}, // lee -> Latn
-    {0xB08B0000u, 44u}, // lem -> Latn
-    {0xBC8B0000u, 45u}, // lep -> Lepc
-    {0xC08B0000u, 44u}, // leq -> Latn
-    {0xD08B0000u, 44u}, // leu -> Latn
-    {0xE48B0000u, 16u}, // lez -> Cyrl
-    {0x6C670000u, 44u}, // lg -> Latn
-    {0x98CB0000u, 44u}, // lgg -> Latn
-    {0x6C690000u, 44u}, // li -> Latn
-    {0x810B0000u, 44u}, // lia -> Latn
-    {0x8D0B0000u, 44u}, // lid -> Latn
-    {0x950B0000u, 17u}, // lif -> Deva
-    {0x990B0000u, 44u}, // lig -> Latn
-    {0x9D0B0000u, 44u}, // lih -> Latn
-    {0xA50B0000u, 44u}, // lij -> Latn
-    {0xC90B0000u, 47u}, // lis -> Lisu
-    {0xBD2B0000u, 44u}, // ljp -> Latn
+    {0xA40B0000u, 46u}, // laj -> Latn
+    {0xC80B0000u, 46u}, // las -> Latn
+    {0x6C620000u, 46u}, // lb -> Latn
+    {0x902B0000u, 17u}, // lbe -> Cyrl
+    {0xD02B0000u, 46u}, // lbu -> Latn
+    {0xD82B0000u, 46u}, // lbw -> Latn
+    {0xB04B0000u, 46u}, // lcm -> Latn
+    {0xBC4B0000u, 89u}, // lcp -> Thai
+    {0x846B0000u, 46u}, // ldb -> Latn
+    {0x8C8B0000u, 46u}, // led -> Latn
+    {0x908B0000u, 46u}, // lee -> Latn
+    {0xB08B0000u, 46u}, // lem -> Latn
+    {0xBC8B0000u, 47u}, // lep -> Lepc
+    {0xC08B0000u, 46u}, // leq -> Latn
+    {0xD08B0000u, 46u}, // leu -> Latn
+    {0xE48B0000u, 17u}, // lez -> Cyrl
+    {0x6C670000u, 46u}, // lg -> Latn
+    {0x98CB0000u, 46u}, // lgg -> Latn
+    {0x6C690000u, 46u}, // li -> Latn
+    {0x810B0000u, 46u}, // lia -> Latn
+    {0x8D0B0000u, 46u}, // lid -> Latn
+    {0x950B0000u, 18u}, // lif -> Deva
+    {0x990B0000u, 46u}, // lig -> Latn
+    {0x9D0B0000u, 46u}, // lih -> Latn
+    {0xA50B0000u, 46u}, // lij -> Latn
+    {0xC90B0000u, 49u}, // lis -> Lisu
+    {0xBD2B0000u, 46u}, // ljp -> Latn
     {0xA14B0000u,  1u}, // lki -> Arab
-    {0xCD4B0000u, 44u}, // lkt -> Latn
-    {0x916B0000u, 44u}, // lle -> Latn
-    {0xB56B0000u, 44u}, // lln -> Latn
-    {0xB58B0000u, 84u}, // lmn -> Telu
-    {0xB98B0000u, 44u}, // lmo -> Latn
-    {0xBD8B0000u, 44u}, // lmp -> Latn
-    {0x6C6E0000u, 44u}, // ln -> Latn
-    {0xC9AB0000u, 44u}, // lns -> Latn
-    {0xD1AB0000u, 44u}, // lnu -> Latn
-    {0x6C6F0000u, 43u}, // lo -> Laoo
-    {0xA5CB0000u, 44u}, // loj -> Latn
-    {0xA9CB0000u, 44u}, // lok -> Latn
-    {0xADCB0000u, 44u}, // lol -> Latn
-    {0xC5CB0000u, 44u}, // lor -> Latn
-    {0xC9CB0000u, 44u}, // los -> Latn
-    {0xE5CB0000u, 44u}, // loz -> Latn
+    {0xCD4B0000u, 46u}, // lkt -> Latn
+    {0x916B0000u, 46u}, // lle -> Latn
+    {0xB56B0000u, 46u}, // lln -> Latn
+    {0xB58B0000u, 86u}, // lmn -> Telu
+    {0xB98B0000u, 46u}, // lmo -> Latn
+    {0xBD8B0000u, 46u}, // lmp -> Latn
+    {0x6C6E0000u, 46u}, // ln -> Latn
+    {0xC9AB0000u, 46u}, // lns -> Latn
+    {0xD1AB0000u, 46u}, // lnu -> Latn
+    {0x6C6F0000u, 45u}, // lo -> Laoo
+    {0xA5CB0000u, 46u}, // loj -> Latn
+    {0xA9CB0000u, 46u}, // lok -> Latn
+    {0xADCB0000u, 46u}, // lol -> Latn
+    {0xC5CB0000u, 46u}, // lor -> Latn
+    {0xC9CB0000u, 46u}, // los -> Latn
+    {0xE5CB0000u, 46u}, // loz -> Latn
     {0x8A2B0000u,  1u}, // lrc -> Arab
-    {0x6C740000u, 44u}, // lt -> Latn
-    {0x9A6B0000u, 44u}, // ltg -> Latn
-    {0x6C750000u, 44u}, // lu -> Latn
-    {0x828B0000u, 44u}, // lua -> Latn
-    {0xBA8B0000u, 44u}, // luo -> Latn
-    {0xE28B0000u, 44u}, // luy -> Latn
+    {0x6C740000u, 46u}, // lt -> Latn
+    {0x9A6B0000u, 46u}, // ltg -> Latn
+    {0x6C750000u, 46u}, // lu -> Latn
+    {0x828B0000u, 46u}, // lua -> Latn
+    {0xBA8B0000u, 46u}, // luo -> Latn
+    {0xE28B0000u, 46u}, // luy -> Latn
     {0xE68B0000u,  1u}, // luz -> Arab
-    {0x6C760000u, 44u}, // lv -> Latn
-    {0xAECB0000u, 87u}, // lwl -> Thai
-    {0x9F2B0000u, 27u}, // lzh -> Hans
-    {0xE72B0000u, 44u}, // lzz -> Latn
-    {0x8C0C0000u, 44u}, // mad -> Latn
-    {0x940C0000u, 44u}, // maf -> Latn
-    {0x980C0000u, 17u}, // mag -> Deva
-    {0xA00C0000u, 17u}, // mai -> Deva
-    {0xA80C0000u, 44u}, // mak -> Latn
-    {0xB40C0000u, 44u}, // man -> Latn
-    {0xB40C474Eu, 58u}, // man-GN -> Nkoo
-    {0xC80C0000u, 44u}, // mas -> Latn
-    {0xD80C0000u, 44u}, // maw -> Latn
-    {0xE40C0000u, 44u}, // maz -> Latn
-    {0x9C2C0000u, 44u}, // mbh -> Latn
-    {0xB82C0000u, 44u}, // mbo -> Latn
-    {0xC02C0000u, 44u}, // mbq -> Latn
-    {0xD02C0000u, 44u}, // mbu -> Latn
-    {0xD82C0000u, 44u}, // mbw -> Latn
-    {0xA04C0000u, 44u}, // mci -> Latn
-    {0xBC4C0000u, 44u}, // mcp -> Latn
-    {0xC04C0000u, 44u}, // mcq -> Latn
-    {0xC44C0000u, 44u}, // mcr -> Latn
-    {0xD04C0000u, 44u}, // mcu -> Latn
-    {0x806C0000u, 44u}, // mda -> Latn
+    {0x6C760000u, 46u}, // lv -> Latn
+    {0xAECB0000u, 89u}, // lwl -> Thai
+    {0x9F2B0000u, 28u}, // lzh -> Hans
+    {0xE72B0000u, 46u}, // lzz -> Latn
+    {0x8C0C0000u, 46u}, // mad -> Latn
+    {0x940C0000u, 46u}, // maf -> Latn
+    {0x980C0000u, 18u}, // mag -> Deva
+    {0xA00C0000u, 18u}, // mai -> Deva
+    {0xA80C0000u, 46u}, // mak -> Latn
+    {0xB40C0000u, 46u}, // man -> Latn
+    {0xB40C474Eu, 60u}, // man-GN -> Nkoo
+    {0xC80C0000u, 46u}, // mas -> Latn
+    {0xD80C0000u, 46u}, // maw -> Latn
+    {0xE40C0000u, 46u}, // maz -> Latn
+    {0x9C2C0000u, 46u}, // mbh -> Latn
+    {0xB82C0000u, 46u}, // mbo -> Latn
+    {0xC02C0000u, 46u}, // mbq -> Latn
+    {0xD02C0000u, 46u}, // mbu -> Latn
+    {0xD82C0000u, 46u}, // mbw -> Latn
+    {0xA04C0000u, 46u}, // mci -> Latn
+    {0xBC4C0000u, 46u}, // mcp -> Latn
+    {0xC04C0000u, 46u}, // mcq -> Latn
+    {0xC44C0000u, 46u}, // mcr -> Latn
+    {0xD04C0000u, 46u}, // mcu -> Latn
+    {0x806C0000u, 46u}, // mda -> Latn
     {0x906C0000u,  1u}, // mde -> Arab
-    {0x946C0000u, 16u}, // mdf -> Cyrl
-    {0x9C6C0000u, 44u}, // mdh -> Latn
-    {0xA46C0000u, 44u}, // mdj -> Latn
-    {0xC46C0000u, 44u}, // mdr -> Latn
-    {0xDC6C0000u, 19u}, // mdx -> Ethi
-    {0x8C8C0000u, 44u}, // med -> Latn
-    {0x908C0000u, 44u}, // mee -> Latn
-    {0xA88C0000u, 44u}, // mek -> Latn
-    {0xB48C0000u, 44u}, // men -> Latn
-    {0xC48C0000u, 44u}, // mer -> Latn
-    {0xCC8C0000u, 44u}, // met -> Latn
-    {0xD08C0000u, 44u}, // meu -> Latn
+    {0x946C0000u, 17u}, // mdf -> Cyrl
+    {0x9C6C0000u, 46u}, // mdh -> Latn
+    {0xA46C0000u, 46u}, // mdj -> Latn
+    {0xC46C0000u, 46u}, // mdr -> Latn
+    {0xDC6C0000u, 20u}, // mdx -> Ethi
+    {0x8C8C0000u, 46u}, // med -> Latn
+    {0x908C0000u, 46u}, // mee -> Latn
+    {0xA88C0000u, 46u}, // mek -> Latn
+    {0xB48C0000u, 46u}, // men -> Latn
+    {0xC48C0000u, 46u}, // mer -> Latn
+    {0xCC8C0000u, 46u}, // met -> Latn
+    {0xD08C0000u, 46u}, // meu -> Latn
     {0x80AC0000u,  1u}, // mfa -> Arab
-    {0x90AC0000u, 44u}, // mfe -> Latn
-    {0xB4AC0000u, 44u}, // mfn -> Latn
-    {0xB8AC0000u, 44u}, // mfo -> Latn
-    {0xC0AC0000u, 44u}, // mfq -> Latn
-    {0x6D670000u, 44u}, // mg -> Latn
-    {0x9CCC0000u, 44u}, // mgh -> Latn
-    {0xACCC0000u, 44u}, // mgl -> Latn
-    {0xB8CC0000u, 44u}, // mgo -> Latn
-    {0xBCCC0000u, 17u}, // mgp -> Deva
-    {0xE0CC0000u, 44u}, // mgy -> Latn
-    {0x6D680000u, 44u}, // mh -> Latn
-    {0xA0EC0000u, 44u}, // mhi -> Latn
-    {0xACEC0000u, 44u}, // mhl -> Latn
-    {0x6D690000u, 44u}, // mi -> Latn
-    {0x950C0000u, 44u}, // mif -> Latn
-    {0xB50C0000u, 44u}, // min -> Latn
-    {0xC90C0000u, 29u}, // mis -> Hatr
-    {0xD90C0000u, 44u}, // miw -> Latn
-    {0x6D6B0000u, 16u}, // mk -> Cyrl
+    {0x90AC0000u, 46u}, // mfe -> Latn
+    {0xB4AC0000u, 46u}, // mfn -> Latn
+    {0xB8AC0000u, 46u}, // mfo -> Latn
+    {0xC0AC0000u, 46u}, // mfq -> Latn
+    {0x6D670000u, 46u}, // mg -> Latn
+    {0x9CCC0000u, 46u}, // mgh -> Latn
+    {0xACCC0000u, 46u}, // mgl -> Latn
+    {0xB8CC0000u, 46u}, // mgo -> Latn
+    {0xBCCC0000u, 18u}, // mgp -> Deva
+    {0xE0CC0000u, 46u}, // mgy -> Latn
+    {0x6D680000u, 46u}, // mh -> Latn
+    {0xA0EC0000u, 46u}, // mhi -> Latn
+    {0xACEC0000u, 46u}, // mhl -> Latn
+    {0x6D690000u, 46u}, // mi -> Latn
+    {0x950C0000u, 46u}, // mif -> Latn
+    {0xB50C0000u, 46u}, // min -> Latn
+    {0xC90C0000u, 30u}, // mis -> Hatr
+    {0xD90C0000u, 46u}, // miw -> Latn
+    {0x6D6B0000u, 17u}, // mk -> Cyrl
     {0xA14C0000u,  1u}, // mki -> Arab
-    {0xAD4C0000u, 44u}, // mkl -> Latn
-    {0xBD4C0000u, 44u}, // mkp -> Latn
-    {0xD94C0000u, 44u}, // mkw -> Latn
-    {0x6D6C0000u, 53u}, // ml -> Mlym
-    {0x916C0000u, 44u}, // mle -> Latn
-    {0xBD6C0000u, 44u}, // mlp -> Latn
-    {0xC96C0000u, 44u}, // mls -> Latn
-    {0xB98C0000u, 44u}, // mmo -> Latn
-    {0xD18C0000u, 44u}, // mmu -> Latn
-    {0xDD8C0000u, 44u}, // mmx -> Latn
-    {0x6D6E0000u, 16u}, // mn -> Cyrl
-    {0x6D6E434Eu, 54u}, // mn-CN -> Mong
-    {0x81AC0000u, 44u}, // mna -> Latn
-    {0x95AC0000u, 44u}, // mnf -> Latn
+    {0xAD4C0000u, 46u}, // mkl -> Latn
+    {0xBD4C0000u, 46u}, // mkp -> Latn
+    {0xD94C0000u, 46u}, // mkw -> Latn
+    {0x6D6C0000u, 55u}, // ml -> Mlym
+    {0x916C0000u, 46u}, // mle -> Latn
+    {0xBD6C0000u, 46u}, // mlp -> Latn
+    {0xC96C0000u, 46u}, // mls -> Latn
+    {0xB98C0000u, 46u}, // mmo -> Latn
+    {0xD18C0000u, 46u}, // mmu -> Latn
+    {0xDD8C0000u, 46u}, // mmx -> Latn
+    {0x6D6E0000u, 17u}, // mn -> Cyrl
+    {0x6D6E434Eu, 56u}, // mn-CN -> Mong
+    {0x81AC0000u, 46u}, // mna -> Latn
+    {0x95AC0000u, 46u}, // mnf -> Latn
     {0xA1AC0000u,  7u}, // mni -> Beng
-    {0xD9AC0000u, 56u}, // mnw -> Mymr
-    {0x6D6F0000u, 44u}, // mo -> Latn
-    {0x81CC0000u, 44u}, // moa -> Latn
-    {0x91CC0000u, 44u}, // moe -> Latn
-    {0x9DCC0000u, 44u}, // moh -> Latn
-    {0xC9CC0000u, 44u}, // mos -> Latn
-    {0xDDCC0000u, 44u}, // mox -> Latn
-    {0xBDEC0000u, 44u}, // mpp -> Latn
-    {0xC9EC0000u, 44u}, // mps -> Latn
-    {0xCDEC0000u, 44u}, // mpt -> Latn
-    {0xDDEC0000u, 44u}, // mpx -> Latn
-    {0xAE0C0000u, 44u}, // mql -> Latn
-    {0x6D720000u, 17u}, // mr -> Deva
-    {0x8E2C0000u, 17u}, // mrd -> Deva
-    {0xA62C0000u, 16u}, // mrj -> Cyrl
-    {0xBA2C0000u, 55u}, // mro -> Mroo
-    {0x6D730000u, 44u}, // ms -> Latn
+    {0xD9AC0000u, 58u}, // mnw -> Mymr
+    {0x6D6F0000u, 46u}, // mo -> Latn
+    {0x81CC0000u, 46u}, // moa -> Latn
+    {0x91CC0000u, 46u}, // moe -> Latn
+    {0x9DCC0000u, 46u}, // moh -> Latn
+    {0xC9CC0000u, 46u}, // mos -> Latn
+    {0xDDCC0000u, 46u}, // mox -> Latn
+    {0xBDEC0000u, 46u}, // mpp -> Latn
+    {0xC9EC0000u, 46u}, // mps -> Latn
+    {0xCDEC0000u, 46u}, // mpt -> Latn
+    {0xDDEC0000u, 46u}, // mpx -> Latn
+    {0xAE0C0000u, 46u}, // mql -> Latn
+    {0x6D720000u, 18u}, // mr -> Deva
+    {0x8E2C0000u, 18u}, // mrd -> Deva
+    {0xA62C0000u, 17u}, // mrj -> Cyrl
+    {0xBA2C0000u, 57u}, // mro -> Mroo
+    {0x6D730000u, 46u}, // ms -> Latn
     {0x6D734343u,  1u}, // ms-CC -> Arab
     {0x6D734944u,  1u}, // ms-ID -> Arab
-    {0x6D740000u, 44u}, // mt -> Latn
-    {0x8A6C0000u, 44u}, // mtc -> Latn
-    {0x966C0000u, 44u}, // mtf -> Latn
-    {0xA26C0000u, 44u}, // mti -> Latn
-    {0xC66C0000u, 17u}, // mtr -> Deva
-    {0x828C0000u, 44u}, // mua -> Latn
-    {0xC68C0000u, 44u}, // mur -> Latn
-    {0xCA8C0000u, 44u}, // mus -> Latn
-    {0x82AC0000u, 44u}, // mva -> Latn
-    {0xB6AC0000u, 44u}, // mvn -> Latn
+    {0x6D740000u, 46u}, // mt -> Latn
+    {0x8A6C0000u, 46u}, // mtc -> Latn
+    {0x966C0000u, 46u}, // mtf -> Latn
+    {0xA26C0000u, 46u}, // mti -> Latn
+    {0xC66C0000u, 18u}, // mtr -> Deva
+    {0x828C0000u, 46u}, // mua -> Latn
+    {0xC68C0000u, 46u}, // mur -> Latn
+    {0xCA8C0000u, 46u}, // mus -> Latn
+    {0x82AC0000u, 46u}, // mva -> Latn
+    {0xB6AC0000u, 46u}, // mvn -> Latn
     {0xE2AC0000u,  1u}, // mvy -> Arab
-    {0xAACC0000u, 44u}, // mwk -> Latn
-    {0xC6CC0000u, 17u}, // mwr -> Deva
-    {0xD6CC0000u, 44u}, // mwv -> Latn
-    {0xDACC0000u, 33u}, // mww -> Hmnp
-    {0x8AEC0000u, 44u}, // mxc -> Latn
-    {0xB2EC0000u, 44u}, // mxm -> Latn
-    {0x6D790000u, 56u}, // my -> Mymr
-    {0xAB0C0000u, 44u}, // myk -> Latn
-    {0xB30C0000u, 19u}, // mym -> Ethi
-    {0xD70C0000u, 16u}, // myv -> Cyrl
-    {0xDB0C0000u, 44u}, // myw -> Latn
-    {0xDF0C0000u, 44u}, // myx -> Latn
-    {0xE70C0000u, 50u}, // myz -> Mand
-    {0xAB2C0000u, 44u}, // mzk -> Latn
-    {0xB32C0000u, 44u}, // mzm -> Latn
+    {0xAACC0000u, 46u}, // mwk -> Latn
+    {0xC6CC0000u, 18u}, // mwr -> Deva
+    {0xD6CC0000u, 46u}, // mwv -> Latn
+    {0xDACC0000u, 34u}, // mww -> Hmnp
+    {0x8AEC0000u, 46u}, // mxc -> Latn
+    {0xB2EC0000u, 46u}, // mxm -> Latn
+    {0x6D790000u, 58u}, // my -> Mymr
+    {0xAB0C0000u, 46u}, // myk -> Latn
+    {0xB30C0000u, 20u}, // mym -> Ethi
+    {0xD70C0000u, 17u}, // myv -> Cyrl
+    {0xDB0C0000u, 46u}, // myw -> Latn
+    {0xDF0C0000u, 46u}, // myx -> Latn
+    {0xE70C0000u, 52u}, // myz -> Mand
+    {0xAB2C0000u, 46u}, // mzk -> Latn
+    {0xB32C0000u, 46u}, // mzm -> Latn
     {0xB72C0000u,  1u}, // mzn -> Arab
-    {0xBF2C0000u, 44u}, // mzp -> Latn
-    {0xDB2C0000u, 44u}, // mzw -> Latn
-    {0xE72C0000u, 44u}, // mzz -> Latn
-    {0x6E610000u, 44u}, // na -> Latn
-    {0x880D0000u, 44u}, // nac -> Latn
-    {0x940D0000u, 44u}, // naf -> Latn
-    {0xA80D0000u, 44u}, // nak -> Latn
-    {0xB40D0000u, 27u}, // nan -> Hans
-    {0xBC0D0000u, 44u}, // nap -> Latn
-    {0xC00D0000u, 44u}, // naq -> Latn
-    {0xC80D0000u, 44u}, // nas -> Latn
-    {0x6E620000u, 44u}, // nb -> Latn
-    {0x804D0000u, 44u}, // nca -> Latn
-    {0x904D0000u, 44u}, // nce -> Latn
-    {0x944D0000u, 44u}, // ncf -> Latn
-    {0x9C4D0000u, 44u}, // nch -> Latn
-    {0xB84D0000u, 44u}, // nco -> Latn
-    {0xD04D0000u, 44u}, // ncu -> Latn
-    {0x6E640000u, 44u}, // nd -> Latn
-    {0x886D0000u, 44u}, // ndc -> Latn
-    {0xC86D0000u, 44u}, // nds -> Latn
-    {0x6E650000u, 17u}, // ne -> Deva
-    {0x848D0000u, 44u}, // neb -> Latn
-    {0xD88D0000u, 17u}, // new -> Deva
-    {0xDC8D0000u, 44u}, // nex -> Latn
-    {0xC4AD0000u, 44u}, // nfr -> Latn
-    {0x6E670000u, 44u}, // ng -> Latn
-    {0x80CD0000u, 44u}, // nga -> Latn
-    {0x84CD0000u, 44u}, // ngb -> Latn
-    {0xACCD0000u, 44u}, // ngl -> Latn
-    {0x84ED0000u, 44u}, // nhb -> Latn
-    {0x90ED0000u, 44u}, // nhe -> Latn
-    {0xD8ED0000u, 44u}, // nhw -> Latn
-    {0x950D0000u, 44u}, // nif -> Latn
-    {0xA10D0000u, 44u}, // nii -> Latn
-    {0xA50D0000u, 44u}, // nij -> Latn
-    {0xB50D0000u, 44u}, // nin -> Latn
-    {0xD10D0000u, 44u}, // niu -> Latn
-    {0xE10D0000u, 44u}, // niy -> Latn
-    {0xE50D0000u, 44u}, // niz -> Latn
-    {0xB92D0000u, 44u}, // njo -> Latn
-    {0x994D0000u, 44u}, // nkg -> Latn
-    {0xB94D0000u, 44u}, // nko -> Latn
-    {0x6E6C0000u, 44u}, // nl -> Latn
-    {0x998D0000u, 44u}, // nmg -> Latn
-    {0xE58D0000u, 44u}, // nmz -> Latn
-    {0x6E6E0000u, 44u}, // nn -> Latn
-    {0x95AD0000u, 44u}, // nnf -> Latn
-    {0x9DAD0000u, 44u}, // nnh -> Latn
-    {0xA9AD0000u, 44u}, // nnk -> Latn
-    {0xB1AD0000u, 44u}, // nnm -> Latn
-    {0xBDAD0000u, 91u}, // nnp -> Wcho
-    {0x6E6F0000u, 44u}, // no -> Latn
-    {0x8DCD0000u, 42u}, // nod -> Lana
-    {0x91CD0000u, 17u}, // noe -> Deva
-    {0xB5CD0000u, 69u}, // non -> Runr
-    {0xBDCD0000u, 44u}, // nop -> Latn
-    {0xD1CD0000u, 44u}, // nou -> Latn
-    {0xBA0D0000u, 58u}, // nqo -> Nkoo
-    {0x6E720000u, 44u}, // nr -> Latn
-    {0x862D0000u, 44u}, // nrb -> Latn
+    {0xBF2C0000u, 46u}, // mzp -> Latn
+    {0xDB2C0000u, 46u}, // mzw -> Latn
+    {0xE72C0000u, 46u}, // mzz -> Latn
+    {0x6E610000u, 46u}, // na -> Latn
+    {0x880D0000u, 46u}, // nac -> Latn
+    {0x940D0000u, 46u}, // naf -> Latn
+    {0xA80D0000u, 46u}, // nak -> Latn
+    {0xB40D0000u, 28u}, // nan -> Hans
+    {0xBC0D0000u, 46u}, // nap -> Latn
+    {0xC00D0000u, 46u}, // naq -> Latn
+    {0xC80D0000u, 46u}, // nas -> Latn
+    {0x6E620000u, 46u}, // nb -> Latn
+    {0x804D0000u, 46u}, // nca -> Latn
+    {0x904D0000u, 46u}, // nce -> Latn
+    {0x944D0000u, 46u}, // ncf -> Latn
+    {0x9C4D0000u, 46u}, // nch -> Latn
+    {0xB84D0000u, 46u}, // nco -> Latn
+    {0xD04D0000u, 46u}, // ncu -> Latn
+    {0x6E640000u, 46u}, // nd -> Latn
+    {0x886D0000u, 46u}, // ndc -> Latn
+    {0xC86D0000u, 46u}, // nds -> Latn
+    {0x6E650000u, 18u}, // ne -> Deva
+    {0x848D0000u, 46u}, // neb -> Latn
+    {0xD88D0000u, 18u}, // new -> Deva
+    {0xDC8D0000u, 46u}, // nex -> Latn
+    {0xC4AD0000u, 46u}, // nfr -> Latn
+    {0x6E670000u, 46u}, // ng -> Latn
+    {0x80CD0000u, 46u}, // nga -> Latn
+    {0x84CD0000u, 46u}, // ngb -> Latn
+    {0xACCD0000u, 46u}, // ngl -> Latn
+    {0x84ED0000u, 46u}, // nhb -> Latn
+    {0x90ED0000u, 46u}, // nhe -> Latn
+    {0xD8ED0000u, 46u}, // nhw -> Latn
+    {0x950D0000u, 46u}, // nif -> Latn
+    {0xA10D0000u, 46u}, // nii -> Latn
+    {0xA50D0000u, 46u}, // nij -> Latn
+    {0xB50D0000u, 46u}, // nin -> Latn
+    {0xD10D0000u, 46u}, // niu -> Latn
+    {0xE10D0000u, 46u}, // niy -> Latn
+    {0xE50D0000u, 46u}, // niz -> Latn
+    {0xB92D0000u, 46u}, // njo -> Latn
+    {0x994D0000u, 46u}, // nkg -> Latn
+    {0xB94D0000u, 46u}, // nko -> Latn
+    {0x6E6C0000u, 46u}, // nl -> Latn
+    {0x998D0000u, 46u}, // nmg -> Latn
+    {0xE58D0000u, 46u}, // nmz -> Latn
+    {0x6E6E0000u, 46u}, // nn -> Latn
+    {0x95AD0000u, 46u}, // nnf -> Latn
+    {0x9DAD0000u, 46u}, // nnh -> Latn
+    {0xA9AD0000u, 46u}, // nnk -> Latn
+    {0xB1AD0000u, 46u}, // nnm -> Latn
+    {0xBDAD0000u, 93u}, // nnp -> Wcho
+    {0x6E6F0000u, 46u}, // no -> Latn
+    {0x8DCD0000u, 44u}, // nod -> Lana
+    {0x91CD0000u, 18u}, // noe -> Deva
+    {0xB5CD0000u, 71u}, // non -> Runr
+    {0xBDCD0000u, 46u}, // nop -> Latn
+    {0xD1CD0000u, 46u}, // nou -> Latn
+    {0xBA0D0000u, 60u}, // nqo -> Nkoo
+    {0x6E720000u, 46u}, // nr -> Latn
+    {0x862D0000u, 46u}, // nrb -> Latn
     {0xAA4D0000u, 10u}, // nsk -> Cans
-    {0xB64D0000u, 44u}, // nsn -> Latn
-    {0xBA4D0000u, 44u}, // nso -> Latn
-    {0xCA4D0000u, 44u}, // nss -> Latn
-    {0xB26D0000u, 44u}, // ntm -> Latn
-    {0xC66D0000u, 44u}, // ntr -> Latn
-    {0xA28D0000u, 44u}, // nui -> Latn
-    {0xBE8D0000u, 44u}, // nup -> Latn
-    {0xCA8D0000u, 44u}, // nus -> Latn
-    {0xD68D0000u, 44u}, // nuv -> Latn
-    {0xDE8D0000u, 44u}, // nux -> Latn
-    {0x6E760000u, 44u}, // nv -> Latn
-    {0x86CD0000u, 44u}, // nwb -> Latn
-    {0xC2ED0000u, 44u}, // nxq -> Latn
-    {0xC6ED0000u, 44u}, // nxr -> Latn
-    {0x6E790000u, 44u}, // ny -> Latn
-    {0xB30D0000u, 44u}, // nym -> Latn
-    {0xB70D0000u, 44u}, // nyn -> Latn
-    {0xA32D0000u, 44u}, // nzi -> Latn
-    {0x6F630000u, 44u}, // oc -> Latn
-    {0x88CE0000u, 44u}, // ogc -> Latn
-    {0xC54E0000u, 44u}, // okr -> Latn
-    {0xD54E0000u, 44u}, // okv -> Latn
-    {0x6F6D0000u, 44u}, // om -> Latn
-    {0x99AE0000u, 44u}, // ong -> Latn
-    {0xB5AE0000u, 44u}, // onn -> Latn
-    {0xC9AE0000u, 44u}, // ons -> Latn
-    {0xB1EE0000u, 44u}, // opm -> Latn
-    {0x6F720000u, 62u}, // or -> Orya
-    {0xBA2E0000u, 44u}, // oro -> Latn
+    {0xB64D0000u, 46u}, // nsn -> Latn
+    {0xBA4D0000u, 46u}, // nso -> Latn
+    {0xCA4D0000u, 46u}, // nss -> Latn
+    {0xB26D0000u, 46u}, // ntm -> Latn
+    {0xC66D0000u, 46u}, // ntr -> Latn
+    {0xA28D0000u, 46u}, // nui -> Latn
+    {0xBE8D0000u, 46u}, // nup -> Latn
+    {0xCA8D0000u, 46u}, // nus -> Latn
+    {0xD68D0000u, 46u}, // nuv -> Latn
+    {0xDE8D0000u, 46u}, // nux -> Latn
+    {0x6E760000u, 46u}, // nv -> Latn
+    {0x86CD0000u, 46u}, // nwb -> Latn
+    {0xC2ED0000u, 46u}, // nxq -> Latn
+    {0xC6ED0000u, 46u}, // nxr -> Latn
+    {0x6E790000u, 46u}, // ny -> Latn
+    {0xB30D0000u, 46u}, // nym -> Latn
+    {0xB70D0000u, 46u}, // nyn -> Latn
+    {0xA32D0000u, 46u}, // nzi -> Latn
+    {0x6F630000u, 46u}, // oc -> Latn
+    {0x88CE0000u, 46u}, // ogc -> Latn
+    {0xC54E0000u, 46u}, // okr -> Latn
+    {0xD54E0000u, 46u}, // okv -> Latn
+    {0x6F6D0000u, 46u}, // om -> Latn
+    {0x99AE0000u, 46u}, // ong -> Latn
+    {0xB5AE0000u, 46u}, // onn -> Latn
+    {0xC9AE0000u, 46u}, // ons -> Latn
+    {0xB1EE0000u, 46u}, // opm -> Latn
+    {0x6F720000u, 64u}, // or -> Orya
+    {0xBA2E0000u, 46u}, // oro -> Latn
     {0xD22E0000u,  1u}, // oru -> Arab
-    {0x6F730000u, 16u}, // os -> Cyrl
-    {0x824E0000u, 63u}, // osa -> Osge
+    {0x6F730000u, 17u}, // os -> Cyrl
+    {0x824E0000u, 65u}, // osa -> Osge
     {0x826E0000u,  1u}, // ota -> Arab
-    {0xAA6E0000u, 61u}, // otk -> Orkh
-    {0xB32E0000u, 44u}, // ozm -> Latn
-    {0x70610000u, 26u}, // pa -> Guru
+    {0xAA6E0000u, 63u}, // otk -> Orkh
+    {0xB32E0000u, 46u}, // ozm -> Latn
+    {0x70610000u, 27u}, // pa -> Guru
     {0x7061504Bu,  1u}, // pa-PK -> Arab
-    {0x980F0000u, 44u}, // pag -> Latn
-    {0xAC0F0000u, 65u}, // pal -> Phli
-    {0xB00F0000u, 44u}, // pam -> Latn
-    {0xBC0F0000u, 44u}, // pap -> Latn
-    {0xD00F0000u, 44u}, // pau -> Latn
-    {0xA02F0000u, 44u}, // pbi -> Latn
-    {0x8C4F0000u, 44u}, // pcd -> Latn
-    {0xB04F0000u, 44u}, // pcm -> Latn
-    {0x886F0000u, 44u}, // pdc -> Latn
-    {0xCC6F0000u, 44u}, // pdt -> Latn
-    {0x8C8F0000u, 44u}, // ped -> Latn
-    {0xB88F0000u, 92u}, // peo -> Xpeo
-    {0xDC8F0000u, 44u}, // pex -> Latn
-    {0xACAF0000u, 44u}, // pfl -> Latn
+    {0x980F0000u, 46u}, // pag -> Latn
+    {0xAC0F0000u, 67u}, // pal -> Phli
+    {0xB00F0000u, 46u}, // pam -> Latn
+    {0xBC0F0000u, 46u}, // pap -> Latn
+    {0xD00F0000u, 46u}, // pau -> Latn
+    {0xA02F0000u, 46u}, // pbi -> Latn
+    {0x8C4F0000u, 46u}, // pcd -> Latn
+    {0xB04F0000u, 46u}, // pcm -> Latn
+    {0x886F0000u, 46u}, // pdc -> Latn
+    {0xCC6F0000u, 46u}, // pdt -> Latn
+    {0x8C8F0000u, 46u}, // ped -> Latn
+    {0xB88F0000u, 94u}, // peo -> Xpeo
+    {0xDC8F0000u, 46u}, // pex -> Latn
+    {0xACAF0000u, 46u}, // pfl -> Latn
     {0xACEF0000u,  1u}, // phl -> Arab
-    {0xB4EF0000u, 66u}, // phn -> Phnx
-    {0xAD0F0000u, 44u}, // pil -> Latn
-    {0xBD0F0000u, 44u}, // pip -> Latn
+    {0xB4EF0000u, 68u}, // phn -> Phnx
+    {0xAD0F0000u, 46u}, // pil -> Latn
+    {0xBD0F0000u, 46u}, // pip -> Latn
     {0x814F0000u,  8u}, // pka -> Brah
-    {0xB94F0000u, 44u}, // pko -> Latn
-    {0x706C0000u, 44u}, // pl -> Latn
-    {0x816F0000u, 44u}, // pla -> Latn
-    {0xC98F0000u, 44u}, // pms -> Latn
-    {0x99AF0000u, 44u}, // png -> Latn
-    {0xB5AF0000u, 44u}, // pnn -> Latn
-    {0xCDAF0000u, 24u}, // pnt -> Grek
-    {0xB5CF0000u, 44u}, // pon -> Latn
-    {0x81EF0000u, 17u}, // ppa -> Deva
-    {0xB9EF0000u, 44u}, // ppo -> Latn
-    {0x822F0000u, 38u}, // pra -> Khar
+    {0xB94F0000u, 46u}, // pko -> Latn
+    {0x706C0000u, 46u}, // pl -> Latn
+    {0x816F0000u, 46u}, // pla -> Latn
+    {0xC98F0000u, 46u}, // pms -> Latn
+    {0x99AF0000u, 46u}, // png -> Latn
+    {0xB5AF0000u, 46u}, // pnn -> Latn
+    {0xCDAF0000u, 25u}, // pnt -> Grek
+    {0xB5CF0000u, 46u}, // pon -> Latn
+    {0x81EF0000u, 18u}, // ppa -> Deva
+    {0xB9EF0000u, 46u}, // ppo -> Latn
+    {0x822F0000u, 39u}, // pra -> Khar
     {0x8E2F0000u,  1u}, // prd -> Arab
-    {0x9A2F0000u, 44u}, // prg -> Latn
+    {0x9A2F0000u, 46u}, // prg -> Latn
     {0x70730000u,  1u}, // ps -> Arab
-    {0xCA4F0000u, 44u}, // pss -> Latn
-    {0x70740000u, 44u}, // pt -> Latn
-    {0xBE6F0000u, 44u}, // ptp -> Latn
-    {0xD28F0000u, 44u}, // puu -> Latn
-    {0x82CF0000u, 44u}, // pwa -> Latn
-    {0x71750000u, 44u}, // qu -> Latn
-    {0x8A900000u, 44u}, // quc -> Latn
-    {0x9A900000u, 44u}, // qug -> Latn
-    {0xA0110000u, 44u}, // rai -> Latn
-    {0xA4110000u, 17u}, // raj -> Deva
-    {0xB8110000u, 44u}, // rao -> Latn
-    {0x94510000u, 44u}, // rcf -> Latn
-    {0xA4910000u, 44u}, // rej -> Latn
-    {0xAC910000u, 44u}, // rel -> Latn
-    {0xC8910000u, 44u}, // res -> Latn
-    {0xB4D10000u, 44u}, // rgn -> Latn
+    {0xCA4F0000u, 46u}, // pss -> Latn
+    {0x70740000u, 46u}, // pt -> Latn
+    {0xBE6F0000u, 46u}, // ptp -> Latn
+    {0xD28F0000u, 46u}, // puu -> Latn
+    {0x82CF0000u, 46u}, // pwa -> Latn
+    {0x71750000u, 46u}, // qu -> Latn
+    {0x8A900000u, 46u}, // quc -> Latn
+    {0x9A900000u, 46u}, // qug -> Latn
+    {0xA0110000u, 46u}, // rai -> Latn
+    {0xA4110000u, 18u}, // raj -> Deva
+    {0xB8110000u, 46u}, // rao -> Latn
+    {0x94510000u, 46u}, // rcf -> Latn
+    {0xA4910000u, 46u}, // rej -> Latn
+    {0xAC910000u, 46u}, // rel -> Latn
+    {0xC8910000u, 46u}, // res -> Latn
+    {0xB4D10000u, 46u}, // rgn -> Latn
     {0x98F10000u,  1u}, // rhg -> Arab
-    {0x81110000u, 44u}, // ria -> Latn
-    {0x95110000u, 85u}, // rif -> Tfng
-    {0x95114E4Cu, 44u}, // rif-NL -> Latn
-    {0xC9310000u, 17u}, // rjs -> Deva
+    {0x81110000u, 46u}, // ria -> Latn
+    {0x95110000u, 87u}, // rif -> Tfng
+    {0x95114E4Cu, 46u}, // rif-NL -> Latn
+    {0xC9310000u, 18u}, // rjs -> Deva
     {0xCD510000u,  7u}, // rkt -> Beng
-    {0x726D0000u, 44u}, // rm -> Latn
-    {0x95910000u, 44u}, // rmf -> Latn
-    {0xB9910000u, 44u}, // rmo -> Latn
+    {0x726D0000u, 46u}, // rm -> Latn
+    {0x95910000u, 46u}, // rmf -> Latn
+    {0xB9910000u, 46u}, // rmo -> Latn
     {0xCD910000u,  1u}, // rmt -> Arab
-    {0xD1910000u, 44u}, // rmu -> Latn
-    {0x726E0000u, 44u}, // rn -> Latn
-    {0x81B10000u, 44u}, // rna -> Latn
-    {0x99B10000u, 44u}, // rng -> Latn
-    {0x726F0000u, 44u}, // ro -> Latn
-    {0x85D10000u, 44u}, // rob -> Latn
-    {0x95D10000u, 44u}, // rof -> Latn
-    {0xB9D10000u, 44u}, // roo -> Latn
-    {0xBA310000u, 44u}, // rro -> Latn
-    {0xB2710000u, 44u}, // rtm -> Latn
-    {0x72750000u, 16u}, // ru -> Cyrl
-    {0x92910000u, 16u}, // rue -> Cyrl
-    {0x9A910000u, 44u}, // rug -> Latn
-    {0x72770000u, 44u}, // rw -> Latn
-    {0xAAD10000u, 44u}, // rwk -> Latn
-    {0xBAD10000u, 44u}, // rwo -> Latn
-    {0xD3110000u, 37u}, // ryu -> Kana
-    {0x73610000u, 17u}, // sa -> Deva
-    {0x94120000u, 44u}, // saf -> Latn
-    {0x9C120000u, 16u}, // sah -> Cyrl
-    {0xC0120000u, 44u}, // saq -> Latn
-    {0xC8120000u, 44u}, // sas -> Latn
-    {0xCC120000u, 44u}, // sat -> Latn
-    {0xD4120000u, 44u}, // sav -> Latn
-    {0xE4120000u, 72u}, // saz -> Saur
-    {0x80320000u, 44u}, // sba -> Latn
-    {0x90320000u, 44u}, // sbe -> Latn
-    {0xBC320000u, 44u}, // sbp -> Latn
-    {0x73630000u, 44u}, // sc -> Latn
-    {0xA8520000u, 17u}, // sck -> Deva
+    {0xD1910000u, 46u}, // rmu -> Latn
+    {0x726E0000u, 46u}, // rn -> Latn
+    {0x81B10000u, 46u}, // rna -> Latn
+    {0x99B10000u, 46u}, // rng -> Latn
+    {0x726F0000u, 46u}, // ro -> Latn
+    {0x85D10000u, 46u}, // rob -> Latn
+    {0x95D10000u, 46u}, // rof -> Latn
+    {0xB9D10000u, 46u}, // roo -> Latn
+    {0xBA310000u, 46u}, // rro -> Latn
+    {0xB2710000u, 46u}, // rtm -> Latn
+    {0x72750000u, 17u}, // ru -> Cyrl
+    {0x92910000u, 17u}, // rue -> Cyrl
+    {0x9A910000u, 46u}, // rug -> Latn
+    {0x72770000u, 46u}, // rw -> Latn
+    {0xAAD10000u, 46u}, // rwk -> Latn
+    {0xBAD10000u, 46u}, // rwo -> Latn
+    {0xD3110000u, 38u}, // ryu -> Kana
+    {0x73610000u, 18u}, // sa -> Deva
+    {0x94120000u, 46u}, // saf -> Latn
+    {0x9C120000u, 17u}, // sah -> Cyrl
+    {0xC0120000u, 46u}, // saq -> Latn
+    {0xC8120000u, 46u}, // sas -> Latn
+    {0xCC120000u, 46u}, // sat -> Latn
+    {0xD4120000u, 46u}, // sav -> Latn
+    {0xE4120000u, 74u}, // saz -> Saur
+    {0x80320000u, 46u}, // sba -> Latn
+    {0x90320000u, 46u}, // sbe -> Latn
+    {0xBC320000u, 46u}, // sbp -> Latn
+    {0x73630000u, 46u}, // sc -> Latn
+    {0xA8520000u, 18u}, // sck -> Deva
     {0xAC520000u,  1u}, // scl -> Arab
-    {0xB4520000u, 44u}, // scn -> Latn
-    {0xB8520000u, 44u}, // sco -> Latn
-    {0xC8520000u, 44u}, // scs -> Latn
+    {0xB4520000u, 46u}, // scn -> Latn
+    {0xB8520000u, 46u}, // sco -> Latn
+    {0xC8520000u, 46u}, // scs -> Latn
     {0x73640000u,  1u}, // sd -> Arab
-    {0x88720000u, 44u}, // sdc -> Latn
+    {0x88720000u, 46u}, // sdc -> Latn
     {0x9C720000u,  1u}, // sdh -> Arab
-    {0x73650000u, 44u}, // se -> Latn
-    {0x94920000u, 44u}, // sef -> Latn
-    {0x9C920000u, 44u}, // seh -> Latn
-    {0xA0920000u, 44u}, // sei -> Latn
-    {0xC8920000u, 44u}, // ses -> Latn
-    {0x73670000u, 44u}, // sg -> Latn
-    {0x80D20000u, 60u}, // sga -> Ogam
-    {0xC8D20000u, 44u}, // sgs -> Latn
-    {0xD8D20000u, 19u}, // sgw -> Ethi
-    {0xE4D20000u, 44u}, // sgz -> Latn
-    {0x73680000u, 44u}, // sh -> Latn
-    {0xA0F20000u, 85u}, // shi -> Tfng
-    {0xA8F20000u, 44u}, // shk -> Latn
-    {0xB4F20000u, 56u}, // shn -> Mymr
+    {0x73650000u, 46u}, // se -> Latn
+    {0x94920000u, 46u}, // sef -> Latn
+    {0x9C920000u, 46u}, // seh -> Latn
+    {0xA0920000u, 46u}, // sei -> Latn
+    {0xC8920000u, 46u}, // ses -> Latn
+    {0x73670000u, 46u}, // sg -> Latn
+    {0x80D20000u, 62u}, // sga -> Ogam
+    {0xC8D20000u, 46u}, // sgs -> Latn
+    {0xD8D20000u, 20u}, // sgw -> Ethi
+    {0xE4D20000u, 46u}, // sgz -> Latn
+    {0x73680000u, 46u}, // sh -> Latn
+    {0xA0F20000u, 87u}, // shi -> Tfng
+    {0xA8F20000u, 46u}, // shk -> Latn
+    {0xB4F20000u, 58u}, // shn -> Mymr
     {0xD0F20000u,  1u}, // shu -> Arab
-    {0x73690000u, 74u}, // si -> Sinh
-    {0x8D120000u, 44u}, // sid -> Latn
-    {0x99120000u, 44u}, // sig -> Latn
-    {0xAD120000u, 44u}, // sil -> Latn
-    {0xB1120000u, 44u}, // sim -> Latn
-    {0xC5320000u, 44u}, // sjr -> Latn
-    {0x736B0000u, 44u}, // sk -> Latn
-    {0x89520000u, 44u}, // skc -> Latn
+    {0x73690000u, 76u}, // si -> Sinh
+    {0x8D120000u, 46u}, // sid -> Latn
+    {0x99120000u, 46u}, // sig -> Latn
+    {0xAD120000u, 46u}, // sil -> Latn
+    {0xB1120000u, 46u}, // sim -> Latn
+    {0xC5320000u, 46u}, // sjr -> Latn
+    {0x736B0000u, 46u}, // sk -> Latn
+    {0x89520000u, 46u}, // skc -> Latn
     {0xC5520000u,  1u}, // skr -> Arab
-    {0xC9520000u, 44u}, // sks -> Latn
-    {0x736C0000u, 44u}, // sl -> Latn
-    {0x8D720000u, 44u}, // sld -> Latn
-    {0xA1720000u, 44u}, // sli -> Latn
-    {0xAD720000u, 44u}, // sll -> Latn
-    {0xE1720000u, 44u}, // sly -> Latn
-    {0x736D0000u, 44u}, // sm -> Latn
-    {0x81920000u, 44u}, // sma -> Latn
-    {0xA5920000u, 44u}, // smj -> Latn
-    {0xB5920000u, 44u}, // smn -> Latn
-    {0xBD920000u, 70u}, // smp -> Samr
-    {0xC1920000u, 44u}, // smq -> Latn
-    {0xC9920000u, 44u}, // sms -> Latn
-    {0x736E0000u, 44u}, // sn -> Latn
-    {0x89B20000u, 44u}, // snc -> Latn
-    {0xA9B20000u, 44u}, // snk -> Latn
-    {0xBDB20000u, 44u}, // snp -> Latn
-    {0xDDB20000u, 44u}, // snx -> Latn
-    {0xE1B20000u, 44u}, // sny -> Latn
-    {0x736F0000u, 44u}, // so -> Latn
-    {0x99D20000u, 75u}, // sog -> Sogd
-    {0xA9D20000u, 44u}, // sok -> Latn
-    {0xC1D20000u, 44u}, // soq -> Latn
-    {0xD1D20000u, 87u}, // sou -> Thai
-    {0xE1D20000u, 44u}, // soy -> Latn
-    {0x8DF20000u, 44u}, // spd -> Latn
-    {0xADF20000u, 44u}, // spl -> Latn
-    {0xC9F20000u, 44u}, // sps -> Latn
-    {0x73710000u, 44u}, // sq -> Latn
-    {0x73720000u, 16u}, // sr -> Cyrl
-    {0x73724D45u, 44u}, // sr-ME -> Latn
-    {0x7372524Fu, 44u}, // sr-RO -> Latn
-    {0x73725255u, 44u}, // sr-RU -> Latn
-    {0x73725452u, 44u}, // sr-TR -> Latn
-    {0x86320000u, 76u}, // srb -> Sora
-    {0xB6320000u, 44u}, // srn -> Latn
-    {0xC6320000u, 44u}, // srr -> Latn
-    {0xDE320000u, 17u}, // srx -> Deva
-    {0x73730000u, 44u}, // ss -> Latn
-    {0x8E520000u, 44u}, // ssd -> Latn
-    {0x9A520000u, 44u}, // ssg -> Latn
-    {0xE2520000u, 44u}, // ssy -> Latn
-    {0x73740000u, 44u}, // st -> Latn
-    {0xAA720000u, 44u}, // stk -> Latn
-    {0xC2720000u, 44u}, // stq -> Latn
-    {0x73750000u, 44u}, // su -> Latn
-    {0x82920000u, 44u}, // sua -> Latn
-    {0x92920000u, 44u}, // sue -> Latn
-    {0xAA920000u, 44u}, // suk -> Latn
-    {0xC6920000u, 44u}, // sur -> Latn
-    {0xCA920000u, 44u}, // sus -> Latn
-    {0x73760000u, 44u}, // sv -> Latn
-    {0x73770000u, 44u}, // sw -> Latn
+    {0xC9520000u, 46u}, // sks -> Latn
+    {0x736C0000u, 46u}, // sl -> Latn
+    {0x8D720000u, 46u}, // sld -> Latn
+    {0xA1720000u, 46u}, // sli -> Latn
+    {0xAD720000u, 46u}, // sll -> Latn
+    {0xE1720000u, 46u}, // sly -> Latn
+    {0x736D0000u, 46u}, // sm -> Latn
+    {0x81920000u, 46u}, // sma -> Latn
+    {0xA5920000u, 46u}, // smj -> Latn
+    {0xB5920000u, 46u}, // smn -> Latn
+    {0xBD920000u, 72u}, // smp -> Samr
+    {0xC1920000u, 46u}, // smq -> Latn
+    {0xC9920000u, 46u}, // sms -> Latn
+    {0x736E0000u, 46u}, // sn -> Latn
+    {0x89B20000u, 46u}, // snc -> Latn
+    {0xA9B20000u, 46u}, // snk -> Latn
+    {0xBDB20000u, 46u}, // snp -> Latn
+    {0xDDB20000u, 46u}, // snx -> Latn
+    {0xE1B20000u, 46u}, // sny -> Latn
+    {0x736F0000u, 46u}, // so -> Latn
+    {0x99D20000u, 77u}, // sog -> Sogd
+    {0xA9D20000u, 46u}, // sok -> Latn
+    {0xC1D20000u, 46u}, // soq -> Latn
+    {0xD1D20000u, 89u}, // sou -> Thai
+    {0xE1D20000u, 46u}, // soy -> Latn
+    {0x8DF20000u, 46u}, // spd -> Latn
+    {0xADF20000u, 46u}, // spl -> Latn
+    {0xC9F20000u, 46u}, // sps -> Latn
+    {0x73710000u, 46u}, // sq -> Latn
+    {0x73720000u, 17u}, // sr -> Cyrl
+    {0x73724D45u, 46u}, // sr-ME -> Latn
+    {0x7372524Fu, 46u}, // sr-RO -> Latn
+    {0x73725255u, 46u}, // sr-RU -> Latn
+    {0x73725452u, 46u}, // sr-TR -> Latn
+    {0x86320000u, 78u}, // srb -> Sora
+    {0xB6320000u, 46u}, // srn -> Latn
+    {0xC6320000u, 46u}, // srr -> Latn
+    {0xDE320000u, 18u}, // srx -> Deva
+    {0x73730000u, 46u}, // ss -> Latn
+    {0x8E520000u, 46u}, // ssd -> Latn
+    {0x9A520000u, 46u}, // ssg -> Latn
+    {0xE2520000u, 46u}, // ssy -> Latn
+    {0x73740000u, 46u}, // st -> Latn
+    {0xAA720000u, 46u}, // stk -> Latn
+    {0xC2720000u, 46u}, // stq -> Latn
+    {0x73750000u, 46u}, // su -> Latn
+    {0x82920000u, 46u}, // sua -> Latn
+    {0x92920000u, 46u}, // sue -> Latn
+    {0xAA920000u, 46u}, // suk -> Latn
+    {0xC6920000u, 46u}, // sur -> Latn
+    {0xCA920000u, 46u}, // sus -> Latn
+    {0x73760000u, 46u}, // sv -> Latn
+    {0x73770000u, 46u}, // sw -> Latn
     {0x86D20000u,  1u}, // swb -> Arab
-    {0x8AD20000u, 44u}, // swc -> Latn
-    {0x9AD20000u, 44u}, // swg -> Latn
-    {0xBED20000u, 44u}, // swp -> Latn
-    {0xD6D20000u, 17u}, // swv -> Deva
-    {0xB6F20000u, 44u}, // sxn -> Latn
-    {0xDAF20000u, 44u}, // sxw -> Latn
+    {0x8AD20000u, 46u}, // swc -> Latn
+    {0x9AD20000u, 46u}, // swg -> Latn
+    {0xBED20000u, 46u}, // swp -> Latn
+    {0xD6D20000u, 18u}, // swv -> Deva
+    {0xB6F20000u, 46u}, // sxn -> Latn
+    {0xDAF20000u, 46u}, // sxw -> Latn
     {0xAF120000u,  7u}, // syl -> Beng
-    {0xC7120000u, 78u}, // syr -> Syrc
-    {0xAF320000u, 44u}, // szl -> Latn
-    {0x74610000u, 81u}, // ta -> Taml
-    {0xA4130000u, 17u}, // taj -> Deva
-    {0xAC130000u, 44u}, // tal -> Latn
-    {0xB4130000u, 44u}, // tan -> Latn
-    {0xC0130000u, 44u}, // taq -> Latn
-    {0x88330000u, 44u}, // tbc -> Latn
-    {0x8C330000u, 44u}, // tbd -> Latn
-    {0x94330000u, 44u}, // tbf -> Latn
-    {0x98330000u, 44u}, // tbg -> Latn
-    {0xB8330000u, 44u}, // tbo -> Latn
-    {0xD8330000u, 44u}, // tbw -> Latn
-    {0xE4330000u, 44u}, // tbz -> Latn
-    {0xA0530000u, 44u}, // tci -> Latn
-    {0xE0530000u, 40u}, // tcy -> Knda
-    {0x8C730000u, 79u}, // tdd -> Tale
-    {0x98730000u, 17u}, // tdg -> Deva
-    {0x9C730000u, 17u}, // tdh -> Deva
-    {0xD0730000u, 44u}, // tdu -> Latn
-    {0x74650000u, 84u}, // te -> Telu
-    {0x8C930000u, 44u}, // ted -> Latn
-    {0xB0930000u, 44u}, // tem -> Latn
-    {0xB8930000u, 44u}, // teo -> Latn
-    {0xCC930000u, 44u}, // tet -> Latn
-    {0xA0B30000u, 44u}, // tfi -> Latn
-    {0x74670000u, 16u}, // tg -> Cyrl
+    {0xC7120000u, 80u}, // syr -> Syrc
+    {0xAF320000u, 46u}, // szl -> Latn
+    {0x74610000u, 83u}, // ta -> Taml
+    {0xA4130000u, 18u}, // taj -> Deva
+    {0xAC130000u, 46u}, // tal -> Latn
+    {0xB4130000u, 46u}, // tan -> Latn
+    {0xC0130000u, 46u}, // taq -> Latn
+    {0x88330000u, 46u}, // tbc -> Latn
+    {0x8C330000u, 46u}, // tbd -> Latn
+    {0x94330000u, 46u}, // tbf -> Latn
+    {0x98330000u, 46u}, // tbg -> Latn
+    {0xB8330000u, 46u}, // tbo -> Latn
+    {0xD8330000u, 46u}, // tbw -> Latn
+    {0xE4330000u, 46u}, // tbz -> Latn
+    {0xA0530000u, 46u}, // tci -> Latn
+    {0xE0530000u, 42u}, // tcy -> Knda
+    {0x8C730000u, 81u}, // tdd -> Tale
+    {0x98730000u, 18u}, // tdg -> Deva
+    {0x9C730000u, 18u}, // tdh -> Deva
+    {0xD0730000u, 46u}, // tdu -> Latn
+    {0x74650000u, 86u}, // te -> Telu
+    {0x8C930000u, 46u}, // ted -> Latn
+    {0xB0930000u, 46u}, // tem -> Latn
+    {0xB8930000u, 46u}, // teo -> Latn
+    {0xCC930000u, 46u}, // tet -> Latn
+    {0xA0B30000u, 46u}, // tfi -> Latn
+    {0x74670000u, 17u}, // tg -> Cyrl
     {0x7467504Bu,  1u}, // tg-PK -> Arab
-    {0x88D30000u, 44u}, // tgc -> Latn
-    {0xB8D30000u, 44u}, // tgo -> Latn
-    {0xD0D30000u, 44u}, // tgu -> Latn
-    {0x74680000u, 87u}, // th -> Thai
-    {0xACF30000u, 17u}, // thl -> Deva
-    {0xC0F30000u, 17u}, // thq -> Deva
-    {0xC4F30000u, 17u}, // thr -> Deva
-    {0x74690000u, 19u}, // ti -> Ethi
-    {0x95130000u, 44u}, // tif -> Latn
-    {0x99130000u, 19u}, // tig -> Ethi
-    {0xA9130000u, 44u}, // tik -> Latn
-    {0xB1130000u, 44u}, // tim -> Latn
-    {0xB9130000u, 44u}, // tio -> Latn
-    {0xD5130000u, 44u}, // tiv -> Latn
-    {0x746B0000u, 44u}, // tk -> Latn
-    {0xAD530000u, 44u}, // tkl -> Latn
-    {0xC5530000u, 44u}, // tkr -> Latn
-    {0xCD530000u, 17u}, // tkt -> Deva
-    {0x746C0000u, 44u}, // tl -> Latn
-    {0x95730000u, 44u}, // tlf -> Latn
-    {0xDD730000u, 44u}, // tlx -> Latn
-    {0xE1730000u, 44u}, // tly -> Latn
-    {0x9D930000u, 44u}, // tmh -> Latn
-    {0xE1930000u, 44u}, // tmy -> Latn
-    {0x746E0000u, 44u}, // tn -> Latn
-    {0x9DB30000u, 44u}, // tnh -> Latn
-    {0x746F0000u, 44u}, // to -> Latn
-    {0x95D30000u, 44u}, // tof -> Latn
-    {0x99D30000u, 44u}, // tog -> Latn
-    {0xC1D30000u, 44u}, // toq -> Latn
-    {0xA1F30000u, 44u}, // tpi -> Latn
-    {0xB1F30000u, 44u}, // tpm -> Latn
-    {0xE5F30000u, 44u}, // tpz -> Latn
-    {0xBA130000u, 44u}, // tqo -> Latn
-    {0x74720000u, 44u}, // tr -> Latn
-    {0xD2330000u, 44u}, // tru -> Latn
-    {0xD6330000u, 44u}, // trv -> Latn
+    {0x88D30000u, 46u}, // tgc -> Latn
+    {0xB8D30000u, 46u}, // tgo -> Latn
+    {0xD0D30000u, 46u}, // tgu -> Latn
+    {0x74680000u, 89u}, // th -> Thai
+    {0xACF30000u, 18u}, // thl -> Deva
+    {0xC0F30000u, 18u}, // thq -> Deva
+    {0xC4F30000u, 18u}, // thr -> Deva
+    {0x74690000u, 20u}, // ti -> Ethi
+    {0x95130000u, 46u}, // tif -> Latn
+    {0x99130000u, 20u}, // tig -> Ethi
+    {0xA9130000u, 46u}, // tik -> Latn
+    {0xB1130000u, 46u}, // tim -> Latn
+    {0xB9130000u, 46u}, // tio -> Latn
+    {0xD5130000u, 46u}, // tiv -> Latn
+    {0x746B0000u, 46u}, // tk -> Latn
+    {0xAD530000u, 46u}, // tkl -> Latn
+    {0xC5530000u, 46u}, // tkr -> Latn
+    {0xCD530000u, 18u}, // tkt -> Deva
+    {0x746C0000u, 46u}, // tl -> Latn
+    {0x95730000u, 46u}, // tlf -> Latn
+    {0xDD730000u, 46u}, // tlx -> Latn
+    {0xE1730000u, 46u}, // tly -> Latn
+    {0x9D930000u, 46u}, // tmh -> Latn
+    {0xE1930000u, 46u}, // tmy -> Latn
+    {0x746E0000u, 46u}, // tn -> Latn
+    {0x9DB30000u, 46u}, // tnh -> Latn
+    {0x746F0000u, 46u}, // to -> Latn
+    {0x95D30000u, 46u}, // tof -> Latn
+    {0x99D30000u, 46u}, // tog -> Latn
+    {0xC1D30000u, 46u}, // toq -> Latn
+    {0xA1F30000u, 46u}, // tpi -> Latn
+    {0xB1F30000u, 46u}, // tpm -> Latn
+    {0xE5F30000u, 46u}, // tpz -> Latn
+    {0xBA130000u, 46u}, // tqo -> Latn
+    {0x74720000u, 46u}, // tr -> Latn
+    {0xD2330000u, 46u}, // tru -> Latn
+    {0xD6330000u, 46u}, // trv -> Latn
     {0xDA330000u,  1u}, // trw -> Arab
-    {0x74730000u, 44u}, // ts -> Latn
-    {0x8E530000u, 24u}, // tsd -> Grek
-    {0x96530000u, 17u}, // tsf -> Deva
-    {0x9A530000u, 44u}, // tsg -> Latn
-    {0xA6530000u, 88u}, // tsj -> Tibt
-    {0xDA530000u, 44u}, // tsw -> Latn
-    {0x74740000u, 16u}, // tt -> Cyrl
-    {0x8E730000u, 44u}, // ttd -> Latn
-    {0x92730000u, 44u}, // tte -> Latn
-    {0xA6730000u, 44u}, // ttj -> Latn
-    {0xC6730000u, 44u}, // ttr -> Latn
-    {0xCA730000u, 87u}, // tts -> Thai
-    {0xCE730000u, 44u}, // ttt -> Latn
-    {0x9E930000u, 44u}, // tuh -> Latn
-    {0xAE930000u, 44u}, // tul -> Latn
-    {0xB2930000u, 44u}, // tum -> Latn
-    {0xC2930000u, 44u}, // tuq -> Latn
-    {0x8EB30000u, 44u}, // tvd -> Latn
-    {0xAEB30000u, 44u}, // tvl -> Latn
-    {0xD2B30000u, 44u}, // tvu -> Latn
-    {0x9ED30000u, 44u}, // twh -> Latn
-    {0xC2D30000u, 44u}, // twq -> Latn
-    {0x9AF30000u, 82u}, // txg -> Tang
-    {0x74790000u, 44u}, // ty -> Latn
-    {0x83130000u, 44u}, // tya -> Latn
-    {0xD7130000u, 16u}, // tyv -> Cyrl
-    {0xB3330000u, 44u}, // tzm -> Latn
-    {0xD0340000u, 44u}, // ubu -> Latn
-    {0xB0740000u, 16u}, // udm -> Cyrl
+    {0x74730000u, 46u}, // ts -> Latn
+    {0x8E530000u, 25u}, // tsd -> Grek
+    {0x96530000u, 18u}, // tsf -> Deva
+    {0x9A530000u, 46u}, // tsg -> Latn
+    {0xA6530000u, 90u}, // tsj -> Tibt
+    {0xDA530000u, 46u}, // tsw -> Latn
+    {0x74740000u, 17u}, // tt -> Cyrl
+    {0x8E730000u, 46u}, // ttd -> Latn
+    {0x92730000u, 46u}, // tte -> Latn
+    {0xA6730000u, 46u}, // ttj -> Latn
+    {0xC6730000u, 46u}, // ttr -> Latn
+    {0xCA730000u, 89u}, // tts -> Thai
+    {0xCE730000u, 46u}, // ttt -> Latn
+    {0x9E930000u, 46u}, // tuh -> Latn
+    {0xAE930000u, 46u}, // tul -> Latn
+    {0xB2930000u, 46u}, // tum -> Latn
+    {0xC2930000u, 46u}, // tuq -> Latn
+    {0x8EB30000u, 46u}, // tvd -> Latn
+    {0xAEB30000u, 46u}, // tvl -> Latn
+    {0xD2B30000u, 46u}, // tvu -> Latn
+    {0x9ED30000u, 46u}, // twh -> Latn
+    {0xC2D30000u, 46u}, // twq -> Latn
+    {0x9AF30000u, 84u}, // txg -> Tang
+    {0x74790000u, 46u}, // ty -> Latn
+    {0x83130000u, 46u}, // tya -> Latn
+    {0xD7130000u, 17u}, // tyv -> Cyrl
+    {0xB3330000u, 46u}, // tzm -> Latn
+    {0xD0340000u, 46u}, // ubu -> Latn
+    {0xB0740000u, 17u}, // udm -> Cyrl
     {0x75670000u,  1u}, // ug -> Arab
-    {0x75674B5Au, 16u}, // ug-KZ -> Cyrl
-    {0x75674D4Eu, 16u}, // ug-MN -> Cyrl
-    {0x80D40000u, 89u}, // uga -> Ugar
-    {0x756B0000u, 16u}, // uk -> Cyrl
-    {0xA1740000u, 44u}, // uli -> Latn
-    {0x85940000u, 44u}, // umb -> Latn
+    {0x75674B5Au, 17u}, // ug-KZ -> Cyrl
+    {0x75674D4Eu, 17u}, // ug-MN -> Cyrl
+    {0x80D40000u, 91u}, // uga -> Ugar
+    {0x756B0000u, 17u}, // uk -> Cyrl
+    {0xA1740000u, 46u}, // uli -> Latn
+    {0x85940000u, 46u}, // umb -> Latn
     {0xC5B40000u,  7u}, // unr -> Beng
-    {0xC5B44E50u, 17u}, // unr-NP -> Deva
+    {0xC5B44E50u, 18u}, // unr-NP -> Deva
     {0xDDB40000u,  7u}, // unx -> Beng
-    {0xA9D40000u, 44u}, // uok -> Latn
+    {0xA9D40000u, 46u}, // uok -> Latn
     {0x75720000u,  1u}, // ur -> Arab
-    {0xA2340000u, 44u}, // uri -> Latn
-    {0xCE340000u, 44u}, // urt -> Latn
-    {0xDA340000u, 44u}, // urw -> Latn
-    {0x82540000u, 44u}, // usa -> Latn
-    {0xC6740000u, 44u}, // utr -> Latn
-    {0x9EB40000u, 44u}, // uvh -> Latn
-    {0xAEB40000u, 44u}, // uvl -> Latn
-    {0x757A0000u, 44u}, // uz -> Latn
+    {0xA2340000u, 46u}, // uri -> Latn
+    {0xCE340000u, 46u}, // urt -> Latn
+    {0xDA340000u, 46u}, // urw -> Latn
+    {0x82540000u, 46u}, // usa -> Latn
+    {0xC6740000u, 46u}, // utr -> Latn
+    {0x9EB40000u, 46u}, // uvh -> Latn
+    {0xAEB40000u, 46u}, // uvl -> Latn
+    {0x757A0000u, 46u}, // uz -> Latn
     {0x757A4146u,  1u}, // uz-AF -> Arab
-    {0x757A434Eu, 16u}, // uz-CN -> Cyrl
-    {0x98150000u, 44u}, // vag -> Latn
-    {0xA0150000u, 90u}, // vai -> Vaii
-    {0xB4150000u, 44u}, // van -> Latn
-    {0x76650000u, 44u}, // ve -> Latn
-    {0x88950000u, 44u}, // vec -> Latn
-    {0xBC950000u, 44u}, // vep -> Latn
-    {0x76690000u, 44u}, // vi -> Latn
-    {0x89150000u, 44u}, // vic -> Latn
-    {0xD5150000u, 44u}, // viv -> Latn
-    {0xC9750000u, 44u}, // vls -> Latn
-    {0x95950000u, 44u}, // vmf -> Latn
-    {0xD9950000u, 44u}, // vmw -> Latn
-    {0x766F0000u, 44u}, // vo -> Latn
-    {0xCDD50000u, 44u}, // vot -> Latn
-    {0xBA350000u, 44u}, // vro -> Latn
-    {0xB6950000u, 44u}, // vun -> Latn
-    {0xCE950000u, 44u}, // vut -> Latn
-    {0x77610000u, 44u}, // wa -> Latn
-    {0x90160000u, 44u}, // wae -> Latn
-    {0xA4160000u, 44u}, // waj -> Latn
-    {0xAC160000u, 19u}, // wal -> Ethi
-    {0xB4160000u, 44u}, // wan -> Latn
-    {0xC4160000u, 44u}, // war -> Latn
-    {0xBC360000u, 44u}, // wbp -> Latn
-    {0xC0360000u, 84u}, // wbq -> Telu
-    {0xC4360000u, 17u}, // wbr -> Deva
-    {0xA0560000u, 44u}, // wci -> Latn
-    {0xC4960000u, 44u}, // wer -> Latn
-    {0xA0D60000u, 44u}, // wgi -> Latn
-    {0x98F60000u, 44u}, // whg -> Latn
-    {0x85160000u, 44u}, // wib -> Latn
-    {0xD1160000u, 44u}, // wiu -> Latn
-    {0xD5160000u, 44u}, // wiv -> Latn
-    {0x81360000u, 44u}, // wja -> Latn
-    {0xA1360000u, 44u}, // wji -> Latn
-    {0xC9760000u, 44u}, // wls -> Latn
-    {0xB9960000u, 44u}, // wmo -> Latn
-    {0x89B60000u, 44u}, // wnc -> Latn
+    {0x757A434Eu, 17u}, // uz-CN -> Cyrl
+    {0x98150000u, 46u}, // vag -> Latn
+    {0xA0150000u, 92u}, // vai -> Vaii
+    {0xB4150000u, 46u}, // van -> Latn
+    {0x76650000u, 46u}, // ve -> Latn
+    {0x88950000u, 46u}, // vec -> Latn
+    {0xBC950000u, 46u}, // vep -> Latn
+    {0x76690000u, 46u}, // vi -> Latn
+    {0x89150000u, 46u}, // vic -> Latn
+    {0xD5150000u, 46u}, // viv -> Latn
+    {0xC9750000u, 46u}, // vls -> Latn
+    {0x95950000u, 46u}, // vmf -> Latn
+    {0xD9950000u, 46u}, // vmw -> Latn
+    {0x766F0000u, 46u}, // vo -> Latn
+    {0xCDD50000u, 46u}, // vot -> Latn
+    {0xBA350000u, 46u}, // vro -> Latn
+    {0xB6950000u, 46u}, // vun -> Latn
+    {0xCE950000u, 46u}, // vut -> Latn
+    {0x77610000u, 46u}, // wa -> Latn
+    {0x90160000u, 46u}, // wae -> Latn
+    {0xA4160000u, 46u}, // waj -> Latn
+    {0xAC160000u, 20u}, // wal -> Ethi
+    {0xB4160000u, 46u}, // wan -> Latn
+    {0xC4160000u, 46u}, // war -> Latn
+    {0xBC360000u, 46u}, // wbp -> Latn
+    {0xC0360000u, 86u}, // wbq -> Telu
+    {0xC4360000u, 18u}, // wbr -> Deva
+    {0xA0560000u, 46u}, // wci -> Latn
+    {0xC4960000u, 46u}, // wer -> Latn
+    {0xA0D60000u, 46u}, // wgi -> Latn
+    {0x98F60000u, 46u}, // whg -> Latn
+    {0x85160000u, 46u}, // wib -> Latn
+    {0xD1160000u, 46u}, // wiu -> Latn
+    {0xD5160000u, 46u}, // wiv -> Latn
+    {0x81360000u, 46u}, // wja -> Latn
+    {0xA1360000u, 46u}, // wji -> Latn
+    {0xC9760000u, 46u}, // wls -> Latn
+    {0xB9960000u, 46u}, // wmo -> Latn
+    {0x89B60000u, 46u}, // wnc -> Latn
     {0xA1B60000u,  1u}, // wni -> Arab
-    {0xD1B60000u, 44u}, // wnu -> Latn
-    {0x776F0000u, 44u}, // wo -> Latn
-    {0x85D60000u, 44u}, // wob -> Latn
-    {0xC9D60000u, 44u}, // wos -> Latn
-    {0xCA360000u, 44u}, // wrs -> Latn
-    {0x9A560000u, 21u}, // wsg -> Gong
-    {0xAA560000u, 44u}, // wsk -> Latn
-    {0xB2760000u, 17u}, // wtm -> Deva
-    {0xD2960000u, 27u}, // wuu -> Hans
-    {0xD6960000u, 44u}, // wuv -> Latn
-    {0x82D60000u, 44u}, // wwa -> Latn
-    {0xD4170000u, 44u}, // xav -> Latn
-    {0xA0370000u, 44u}, // xbi -> Latn
+    {0xD1B60000u, 46u}, // wnu -> Latn
+    {0x776F0000u, 46u}, // wo -> Latn
+    {0x85D60000u, 46u}, // wob -> Latn
+    {0xC9D60000u, 46u}, // wos -> Latn
+    {0xCA360000u, 46u}, // wrs -> Latn
+    {0x9A560000u, 22u}, // wsg -> Gong
+    {0xAA560000u, 46u}, // wsk -> Latn
+    {0xB2760000u, 18u}, // wtm -> Deva
+    {0xD2960000u, 28u}, // wuu -> Hans
+    {0xD6960000u, 46u}, // wuv -> Latn
+    {0x82D60000u, 46u}, // wwa -> Latn
+    {0xD4170000u, 46u}, // xav -> Latn
+    {0xA0370000u, 46u}, // xbi -> Latn
+    {0xB8570000u, 14u}, // xco -> Chrs
     {0xC4570000u, 11u}, // xcr -> Cari
-    {0xC8970000u, 44u}, // xes -> Latn
-    {0x78680000u, 44u}, // xh -> Latn
-    {0x81770000u, 44u}, // xla -> Latn
-    {0x89770000u, 48u}, // xlc -> Lyci
-    {0x8D770000u, 49u}, // xld -> Lydi
-    {0x95970000u, 20u}, // xmf -> Geor
-    {0xB5970000u, 51u}, // xmn -> Mani
-    {0xC5970000u, 52u}, // xmr -> Merc
-    {0x81B70000u, 57u}, // xna -> Narb
-    {0xC5B70000u, 17u}, // xnr -> Deva
-    {0x99D70000u, 44u}, // xog -> Latn
-    {0xB5D70000u, 44u}, // xon -> Latn
-    {0xC5F70000u, 68u}, // xpr -> Prti
-    {0x86370000u, 44u}, // xrb -> Latn
-    {0x82570000u, 71u}, // xsa -> Sarb
-    {0xA2570000u, 44u}, // xsi -> Latn
-    {0xB2570000u, 44u}, // xsm -> Latn
-    {0xC6570000u, 17u}, // xsr -> Deva
-    {0x92D70000u, 44u}, // xwe -> Latn
-    {0xB0180000u, 44u}, // yam -> Latn
-    {0xB8180000u, 44u}, // yao -> Latn
-    {0xBC180000u, 44u}, // yap -> Latn
-    {0xC8180000u, 44u}, // yas -> Latn
-    {0xCC180000u, 44u}, // yat -> Latn
-    {0xD4180000u, 44u}, // yav -> Latn
-    {0xE0180000u, 44u}, // yay -> Latn
-    {0xE4180000u, 44u}, // yaz -> Latn
-    {0x80380000u, 44u}, // yba -> Latn
-    {0x84380000u, 44u}, // ybb -> Latn
-    {0xE0380000u, 44u}, // yby -> Latn
-    {0xC4980000u, 44u}, // yer -> Latn
-    {0xC4D80000u, 44u}, // ygr -> Latn
-    {0xD8D80000u, 44u}, // ygw -> Latn
-    {0x79690000u, 30u}, // yi -> Hebr
-    {0xB9580000u, 44u}, // yko -> Latn
-    {0x91780000u, 44u}, // yle -> Latn
-    {0x99780000u, 44u}, // ylg -> Latn
-    {0xAD780000u, 44u}, // yll -> Latn
-    {0xAD980000u, 44u}, // yml -> Latn
-    {0x796F0000u, 44u}, // yo -> Latn
-    {0xB5D80000u, 44u}, // yon -> Latn
-    {0x86380000u, 44u}, // yrb -> Latn
-    {0x92380000u, 44u}, // yre -> Latn
-    {0xAE380000u, 44u}, // yrl -> Latn
-    {0xCA580000u, 44u}, // yss -> Latn
-    {0x82980000u, 44u}, // yua -> Latn
-    {0x92980000u, 28u}, // yue -> Hant
-    {0x9298434Eu, 27u}, // yue-CN -> Hans
-    {0xA6980000u, 44u}, // yuj -> Latn
-    {0xCE980000u, 44u}, // yut -> Latn
-    {0xDA980000u, 44u}, // yuw -> Latn
-    {0x7A610000u, 44u}, // za -> Latn
-    {0x98190000u, 44u}, // zag -> Latn
+    {0xC8970000u, 46u}, // xes -> Latn
+    {0x78680000u, 46u}, // xh -> Latn
+    {0x81770000u, 46u}, // xla -> Latn
+    {0x89770000u, 50u}, // xlc -> Lyci
+    {0x8D770000u, 51u}, // xld -> Lydi
+    {0x95970000u, 21u}, // xmf -> Geor
+    {0xB5970000u, 53u}, // xmn -> Mani
+    {0xC5970000u, 54u}, // xmr -> Merc
+    {0x81B70000u, 59u}, // xna -> Narb
+    {0xC5B70000u, 18u}, // xnr -> Deva
+    {0x99D70000u, 46u}, // xog -> Latn
+    {0xB5D70000u, 46u}, // xon -> Latn
+    {0xC5F70000u, 70u}, // xpr -> Prti
+    {0x86370000u, 46u}, // xrb -> Latn
+    {0x82570000u, 73u}, // xsa -> Sarb
+    {0xA2570000u, 46u}, // xsi -> Latn
+    {0xB2570000u, 46u}, // xsm -> Latn
+    {0xC6570000u, 18u}, // xsr -> Deva
+    {0x92D70000u, 46u}, // xwe -> Latn
+    {0xB0180000u, 46u}, // yam -> Latn
+    {0xB8180000u, 46u}, // yao -> Latn
+    {0xBC180000u, 46u}, // yap -> Latn
+    {0xC8180000u, 46u}, // yas -> Latn
+    {0xCC180000u, 46u}, // yat -> Latn
+    {0xD4180000u, 46u}, // yav -> Latn
+    {0xE0180000u, 46u}, // yay -> Latn
+    {0xE4180000u, 46u}, // yaz -> Latn
+    {0x80380000u, 46u}, // yba -> Latn
+    {0x84380000u, 46u}, // ybb -> Latn
+    {0xE0380000u, 46u}, // yby -> Latn
+    {0xC4980000u, 46u}, // yer -> Latn
+    {0xC4D80000u, 46u}, // ygr -> Latn
+    {0xD8D80000u, 46u}, // ygw -> Latn
+    {0x79690000u, 31u}, // yi -> Hebr
+    {0xB9580000u, 46u}, // yko -> Latn
+    {0x91780000u, 46u}, // yle -> Latn
+    {0x99780000u, 46u}, // ylg -> Latn
+    {0xAD780000u, 46u}, // yll -> Latn
+    {0xAD980000u, 46u}, // yml -> Latn
+    {0x796F0000u, 46u}, // yo -> Latn
+    {0xB5D80000u, 46u}, // yon -> Latn
+    {0x86380000u, 46u}, // yrb -> Latn
+    {0x92380000u, 46u}, // yre -> Latn
+    {0xAE380000u, 46u}, // yrl -> Latn
+    {0xCA580000u, 46u}, // yss -> Latn
+    {0x82980000u, 46u}, // yua -> Latn
+    {0x92980000u, 29u}, // yue -> Hant
+    {0x9298434Eu, 28u}, // yue-CN -> Hans
+    {0xA6980000u, 46u}, // yuj -> Latn
+    {0xCE980000u, 46u}, // yut -> Latn
+    {0xDA980000u, 46u}, // yuw -> Latn
+    {0x7A610000u, 46u}, // za -> Latn
+    {0x98190000u, 46u}, // zag -> Latn
     {0xA4790000u,  1u}, // zdj -> Arab
-    {0x80990000u, 44u}, // zea -> Latn
-    {0x9CD90000u, 85u}, // zgh -> Tfng
-    {0x7A680000u, 27u}, // zh -> Hans
-    {0x7A684155u, 28u}, // zh-AU -> Hant
-    {0x7A68424Eu, 28u}, // zh-BN -> Hant
-    {0x7A684742u, 28u}, // zh-GB -> Hant
-    {0x7A684746u, 28u}, // zh-GF -> Hant
-    {0x7A68484Bu, 28u}, // zh-HK -> Hant
-    {0x7A684944u, 28u}, // zh-ID -> Hant
-    {0x7A684D4Fu, 28u}, // zh-MO -> Hant
-    {0x7A684D59u, 28u}, // zh-MY -> Hant
-    {0x7A685041u, 28u}, // zh-PA -> Hant
-    {0x7A685046u, 28u}, // zh-PF -> Hant
-    {0x7A685048u, 28u}, // zh-PH -> Hant
-    {0x7A685352u, 28u}, // zh-SR -> Hant
-    {0x7A685448u, 28u}, // zh-TH -> Hant
-    {0x7A685457u, 28u}, // zh-TW -> Hant
-    {0x7A685553u, 28u}, // zh-US -> Hant
-    {0x7A68564Eu, 28u}, // zh-VN -> Hant
-    {0xDCF90000u, 59u}, // zhx -> Nshu
-    {0x81190000u, 44u}, // zia -> Latn
-    {0xB1790000u, 44u}, // zlm -> Latn
-    {0xA1990000u, 44u}, // zmi -> Latn
-    {0x91B90000u, 44u}, // zne -> Latn
-    {0x7A750000u, 44u}, // zu -> Latn
-    {0x83390000u, 44u}, // zza -> Latn
+    {0x80990000u, 46u}, // zea -> Latn
+    {0x9CD90000u, 87u}, // zgh -> Tfng
+    {0x7A680000u, 28u}, // zh -> Hans
+    {0x7A684155u, 29u}, // zh-AU -> Hant
+    {0x7A68424Eu, 29u}, // zh-BN -> Hant
+    {0x7A684742u, 29u}, // zh-GB -> Hant
+    {0x7A684746u, 29u}, // zh-GF -> Hant
+    {0x7A68484Bu, 29u}, // zh-HK -> Hant
+    {0x7A684944u, 29u}, // zh-ID -> Hant
+    {0x7A684D4Fu, 29u}, // zh-MO -> Hant
+    {0x7A684D59u, 29u}, // zh-MY -> Hant
+    {0x7A685041u, 29u}, // zh-PA -> Hant
+    {0x7A685046u, 29u}, // zh-PF -> Hant
+    {0x7A685048u, 29u}, // zh-PH -> Hant
+    {0x7A685352u, 29u}, // zh-SR -> Hant
+    {0x7A685448u, 29u}, // zh-TH -> Hant
+    {0x7A685457u, 29u}, // zh-TW -> Hant
+    {0x7A685553u, 29u}, // zh-US -> Hant
+    {0x7A68564Eu, 29u}, // zh-VN -> Hant
+    {0xDCF90000u, 61u}, // zhx -> Nshu
+    {0x81190000u, 46u}, // zia -> Latn
+    {0xCD590000u, 41u}, // zkt -> Kits
+    {0xB1790000u, 46u}, // zlm -> Latn
+    {0xA1990000u, 46u}, // zmi -> Latn
+    {0x91B90000u, 46u}, // zne -> Latn
+    {0x7A750000u, 46u}, // zu -> Latn
+    {0x83390000u, 46u}, // zza -> Latn
 });
 
 std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -1829,6 +1833,7 @@
     0xC66A4D594C61746ELLU, // ktr_Latn_MY
     0x6B75495141726162LLU, // ku_Arab_IQ
     0x6B7554524C61746ELLU, // ku_Latn_TR
+    0x6B75474559657A69LLU, // ku_Yezi_GE
     0xB28A52554379726CLLU, // kum_Cyrl_RU
     0x6B7652554379726CLLU, // kv_Cyrl_RU
     0xC6AA49444C61746ELLU, // kvr_Latn_ID
@@ -2199,6 +2204,7 @@
     0xB276494E44657661LLU, // wtm_Deva_IN
     0xD296434E48616E73LLU, // wuu_Hans_CN
     0xD41742524C61746ELLU, // xav_Latn_BR
+    0xB857555A43687273LLU, // xco_Chrs_UZ
     0xC457545243617269LLU, // xcr_Cari_TR
     0x78685A414C61746ELLU, // xh_Latn_ZA
     0x897754524C796369LLU, // xlc_Lyci_TR
@@ -2231,6 +2237,7 @@
     0x7A68434E48616E73LLU, // zh_Hans_CN
     0x7A68545748616E74LLU, // zh_Hant_TW
     0xDCF9434E4E736875LLU, // zhx_Nshu_CN
+    0xCD59434E4B697473LLU, // zkt_Kits_CN
     0xB17954474C61746ELLU, // zlm_Latn_TG
     0xA1994D594C61746ELLU, // zmi_Latn_MY
     0x7A755A414C61746ELLU, // zu_Latn_ZA
diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS
index 8cffd6a..bc056df 100644
--- a/libs/androidfw/OWNERS
+++ b/libs/androidfw/OWNERS
@@ -3,4 +3,4 @@
 rtmitchell@google.com
 
 per-file CursorWindow.cpp=omakoto@google.com
-per-file LocaleDataTables.cpp=vichang@google.com,tobiast@google.com,nikitai@google.com
+per-file LocaleDataTables.cpp=vichang@google.com,ngeoffray@google.com,nikitai@google.com
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 5be2105..568e3b6 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -40,7 +40,7 @@
     explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
     }
 
-    bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
         // Data is usually requested sequentially, so this helps avoid pointless
         // fseeks every time we perform a read. There's an impedence mismatch
         // here because the original API was designed around pread and pwrite.
@@ -63,7 +63,7 @@
 
   private:
     FILE* mFp;
-    mutable uint32_t mCurrentOffset;
+    mutable off64_t mCurrentOffset;
 };
 
 class FdReader : public zip_archive::Reader {
@@ -71,8 +71,8 @@
     explicit FdReader(int fd) : mFd(fd) {
     }
 
-    bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
-      return android::base::ReadFullyAtOffset(mFd, buf, len, static_cast<off_t>(offset));
+    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+      return android::base::ReadFullyAtOffset(mFd, buf, len, offset);
     }
 
   private:
@@ -86,8 +86,8 @@
         mInputSize(inputSize) {
     }
 
-    bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
-        if (offset + len > mInputSize) {
+    bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+        if (mInputSize < len || offset > mInputSize - len) {
             return false;
         }
 
diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h
index 6fa089a..acf413a 100644
--- a/libs/androidfw/include/androidfw/ConfigDescription.h
+++ b/libs/androidfw/include/androidfw/ConfigDescription.h
@@ -151,8 +151,8 @@
   size = sizeof(android::ResTable_config);
 }
 
-inline ConfigDescription::ConfigDescription(const ConfigDescription& o) {
-  *static_cast<android::ResTable_config*>(this) = o;
+inline ConfigDescription::ConfigDescription(const ConfigDescription& o)
+  : android::ResTable_config(o) {
 }
 
 inline ConfigDescription::ConfigDescription(ConfigDescription&& o) noexcept {
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index 808921d..61d06c2 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -16,8 +16,6 @@
 
 #include "PathParser.h"
 
-#include "jni.h"
-
 #include <errno.h>
 #include <stdlib.h>
 #include <utils/Log.h>
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index f5bebce..878bb7c 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -22,7 +22,6 @@
 
 #include <android/log.h>
 #include <cutils/compiler.h>
-#include <jni.h>
 
 #include <string>
 
diff --git a/lowpan/tests/Android.bp b/lowpan/tests/Android.bp
new file mode 100644
index 0000000..ad2bc27
--- /dev/null
+++ b/lowpan/tests/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Make test APK
+// ============================================================
+android_test {
+    name: "FrameworksLowpanApiTests",
+    srcs: ["**/*.java"],
+    // Filter all src files to just java files
+    jacoco: {
+        include_filter: ["android.net.lowpan.*"],
+        exclude_filter: [
+	    "android.net.lowpan.LowpanInterfaceTest*",
+	    "android.net.lowpan.LowpanManagerTest*",
+	],
+    },
+    static_libs: [
+        "androidx.test.rules",
+        "guava",
+        "mockito-target-minus-junit4",
+        "frameworks-base-testutils",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+    certificate: "platform",
+}
diff --git a/lowpan/tests/Android.mk b/lowpan/tests/Android.mk
deleted file mode 100644
index 832ed2f..0000000
--- a/lowpan/tests/Android.mk
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Make test APK
-# ============================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-# This list is generated from the java source files in this module
-# The list is a comma separated list of class names with * matching zero or more characters.
-# Example:
-#   Input files: src/com/android/server/lowpan/Test.java src/com/android/server/lowpan/AnotherTest.java
-#   Generated exclude list: com.android.server.lowpan.Test*,com.android.server.lowpan.AnotherTest*
-
-# Filter all src files to just java files
-local_java_files := $(filter %.java,$(LOCAL_SRC_FILES))
-# Transform java file names into full class names.
-# This only works if the class name matches the file name and the directory structure
-# matches the package.
-local_classes := $(subst /,.,$(patsubst src/%.java,%,$(local_java_files)))
-# Convert class name list to jacoco exclude list
-# This appends a * to all classes and replace the space separators with commas.
-# These patterns will match all classes in this module and their inner classes.
-jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes)))
-
-jacoco_include := android.net.lowpan.*
-
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include)
-LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-	androidx.test.rules \
-	guava \
-	mockito-target-minus-junit4 \
-	frameworks-base-testutils \
-
-LOCAL_JAVA_LIBRARIES := \
-	android.test.runner \
-	android.test.base \
-
-LOCAL_PACKAGE_NAME := FrameworksLowpanApiTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_CERTIFICATE := platform
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-include $(BUILD_PACKAGE)
diff --git a/media/Android.bp b/media/Android.bp
index 4363568..a432c8d 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -26,8 +26,10 @@
 
     installable: true,
 
-    // Make sure that the implementaion only relies on SDK or system APIs.
+    // TODO: build against stable API surface. Use core_platform for now to avoid
+    // link-check failure with exoplayer building against "current".
     sdk_version: "core_platform",
+    min_sdk_version: "29",
     libs: [
         // The order matters. android_system_* library should come later.
         "framework_media_annotation",
@@ -94,4 +96,5 @@
     name: "framework_media_annotation",
     srcs: [":framework-media-annotation-srcs"],
     installable: false,
+    sdk_version: "core_current",
 }
diff --git a/media/OWNERS b/media/OWNERS
index be60583..242ff28 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -14,6 +14,7 @@
 klhyun@google.com
 lajos@google.com
 marcone@google.com
+philburk@google.com
 sungsoo@google.com
 wjia@google.com
 
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index bb87404..bd54f2b 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -507,6 +507,16 @@
 
     /**
      * @hide
+     * Return usage, even the non-public ones.
+     * Internal use only
+     * @return one of the values that can be set in {@link Builder#setUsage(int)}
+     */
+    public int getSystemUsage() {
+        return mUsage;
+    }
+
+    /**
+     * @hide
      * Return the Bundle of data.
      * @return a copy of the Bundle for this instance, may be null.
      */
diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java
index 4e70501..4c0850b 100644
--- a/media/java/android/media/AudioFocusRequest.java
+++ b/media/java/android/media/AudioFocusRequest.java
@@ -80,9 +80,9 @@
  * <p>An {@code AudioFocusRequest} instance always contains one of the four types of requests
  * explained above. It is passed when building an {@code AudioFocusRequest} instance with its
  * builder in the {@link Builder} constructor
- * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(int)}, or
+ * {@link AudioFocusRequest.Builder#Builder(int)}, or
  * with {@link AudioFocusRequest.Builder#setFocusGain(int)} after copying an existing instance with
- * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(AudioFocusRequest)}.
+ * {@link AudioFocusRequest.Builder#Builder(AudioFocusRequest)}.
  *
  * <h3>Qualifying your focus request</h3>
  * <h4>Use case requiring a focus request</h4>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 94d32c6..59943eb 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4213,6 +4213,7 @@
      * {@hide}
      */
     @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public void setWiredDeviceConnectionState(int type, int state, String address, String name) {
         final IAudioService service = getService();
         try {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 1ad6198..457ae4f 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -43,44 +43,54 @@
 /**
  * @hide
  */
+@TestApi
 public class AudioSystem
 {
     private static final boolean DEBUG_VOLUME = false;
 
     private static final String TAG = "AudioSystem";
+
+    // private constructor to prevent instantiating AudioSystem
+    private AudioSystem() {
+        throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
+    }
+
     /* These values must be kept in sync with system/audio.h */
     /*
      * If these are modified, please also update Settings.System.VOLUME_SETTINGS
      * and attrs.xml and AudioManager.java.
      */
-    /** Used to identify the default audio stream volume */
+    /** @hide Used to identify the default audio stream volume */
+    @TestApi
     public static final int STREAM_DEFAULT = -1;
-    /** Used to identify the volume of audio streams for phone calls */
+    /** @hide Used to identify the volume of audio streams for phone calls */
     public static final int STREAM_VOICE_CALL = 0;
-    /** Used to identify the volume of audio streams for system sounds */
+    /** @hide Used to identify the volume of audio streams for system sounds */
     public static final int STREAM_SYSTEM = 1;
-    /** Used to identify the volume of audio streams for the phone ring and message alerts */
+    /** @hide Used to identify the volume of audio streams for the phone ring and message alerts */
     public static final int STREAM_RING = 2;
-    /** Used to identify the volume of audio streams for music playback */
+    /** @hide Used to identify the volume of audio streams for music playback */
     public static final int STREAM_MUSIC = 3;
-    /** Used to identify the volume of audio streams for alarms */
+    /** @hide Used to identify the volume of audio streams for alarms */
     public static final int STREAM_ALARM = 4;
-    /** Used to identify the volume of audio streams for notifications */
+    /** @hide Used to identify the volume of audio streams for notifications */
     public static final int STREAM_NOTIFICATION = 5;
-    /** Used to identify the volume of audio streams for phone calls when connected on bluetooth */
+    /** @hide
+     *  Used to identify the volume of audio streams for phone calls when connected on bluetooth */
     public static final int STREAM_BLUETOOTH_SCO = 6;
-    /** Used to identify the volume of audio streams for enforced system sounds in certain
+    /** @hide Used to identify the volume of audio streams for enforced system sounds in certain
      * countries (e.g camera in Japan) */
     @UnsupportedAppUsage
     public static final int STREAM_SYSTEM_ENFORCED = 7;
-    /** Used to identify the volume of audio streams for DTMF tones */
+    /** @hide Used to identify the volume of audio streams for DTMF tones */
     public static final int STREAM_DTMF = 8;
-    /** Used to identify the volume of audio streams exclusively transmitted through the
+    /** @hide Used to identify the volume of audio streams exclusively transmitted through the
      *  speaker (TTS) of the device */
     public static final int STREAM_TTS = 9;
-    /** Used to identify the volume of audio streams for accessibility prompts */
+    /** @hide Used to identify the volume of audio streams for accessibility prompts */
     public static final int STREAM_ACCESSIBILITY = 10;
     /**
+     * @hide
      * @deprecated Use {@link #numStreamTypes() instead}
      */
     public static final int NUM_STREAMS = 5;
@@ -93,9 +103,16 @@
 
     // Expose only the getter method publicly so we can change it in the future
     private static final int NUM_STREAM_TYPES = 11;
+
+    /**
+     * @hide
+     * @return total number of stream types
+     */
     @UnsupportedAppUsage
+    @TestApi
     public static final int getNumStreamTypes() { return NUM_STREAM_TYPES; }
 
+    /** @hide */
     public static final String[] STREAM_NAMES = new String[] {
         "STREAM_VOICE_CALL",
         "STREAM_SYSTEM",
@@ -110,7 +127,8 @@
         "STREAM_ACCESSIBILITY"
     };
 
-    /*
+    /**
+     * @hide
      * Sets the microphone mute on or off.
      *
      * @param on set <var>true</var> to mute the microphone;
@@ -120,7 +138,8 @@
     @UnsupportedAppUsage
     public static native int muteMicrophone(boolean on);
 
-    /*
+    /**
+     * @hide
      * Checks whether the microphone mute is on or off.
      *
      * @return true if microphone is muted, false if it's not
@@ -129,14 +148,22 @@
     public static native boolean isMicrophoneMuted();
 
     /* modes for setPhoneState, must match AudioSystem.h audio_mode */
+    /** @hide */
     public static final int MODE_INVALID            = -2;
+    /** @hide */
     public static final int MODE_CURRENT            = -1;
+    /** @hide */
     public static final int MODE_NORMAL             = 0;
+    /** @hide */
     public static final int MODE_RINGTONE           = 1;
+    /** @hide */
     public static final int MODE_IN_CALL            = 2;
+    /** @hide */
     public static final int MODE_IN_COMMUNICATION   = 3;
+    /** @hide */
     public static final int NUM_MODES               = 4;
 
+    /** @hide */
     public static String modeToString(int mode) {
         switch (mode) {
             case MODE_CURRENT: return "MODE_CURRENT";
@@ -150,15 +177,23 @@
     }
 
     /* Formats for A2DP codecs, must match system/audio-base.h audio_format_t */
+    /** @hide */
     public static final int AUDIO_FORMAT_INVALID        = 0xFFFFFFFF;
+    /** @hide */
     public static final int AUDIO_FORMAT_DEFAULT        = 0;
+    /** @hide */
     public static final int AUDIO_FORMAT_AAC            = 0x04000000;
+    /** @hide */
     public static final int AUDIO_FORMAT_SBC            = 0x1F000000;
+    /** @hide */
     public static final int AUDIO_FORMAT_APTX           = 0x20000000;
+    /** @hide */
     public static final int AUDIO_FORMAT_APTX_HD        = 0x21000000;
+    /** @hide */
     public static final int AUDIO_FORMAT_LDAC           = 0x23000000;
 
     /**
+     * @hide
      * Convert audio format enum values to Bluetooth codec values
      */
     public static int audioFormatToBluetoothSourceCodec(int audioFormat) {
@@ -173,25 +208,27 @@
     }
 
     /* Routing bits for the former setRouting/getRouting API */
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_EARPIECE          = (1 << 0);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_SPEAKER           = (1 << 1);
-    /** @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */
+    /** @hide @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */
     @Deprecated public static final int ROUTE_BLUETOOTH = (1 << 2);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_BLUETOOTH_SCO     = (1 << 2);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_HEADSET           = (1 << 3);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_BLUETOOTH_A2DP    = (1 << 4);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_ALL               = 0xFFFFFFFF;
 
     // Keep in sync with system/media/audio/include/system/audio.h
+    /**  @hide */
     public static final int AUDIO_SESSION_ALLOCATE = 0;
 
-    /*
+    /**
+     * @hide
      * Checks whether the specified stream type is active.
      *
      * return true if any track playing on this stream is active.
@@ -199,7 +236,8 @@
     @UnsupportedAppUsage
     public static native boolean isStreamActive(int stream, int inPastMs);
 
-    /*
+    /**
+     * @hide
      * Checks whether the specified stream type is active on a remotely connected device. The notion
      * of what constitutes a remote device is enforced by the audio policy manager of the platform.
      *
@@ -207,7 +245,8 @@
      */
     public static native boolean isStreamActiveRemotely(int stream, int inPastMs);
 
-    /*
+    /**
+     * @hide
      * Checks whether the specified audio source is active.
      *
      * return true if any recorder using this source is currently recording
@@ -215,23 +254,27 @@
     @UnsupportedAppUsage
     public static native boolean isSourceActive(int source);
 
-    /*
+    /**
+     * @hide
      * Returns a new unused audio session ID
      */
     public static native int newAudioSessionId();
 
-    /*
+    /**
+     * @hide
      * Returns a new unused audio player ID
      */
     public static native int newAudioPlayerId();
 
     /**
+     * @hide
      * Returns a new unused audio recorder ID
      */
     public static native int newAudioRecorderId();
 
 
-    /*
+    /**
+     * @hide
      * Sets a group generic audio configuration parameters. The use of these parameters
      * are platform dependent, see libaudio
      *
@@ -241,7 +284,8 @@
     @UnsupportedAppUsage
     public static native int setParameters(String keyValuePairs);
 
-    /*
+    /**
+     * @hide
      * Gets a group generic audio configuration parameters. The use of these parameters
      * are platform dependent, see libaudio
      *
@@ -253,16 +297,16 @@
     public static native String getParameters(String keys);
 
     // These match the enum AudioError in frameworks/base/core/jni/android_media_AudioSystem.cpp
-    /* Command sucessful or Media server restarted. see ErrorCallback */
+    /** @hide Command successful or Media server restarted. see ErrorCallback */
     public static final int AUDIO_STATUS_OK = 0;
-    /* Command failed or unspecified audio error.  see ErrorCallback */
+    /** @hide Command failed or unspecified audio error.  see ErrorCallback */
     public static final int AUDIO_STATUS_ERROR = 1;
-    /* Media server died. see ErrorCallback */
+    /** @hide Media server died. see ErrorCallback */
     public static final int AUDIO_STATUS_SERVER_DIED = 100;
 
-    private static ErrorCallback mErrorCallback;
+    private static ErrorCallback sErrorCallback;
 
-    /*
+    /** @hide
      * Handles the audio error callback.
      */
     public interface ErrorCallback
@@ -277,7 +321,8 @@
         void onError(int error);
     };
 
-    /*
+    /**
+     * @hide
      * Registers a callback to be invoked when an error occurs.
      * @param cb the callback to run
      */
@@ -285,7 +330,7 @@
     public static void setErrorCallback(ErrorCallback cb)
     {
         synchronized (AudioSystem.class) {
-            mErrorCallback = cb;
+            sErrorCallback = cb;
             if (cb != null) {
                 cb.onError(checkAudioFlinger());
             }
@@ -297,8 +342,8 @@
     {
         ErrorCallback errorCallback = null;
         synchronized (AudioSystem.class) {
-            if (mErrorCallback != null) {
-                errorCallback = mErrorCallback;
+            if (sErrorCallback != null) {
+                errorCallback = sErrorCallback;
             }
         }
         if (errorCallback != null) {
@@ -307,6 +352,7 @@
     }
 
     /**
+     * @hide
      * Handles events from the audio policy manager about dynamic audio policies
      * @see android.media.audiopolicy.AudioPolicy
      */
@@ -320,6 +366,7 @@
 
     private static DynamicPolicyCallback sDynPolicyCallback;
 
+    /** @hide */
     public static void setDynamicPolicyCallback(DynamicPolicyCallback cb)
     {
         synchronized (AudioSystem.class) {
@@ -349,6 +396,7 @@
     }
 
     /**
+     * @hide
      * Handles events from the audio policy manager about recording events
      * @see android.media.AudioManager.AudioRecordingCallback
      */
@@ -380,6 +428,7 @@
 
     private static AudioRecordingCallback sRecordingCallback;
 
+    /** @hide */
     public static void setRecordingCallback(AudioRecordingCallback cb) {
         synchronized (AudioSystem.class) {
             sRecordingCallback = cb;
@@ -428,13 +477,21 @@
      * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...)
      * Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h
      */
+    /** @hide */
     public static final int SUCCESS            = 0;
+    /** @hide */
     public static final int ERROR              = -1;
+    /** @hide */
     public static final int BAD_VALUE          = -2;
+    /** @hide */
     public static final int INVALID_OPERATION  = -3;
+    /** @hide */
     public static final int PERMISSION_DENIED  = -4;
+    /** @hide */
     public static final int NO_INIT            = -5;
+    /** @hide */
     public static final int DEAD_OBJECT        = -6;
+    /** @hide */
     public static final int WOULD_BLOCK        = -7;
 
     /** @hide */
@@ -452,6 +509,7 @@
     public @interface AudioSystemError {}
 
     /**
+     * @hide
      * Convert an int error value to its String value for readability.
      * Accepted error values are the java AudioSystem errors, matching android_media_AudioErrors.h,
      * which map onto the native status_t type.
@@ -488,74 +546,113 @@
     //
     // audio device definitions: must be kept in sync with values in system/core/audio.h
     //
-
+    /** @hide */
     public static final int DEVICE_NONE = 0x0;
     // reserved bits
+    /** @hide */
     public static final int DEVICE_BIT_IN = 0x80000000;
+    /** @hide */
     public static final int DEVICE_BIT_DEFAULT = 0x40000000;
     // output devices, be sure to update AudioManager.java also
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_EARPIECE = 0x1;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_SPEAKER = 0x2;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_WIRED_HEADSET = 0x4;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
+    /** @hide */
     public static final int DEVICE_OUT_HDMI = DEVICE_OUT_AUX_DIGITAL;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_USB_ACCESSORY = 0x2000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_USB_DEVICE = 0x4000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_REMOTE_SUBMIX = 0x8000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_TELEPHONY_TX = 0x10000;
+    /** @hide */
     public static final int DEVICE_OUT_LINE = 0x20000;
+    /** @hide */
     public static final int DEVICE_OUT_HDMI_ARC = 0x40000;
+    /** @hide */
     public static final int DEVICE_OUT_SPDIF = 0x80000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_FM = 0x100000;
+    /** @hide */
     public static final int DEVICE_OUT_AUX_LINE = 0x200000;
+    /** @hide */
     public static final int DEVICE_OUT_SPEAKER_SAFE = 0x400000;
+    /** @hide */
     public static final int DEVICE_OUT_IP = 0x800000;
+    /** @hide */
     public static final int DEVICE_OUT_BUS = 0x1000000;
+    /** @hide */
     public static final int DEVICE_OUT_PROXY = 0x2000000;
+    /** @hide */
     public static final int DEVICE_OUT_USB_HEADSET = 0x4000000;
+    /** @hide */
     public static final int DEVICE_OUT_HEARING_AID = 0x8000000;
 
+    /** @hide */
     public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
 
     // Deprecated in R because multiple device types are no longer accessed as a bit mask.
     // Removing this will get lint warning about changing hidden apis.
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_ALL_USB = (DEVICE_OUT_USB_ACCESSORY |
                                                   DEVICE_OUT_USB_DEVICE |
                                                   DEVICE_OUT_USB_HEADSET);
 
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_A2DP_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_SCO_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_USB_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_HDMI_SYSTEM_AUDIO_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER_SET;
     static {
         DEVICE_OUT_ALL_SET = new HashSet<>();
@@ -615,53 +712,85 @@
     }
 
     // input devices
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_COMMUNICATION = DEVICE_BIT_IN | 0x1;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_AMBIENT = DEVICE_BIT_IN | 0x2;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_BUILTIN_MIC = DEVICE_BIT_IN | 0x4;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = DEVICE_BIT_IN | 0x8;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_WIRED_HEADSET = DEVICE_BIT_IN | 0x10;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_AUX_DIGITAL = DEVICE_BIT_IN | 0x20;
+    /** @hide */
     public static final int DEVICE_IN_HDMI = DEVICE_IN_AUX_DIGITAL;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_VOICE_CALL = DEVICE_BIT_IN | 0x40;
+    /** @hide */
     public static final int DEVICE_IN_TELEPHONY_RX = DEVICE_IN_VOICE_CALL;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_BACK_MIC = DEVICE_BIT_IN | 0x80;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_REMOTE_SUBMIX = DEVICE_BIT_IN | 0x100;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_ANLG_DOCK_HEADSET = DEVICE_BIT_IN | 0x200;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_DGTL_DOCK_HEADSET = DEVICE_BIT_IN | 0x400;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_USB_ACCESSORY = DEVICE_BIT_IN | 0x800;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_USB_DEVICE = DEVICE_BIT_IN | 0x1000;
+    /** @hide */
     public static final int DEVICE_IN_FM_TUNER = DEVICE_BIT_IN | 0x2000;
+    /** @hide */
     public static final int DEVICE_IN_TV_TUNER = DEVICE_BIT_IN | 0x4000;
+    /** @hide */
     public static final int DEVICE_IN_LINE = DEVICE_BIT_IN | 0x8000;
+    /** @hide */
     public static final int DEVICE_IN_SPDIF = DEVICE_BIT_IN | 0x10000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_BLUETOOTH_A2DP = DEVICE_BIT_IN | 0x20000;
+    /** @hide */
     public static final int DEVICE_IN_LOOPBACK = DEVICE_BIT_IN | 0x40000;
+    /** @hide */
     public static final int DEVICE_IN_IP = DEVICE_BIT_IN | 0x80000;
+    /** @hide */
     public static final int DEVICE_IN_BUS = DEVICE_BIT_IN | 0x100000;
+    /** @hide */
     public static final int DEVICE_IN_PROXY = DEVICE_BIT_IN | 0x1000000;
+    /** @hide */
     public static final int DEVICE_IN_USB_HEADSET = DEVICE_BIT_IN | 0x2000000;
+    /** @hide */
     public static final int DEVICE_IN_BLUETOOTH_BLE = DEVICE_BIT_IN | 0x4000000;
+    /** @hide */
     public static final int DEVICE_IN_HDMI_ARC = DEVICE_BIT_IN | 0x8000000;
+    /** @hide */
     public static final int DEVICE_IN_ECHO_REFERENCE = DEVICE_BIT_IN | 0x10000000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT;
 
+    /** @hide */
     public static final Set<Integer> DEVICE_IN_ALL_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_IN_ALL_SCO_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_IN_ALL_USB_SET;
     static {
         DEVICE_IN_ALL_SET = new HashSet<>();
@@ -703,12 +832,15 @@
     }
 
     // device states, must match AudioSystem::device_connection_state
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_STATE_UNAVAILABLE = 0;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_STATE_AVAILABLE = 1;
     private static final int NUM_DEVICE_STATES = 1;
 
+    /** @hide */
     public static String deviceStateToString(int state) {
         switch (state) {
             case DEVICE_STATE_UNAVAILABLE: return "DEVICE_STATE_UNAVAILABLE";
@@ -717,63 +849,65 @@
         }
     }
 
-    public static final String DEVICE_OUT_EARPIECE_NAME = "earpiece";
-    public static final String DEVICE_OUT_SPEAKER_NAME = "speaker";
-    public static final String DEVICE_OUT_WIRED_HEADSET_NAME = "headset";
-    public static final String DEVICE_OUT_WIRED_HEADPHONE_NAME = "headphone";
-    public static final String DEVICE_OUT_BLUETOOTH_SCO_NAME = "bt_sco";
-    public static final String DEVICE_OUT_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs";
-    public static final String DEVICE_OUT_BLUETOOTH_SCO_CARKIT_NAME = "bt_sco_carkit";
-    public static final String DEVICE_OUT_BLUETOOTH_A2DP_NAME = "bt_a2dp";
+    /** @hide */ public static final String DEVICE_OUT_EARPIECE_NAME = "earpiece";
+    /** @hide */ public static final String DEVICE_OUT_SPEAKER_NAME = "speaker";
+    /** @hide */ public static final String DEVICE_OUT_WIRED_HEADSET_NAME = "headset";
+    /** @hide */ public static final String DEVICE_OUT_WIRED_HEADPHONE_NAME = "headphone";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_SCO_NAME = "bt_sco";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_SCO_CARKIT_NAME = "bt_sco_carkit";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_A2DP_NAME = "bt_a2dp";
+    /** @hide */
     public static final String DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES_NAME = "bt_a2dp_hp";
-    public static final String DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME = "bt_a2dp_spk";
-    public static final String DEVICE_OUT_AUX_DIGITAL_NAME = "aux_digital";
-    public static final String DEVICE_OUT_HDMI_NAME = "hdmi";
-    public static final String DEVICE_OUT_ANLG_DOCK_HEADSET_NAME = "analog_dock";
-    public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock";
-    public static final String DEVICE_OUT_USB_ACCESSORY_NAME = "usb_accessory";
-    public static final String DEVICE_OUT_USB_DEVICE_NAME = "usb_device";
-    public static final String DEVICE_OUT_REMOTE_SUBMIX_NAME = "remote_submix";
-    public static final String DEVICE_OUT_TELEPHONY_TX_NAME = "telephony_tx";
-    public static final String DEVICE_OUT_LINE_NAME = "line";
-    public static final String DEVICE_OUT_HDMI_ARC_NAME = "hmdi_arc";
-    public static final String DEVICE_OUT_SPDIF_NAME = "spdif";
-    public static final String DEVICE_OUT_FM_NAME = "fm_transmitter";
-    public static final String DEVICE_OUT_AUX_LINE_NAME = "aux_line";
-    public static final String DEVICE_OUT_SPEAKER_SAFE_NAME = "speaker_safe";
-    public static final String DEVICE_OUT_IP_NAME = "ip";
-    public static final String DEVICE_OUT_BUS_NAME = "bus";
-    public static final String DEVICE_OUT_PROXY_NAME = "proxy";
-    public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset";
-    public static final String DEVICE_OUT_HEARING_AID_NAME = "hearing_aid_out";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME = "bt_a2dp_spk";
+    /** @hide */ public static final String DEVICE_OUT_AUX_DIGITAL_NAME = "aux_digital";
+    /** @hide */ public static final String DEVICE_OUT_HDMI_NAME = "hdmi";
+    /** @hide */ public static final String DEVICE_OUT_ANLG_DOCK_HEADSET_NAME = "analog_dock";
+    /** @hide */ public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock";
+    /** @hide */ public static final String DEVICE_OUT_USB_ACCESSORY_NAME = "usb_accessory";
+    /** @hide */ public static final String DEVICE_OUT_USB_DEVICE_NAME = "usb_device";
+    /** @hide */ public static final String DEVICE_OUT_REMOTE_SUBMIX_NAME = "remote_submix";
+    /** @hide */ public static final String DEVICE_OUT_TELEPHONY_TX_NAME = "telephony_tx";
+    /** @hide */ public static final String DEVICE_OUT_LINE_NAME = "line";
+    /** @hide */ public static final String DEVICE_OUT_HDMI_ARC_NAME = "hmdi_arc";
+    /** @hide */ public static final String DEVICE_OUT_SPDIF_NAME = "spdif";
+    /** @hide */ public static final String DEVICE_OUT_FM_NAME = "fm_transmitter";
+    /** @hide */ public static final String DEVICE_OUT_AUX_LINE_NAME = "aux_line";
+    /** @hide */ public static final String DEVICE_OUT_SPEAKER_SAFE_NAME = "speaker_safe";
+    /** @hide */ public static final String DEVICE_OUT_IP_NAME = "ip";
+    /** @hide */ public static final String DEVICE_OUT_BUS_NAME = "bus";
+    /** @hide */ public static final String DEVICE_OUT_PROXY_NAME = "proxy";
+    /** @hide */ public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset";
+    /** @hide */ public static final String DEVICE_OUT_HEARING_AID_NAME = "hearing_aid_out";
 
-    public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
-    public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
-    public static final String DEVICE_IN_BUILTIN_MIC_NAME = "mic";
-    public static final String DEVICE_IN_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs";
-    public static final String DEVICE_IN_WIRED_HEADSET_NAME = "headset";
-    public static final String DEVICE_IN_AUX_DIGITAL_NAME = "aux_digital";
-    public static final String DEVICE_IN_TELEPHONY_RX_NAME = "telephony_rx";
-    public static final String DEVICE_IN_BACK_MIC_NAME = "back_mic";
-    public static final String DEVICE_IN_REMOTE_SUBMIX_NAME = "remote_submix";
-    public static final String DEVICE_IN_ANLG_DOCK_HEADSET_NAME = "analog_dock";
-    public static final String DEVICE_IN_DGTL_DOCK_HEADSET_NAME = "digital_dock";
-    public static final String DEVICE_IN_USB_ACCESSORY_NAME = "usb_accessory";
-    public static final String DEVICE_IN_USB_DEVICE_NAME = "usb_device";
-    public static final String DEVICE_IN_FM_TUNER_NAME = "fm_tuner";
-    public static final String DEVICE_IN_TV_TUNER_NAME = "tv_tuner";
-    public static final String DEVICE_IN_LINE_NAME = "line";
-    public static final String DEVICE_IN_SPDIF_NAME = "spdif";
-    public static final String DEVICE_IN_BLUETOOTH_A2DP_NAME = "bt_a2dp";
-    public static final String DEVICE_IN_LOOPBACK_NAME = "loopback";
-    public static final String DEVICE_IN_IP_NAME = "ip";
-    public static final String DEVICE_IN_BUS_NAME = "bus";
-    public static final String DEVICE_IN_PROXY_NAME = "proxy";
-    public static final String DEVICE_IN_USB_HEADSET_NAME = "usb_headset";
-    public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble";
-    public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference";
-    public static final String DEVICE_IN_HDMI_ARC_NAME = "hdmi_arc";
+    /** @hide */ public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
+    /** @hide */ public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
+    /** @hide */ public static final String DEVICE_IN_BUILTIN_MIC_NAME = "mic";
+    /** @hide */ public static final String DEVICE_IN_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs";
+    /** @hide */ public static final String DEVICE_IN_WIRED_HEADSET_NAME = "headset";
+    /** @hide */ public static final String DEVICE_IN_AUX_DIGITAL_NAME = "aux_digital";
+    /** @hide */ public static final String DEVICE_IN_TELEPHONY_RX_NAME = "telephony_rx";
+    /** @hide */ public static final String DEVICE_IN_BACK_MIC_NAME = "back_mic";
+    /** @hide */ public static final String DEVICE_IN_REMOTE_SUBMIX_NAME = "remote_submix";
+    /** @hide */ public static final String DEVICE_IN_ANLG_DOCK_HEADSET_NAME = "analog_dock";
+    /** @hide */ public static final String DEVICE_IN_DGTL_DOCK_HEADSET_NAME = "digital_dock";
+    /** @hide */ public static final String DEVICE_IN_USB_ACCESSORY_NAME = "usb_accessory";
+    /** @hide */ public static final String DEVICE_IN_USB_DEVICE_NAME = "usb_device";
+    /** @hide */ public static final String DEVICE_IN_FM_TUNER_NAME = "fm_tuner";
+    /** @hide */ public static final String DEVICE_IN_TV_TUNER_NAME = "tv_tuner";
+    /** @hide */ public static final String DEVICE_IN_LINE_NAME = "line";
+    /** @hide */ public static final String DEVICE_IN_SPDIF_NAME = "spdif";
+    /** @hide */ public static final String DEVICE_IN_BLUETOOTH_A2DP_NAME = "bt_a2dp";
+    /** @hide */ public static final String DEVICE_IN_LOOPBACK_NAME = "loopback";
+    /** @hide */ public static final String DEVICE_IN_IP_NAME = "ip";
+    /** @hide */ public static final String DEVICE_IN_BUS_NAME = "bus";
+    /** @hide */ public static final String DEVICE_IN_PROXY_NAME = "proxy";
+    /** @hide */ public static final String DEVICE_IN_USB_HEADSET_NAME = "usb_headset";
+    /** @hide */ public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble";
+    /** @hide */ public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference";
+    /** @hide */ public static final String DEVICE_IN_HDMI_ARC_NAME = "hdmi_arc";
 
+    /** @hide */
     @UnsupportedAppUsage
     public static String getOutputDeviceName(int device)
     {
@@ -840,6 +974,7 @@
         }
     }
 
+    /** @hide */
     public static String getInputDeviceName(int device)
     {
         switch(device) {
@@ -902,35 +1037,31 @@
     }
 
     // phone state, match audio_mode???
-    public static final int PHONE_STATE_OFFCALL = 0;
-    public static final int PHONE_STATE_RINGING = 1;
-    public static final int PHONE_STATE_INCALL = 2;
+    /** @hide */ public static final int PHONE_STATE_OFFCALL = 0;
+    /** @hide */ public static final int PHONE_STATE_RINGING = 1;
+    /** @hide */ public static final int PHONE_STATE_INCALL = 2;
 
     // device categories config for setForceUse, must match audio_policy_forced_cfg_t
-    @UnsupportedAppUsage
-    public static final int FORCE_NONE = 0;
-    public static final int FORCE_SPEAKER = 1;
-    public static final int FORCE_HEADPHONES = 2;
-    public static final int FORCE_BT_SCO = 3;
-    public static final int FORCE_BT_A2DP = 4;
-    public static final int FORCE_WIRED_ACCESSORY = 5;
-    @UnsupportedAppUsage
-    public static final int FORCE_BT_CAR_DOCK = 6;
-    @UnsupportedAppUsage
-    public static final int FORCE_BT_DESK_DOCK = 7;
-    @UnsupportedAppUsage
-    public static final int FORCE_ANALOG_DOCK = 8;
-    @UnsupportedAppUsage
-    public static final int FORCE_DIGITAL_DOCK = 9;
-    public static final int FORCE_NO_BT_A2DP = 10;
-    public static final int FORCE_SYSTEM_ENFORCED = 11;
-    public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12;
-    public static final int FORCE_ENCODED_SURROUND_NEVER = 13;
-    public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14;
-    public static final int FORCE_ENCODED_SURROUND_MANUAL = 15;
-    public static final int NUM_FORCE_CONFIG = 16;
-    public static final int FORCE_DEFAULT = FORCE_NONE;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_NONE = 0;
+    /** @hide */ public static final int FORCE_SPEAKER = 1;
+    /** @hide */ public static final int FORCE_HEADPHONES = 2;
+    /** @hide */ public static final int FORCE_BT_SCO = 3;
+    /** @hide */ public static final int FORCE_BT_A2DP = 4;
+    /** @hide */ public static final int FORCE_WIRED_ACCESSORY = 5;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_BT_CAR_DOCK = 6;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_BT_DESK_DOCK = 7;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_ANALOG_DOCK = 8;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_DIGITAL_DOCK = 9;
+    /** @hide */ public static final int FORCE_NO_BT_A2DP = 10;
+    /** @hide */ public static final int FORCE_SYSTEM_ENFORCED = 11;
+    /** @hide */ public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12;
+    /** @hide */ public static final int FORCE_ENCODED_SURROUND_NEVER = 13;
+    /** @hide */ public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14;
+    /** @hide */ public static final int FORCE_ENCODED_SURROUND_MANUAL = 15;
+    /** @hide */ public static final int NUM_FORCE_CONFIG = 16;
+    /** @hide */ public static final int FORCE_DEFAULT = FORCE_NONE;
 
+    /** @hide */
     public static String forceUseConfigToString(int config) {
         switch (config) {
             case FORCE_NONE: return "FORCE_NONE";
@@ -954,16 +1085,17 @@
     }
 
     // usage for setForceUse, must match audio_policy_force_use_t
-    public static final int FOR_COMMUNICATION = 0;
-    public static final int FOR_MEDIA = 1;
-    public static final int FOR_RECORD = 2;
-    public static final int FOR_DOCK = 3;
-    public static final int FOR_SYSTEM = 4;
-    public static final int FOR_HDMI_SYSTEM_AUDIO = 5;
-    public static final int FOR_ENCODED_SURROUND = 6;
-    public static final int FOR_VIBRATE_RINGING = 7;
+    /** @hide */ public static final int FOR_COMMUNICATION = 0;
+    /** @hide */ public static final int FOR_MEDIA = 1;
+    /** @hide */ public static final int FOR_RECORD = 2;
+    /** @hide */ public static final int FOR_DOCK = 3;
+    /** @hide */ public static final int FOR_SYSTEM = 4;
+    /** @hide */ public static final int FOR_HDMI_SYSTEM_AUDIO = 5;
+    /** @hide */ public static final int FOR_ENCODED_SURROUND = 6;
+    /** @hide */ public static final int FOR_VIBRATE_RINGING = 7;
     private static final int NUM_FORCE_USE = 8;
 
+    /** @hide */
     public static String forceUseUsageToString(int usage) {
         switch (usage) {
             case FOR_COMMUNICATION: return "FOR_COMMUNICATION";
@@ -978,7 +1110,7 @@
         }
     }
 
-    /** Wrapper for native methods called from AudioService */
+    /** @hide Wrapper for native methods called from AudioService */
     public static int setStreamVolumeIndexAS(int stream, int index, int device) {
         if (DEBUG_VOLUME) {
             Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream]
@@ -988,10 +1120,11 @@
     }
 
     // usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t
-    public static final int SYNC_EVENT_NONE = 0;
-    public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1;
+    /** @hide */ public static final int SYNC_EVENT_NONE = 0;
+    /** @hide */ public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1;
 
     /**
+     * @hide
      * @return command completion status, one of {@link #AUDIO_STATUS_OK},
      *     {@link #AUDIO_STATUS_ERROR} or {@link #AUDIO_STATUS_SERVER_DIED}
      */
@@ -999,22 +1132,29 @@
     public static native int setDeviceConnectionState(int device, int state,
                                                       String device_address, String device_name,
                                                       int codecFormat);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int getDeviceConnectionState(int device, String device_address);
+    /** @hide */
     public static native int handleDeviceConfigChange(int device,
                                                       String device_address,
                                                       String device_name,
                                                       int codecFormat);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int setPhoneState(int state);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int setForceUse(int usage, int config);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int getForceUse(int usage);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int initStreamVolume(int stream, int indexMin, int indexMax);
     @UnsupportedAppUsage
     private static native int setStreamVolumeIndex(int stream, int index, int device);
+    /** @hide */
     public static native int getStreamVolumeIndex(int stream, int device);
     /**
      * @hide
@@ -1051,12 +1191,17 @@
      */
     public static native int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attributes);
 
+    /** @hide */
     public static native int setMasterVolume(float value);
+    /** @hide */
     public static native float getMasterVolume();
+    /** @hide */
     @UnsupportedAppUsage
     public static native int setMasterMute(boolean mute);
+    /** @hide */
     @UnsupportedAppUsage
     public static native boolean getMasterMute();
+    /** @hide */
     @UnsupportedAppUsage
     public static native int getDevicesForStream(int stream);
 
@@ -1070,31 +1215,43 @@
     /** @hide returns master balance value in range -1.f -> 1.f, where 0.f is dead center. */
     @TestApi
     public static native float getMasterBalance();
-    /** @hide changes the audio balance of the device. */
+    /** @hide Changes the audio balance of the device. */
     @TestApi
     public static native int setMasterBalance(float balance);
 
     // helpers for android.media.AudioManager.getProperty(), see description there for meaning
+    /** @hide */
     @UnsupportedAppUsage(trackingBug = 134049522)
     public static native int getPrimaryOutputSamplingRate();
+    /** @hide */
     @UnsupportedAppUsage(trackingBug = 134049522)
     public static native int getPrimaryOutputFrameCount();
+    /** @hide */
     @UnsupportedAppUsage
     public static native int getOutputLatency(int stream);
 
+    /** @hide */
     public static native int setLowRamDevice(boolean isLowRamDevice, long totalMemory);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int checkAudioFlinger();
 
+    /** @hide */
     public static native int listAudioPorts(ArrayList<AudioPort> ports, int[] generation);
+    /** @hide */
     public static native int createAudioPatch(AudioPatch[] patch,
                                             AudioPortConfig[] sources, AudioPortConfig[] sinks);
+    /** @hide */
     public static native int releaseAudioPatch(AudioPatch patch);
+    /** @hide */
     public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation);
+    /** @hide */
     public static native int setAudioPortConfig(AudioPortConfig config);
 
+    /** @hide */
     public static native int startAudioSource(AudioPortConfig config,
                                               AudioAttributes audioAttributes);
+    /** @hide */
     public static native int stopAudioSource(int handle);
 
     // declare this instance as having a dynamic policy callback handler
@@ -1103,24 +1260,29 @@
     private static native final void native_register_recording_callback();
 
     // must be kept in sync with value in include/system/audio.h
-    public static final int AUDIO_HW_SYNC_INVALID = 0;
+    /** @hide */ public static final int AUDIO_HW_SYNC_INVALID = 0;
 
+    /** @hide */
     public static native int getAudioHwSyncForSession(int sessionId);
 
+    /** @hide */
     public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register);
 
-    /** see AudioPolicy.setUidDeviceAffinities() */
+    /** @hide see AudioPolicy.setUidDeviceAffinities() */
     public static native int setUidDeviceAffinities(int uid, @NonNull int[] types,
             @NonNull String[] addresses);
 
-    /** see AudioPolicy.removeUidDeviceAffinities() */
+    /** @hide see AudioPolicy.removeUidDeviceAffinities() */
     public static native int removeUidDeviceAffinities(int uid);
 
+    /** @hide */
     public static native int systemReady();
 
+    /** @hide */
     public static native float getStreamVolumeDB(int stream, int index, int device);
 
     /**
+     * @hide
      * @see AudioManager#setAllowedCapturePolicy()
      */
     public static native int setAllowedCapturePolicy(int uid, int flags);
@@ -1134,34 +1296,43 @@
     private static native boolean native_is_offload_supported(int encoding, int sampleRate,
             int channelMask, int channelIndexMask, int streamType);
 
+    /** @hide */
     public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo);
 
+    /** @hide */
     public static native int getSurroundFormats(Map<Integer, Boolean> surroundFormats,
                                                 boolean reported);
 
     /**
+     * @hide
      * Returns a list of audio formats (codec) supported on the A2DP offload path.
      */
     public static native int getHwOffloadEncodingFormatsSupportedForA2DP(
             ArrayList<Integer> formatList);
 
+    /** @hide */
     public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled);
 
     /**
+     * @hide
      * Communicate UID of active assistant to audio policy service.
      */
     public static native int setAssistantUid(int uid);
+
     /**
+     * @hide
      * Communicate UIDs of active accessibility services to audio policy service.
      */
     public static native int setA11yServicesUids(int[] uids);
 
     /**
+     * @hide
      * @see AudioManager#isHapticPlaybackSupported()
      */
     public static native boolean isHapticPlaybackSupported();
 
     /**
+     * @hide
      * Send audio HAL server process pids to native audioserver process for use
      * when generating audio HAL servers tombstones
      */
@@ -1170,6 +1341,7 @@
     // Items shared with audio service
 
     /**
+     * @hide
      * The delay before playing a sound. This small period exists so the user
      * can press another key (non-volume keys, too) to have it NOT be audible.
      * <p>
@@ -1178,6 +1350,7 @@
     public static final int PLAY_SOUND_DELAY = 300;
 
     /**
+     * @hide
      * Constant to identify a focus stack entry that is used to hold the focus while the phone
      * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
      * entering and exiting calls.
@@ -1185,6 +1358,7 @@
     public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
 
     /**
+     * @hide
      * @see AudioManager#setVibrateSetting(int, int)
      */
     public static int getValueForVibrateSetting(int existingValue, int vibrateType,
@@ -1200,10 +1374,12 @@
         return existingValue;
     }
 
+    /** @hide */
     public static int getDefaultStreamVolume(int streamType) {
         return DEFAULT_STREAM_VOLUME[streamType];
     }
 
+    /** @hide */
     public static int[] DEFAULT_STREAM_VOLUME = new int[] {
         4,  // STREAM_VOICE_CALL
         7,  // STREAM_SYSTEM
@@ -1218,20 +1394,22 @@
         5, // STREAM_ACCESSIBILITY
     };
 
+    /** @hide */
     public static String streamToString(int stream) {
         if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
         if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
         return "UNKNOWN_STREAM_" + stream;
     }
 
-    /** The platform has no specific capabilities */
+    /** @hide The platform has no specific capabilities */
     public static final int PLATFORM_DEFAULT = 0;
-    /** The platform is voice call capable (a phone) */
+    /** @hide The platform is voice call capable (a phone) */
     public static final int PLATFORM_VOICE = 1;
-    /** The platform is a television or a set-top box */
+    /** @hide The platform is a television or a set-top box */
     public static final int PLATFORM_TELEVISION = 2;
 
     /**
+     * @hide
      * Return the platform type that this is running on. One of:
      * <ul>
      * <li>{@link #PLATFORM_VOICE}</li>
@@ -1261,6 +1439,7 @@
     }
 
     /**
+     * @hide
      * Return a set of audio device types from a bit mask audio device type, which may
      * represent multiple audio device types.
      * FIXME: Remove this when getting ride of bit mask usage of audio device types.
@@ -1278,6 +1457,7 @@
     }
 
     /**
+     * @hide
      * Return the intersection of two audio device types collections.
      */
     public static Set<Integer> intersectionAudioDeviceTypes(
@@ -1288,12 +1468,14 @@
     }
 
     /**
+     * @hide
      * Return true if the audio device types collection only contains the given device type.
      */
     public static boolean isSingleAudioDeviceType(@NonNull Set<Integer> types, int type) {
         return types.size() == 1 && types.contains(type);
     }
 
+    /** @hide */
     public static final int DEFAULT_MUTE_STREAMS_AFFECTED =
             (1 << STREAM_MUSIC) |
             (1 << STREAM_RING) |
@@ -1303,6 +1485,7 @@
             (1 << STREAM_BLUETOOTH_SCO);
 
     /**
+     * @hide
      * Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes.
      * Keep in sync with core/jni/android_media_DeviceCallback.h.
      */
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 0ced68ef..babe072 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1098,7 +1098,7 @@
      * Can only be called only if the AudioTrack is opened in offload mode
      * {@see Builder#setOffloadedPlayback(boolean)}.
      * Can only be called only if the AudioTrack is in state {@link #PLAYSTATE_PLAYING}
-     * {@see #getPlaystate()}.
+     * {@see #getPlayState()}.
      * Use this method in the same thread as any write() operation.
      */
     public void setOffloadEndOfStream() {
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 392e8fe..a5da648 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -20,9 +20,10 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.lang.ref.WeakReference;
 
 /**
@@ -158,6 +159,7 @@
     /**
      * Indicates the state of the Visualizer instance
      */
+    @GuardedBy("mStateLock")
     private int mState = STATE_UNINITIALIZED;
     /**
      * Lock to synchronize access to mState
@@ -166,6 +168,7 @@
     /**
      * System wide unique Identifier of the visualizer engine used by this Visualizer instance
      */
+    @GuardedBy("mStateLock")
     @UnsupportedAppUsage
     private int mId;
 
@@ -176,19 +179,24 @@
     /**
      * Handler for events coming from the native code
      */
-    private NativeEventHandler mNativeEventHandler = null;
+    @GuardedBy("mListenerLock")
+    private Handler mNativeEventHandler = null;
     /**
      *  PCM and FFT capture listener registered by client
      */
+    @GuardedBy("mListenerLock")
     private OnDataCaptureListener mCaptureListener = null;
     /**
      *  Server Died listener registered by client
      */
+    @GuardedBy("mListenerLock")
     private OnServerDiedListener mServerDiedListener = null;
 
     // accessed by native methods
-    private long mNativeVisualizer;
-    private long mJniData;
+    private long mNativeVisualizer;  // guarded by a static lock in native code
+    private long mJniData;  // set in native_setup, _release;
+                            // get in native_release, _setEnabled, _setPeriodicCapture
+                            // thus, effectively guarded by mStateLock
 
     //--------------------------------------------------------------------------
     // Constructor, Finalize
@@ -244,7 +252,9 @@
 
     @Override
     protected void finalize() {
-        native_finalize();
+        synchronized (mStateLock) {
+            native_finalize();
+        }
     }
 
     /**
@@ -601,25 +611,28 @@
      */
     public int setDataCaptureListener(OnDataCaptureListener listener,
             int rate, boolean waveform, boolean fft) {
-        synchronized (mListenerLock) {
-            mCaptureListener = listener;
-        }
         if (listener == null) {
             // make sure capture callback is stopped in native code
             waveform = false;
             fft = false;
         }
-        int status = native_setPeriodicCapture(rate, waveform, fft);
+        int status;
+        synchronized (mStateLock) {
+            status = native_setPeriodicCapture(rate, waveform, fft);
+        }
         if (status == SUCCESS) {
-            if ((listener != null) && (mNativeEventHandler == null)) {
-                Looper looper;
-                if ((looper = Looper.myLooper()) != null) {
-                    mNativeEventHandler = new NativeEventHandler(this, looper);
-                } else if ((looper = Looper.getMainLooper()) != null) {
-                    mNativeEventHandler = new NativeEventHandler(this, looper);
-                } else {
-                    mNativeEventHandler = null;
-                    status = ERROR_NO_INIT;
+            synchronized (mListenerLock) {
+                mCaptureListener = listener;
+                if ((listener != null) && (mNativeEventHandler == null)) {
+                    Looper looper;
+                    if ((looper = Looper.myLooper()) != null) {
+                        mNativeEventHandler = new Handler(looper);
+                    } else if ((looper = Looper.getMainLooper()) != null) {
+                        mNativeEventHandler = new Handler(looper);
+                    } else {
+                        mNativeEventHandler = null;
+                        status = ERROR_NO_INIT;
+                    }
                 }
             }
         }
@@ -663,112 +676,61 @@
         return SUCCESS;
     }
 
-    /**
-     * Helper class to handle the forwarding of native events to the appropriate listeners
-     */
-    private class NativeEventHandler extends Handler
-    {
-        private Visualizer mVisualizer;
-
-        public NativeEventHandler(Visualizer v, Looper looper) {
-            super(looper);
-            mVisualizer = v;
-        }
-
-        private void handleCaptureMessage(Message msg) {
-            OnDataCaptureListener l = null;
-            synchronized (mListenerLock) {
-                l = mVisualizer.mCaptureListener;
-            }
-
-            if (l != null) {
-                byte[] data = (byte[])msg.obj;
-                int samplingRate = msg.arg1;
-
-                switch(msg.what) {
-                case NATIVE_EVENT_PCM_CAPTURE:
-                    l.onWaveFormDataCapture(mVisualizer, data, samplingRate);
-                    break;
-                case NATIVE_EVENT_FFT_CAPTURE:
-                    l.onFftDataCapture(mVisualizer, data, samplingRate);
-                    break;
-                default:
-                    Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what);
-                    break;
-                }
-            }
-        }
-
-        private void handleServerDiedMessage(Message msg) {
-            OnServerDiedListener l = null;
-            synchronized (mListenerLock) {
-                l = mVisualizer.mServerDiedListener;
-            }
-
-            if (l != null)
-                l.onServerDied();
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (mVisualizer == null) {
-                return;
-            }
-
-            switch(msg.what) {
-            case NATIVE_EVENT_PCM_CAPTURE:
-            case NATIVE_EVENT_FFT_CAPTURE:
-                handleCaptureMessage(msg);
-                break;
-            case NATIVE_EVENT_SERVER_DIED:
-                handleServerDiedMessage(msg);
-                break;
-            default:
-                Log.e(TAG,"Unknown native event: "+msg.what);
-                break;
-            }
-        }
-    }
-
     //---------------------------------------------------------
     // Interface definitions
     //--------------------
 
     private static native final void native_init();
 
+    @GuardedBy("mStateLock")
     private native final int native_setup(Object audioeffect_this,
                                           int audioSession,
                                           int[] id,
                                           String opPackageName);
 
+    @GuardedBy("mStateLock")
     private native final void native_finalize();
 
+    @GuardedBy("mStateLock")
     private native final void native_release();
 
+    @GuardedBy("mStateLock")
     private native final int native_setEnabled(boolean enabled);
 
+    @GuardedBy("mStateLock")
     private native final boolean native_getEnabled();
 
+    @GuardedBy("mStateLock")
     private native final int native_setCaptureSize(int size);
 
+    @GuardedBy("mStateLock")
     private native final int native_getCaptureSize();
 
+    @GuardedBy("mStateLock")
     private native final int native_setScalingMode(int mode);
 
+    @GuardedBy("mStateLock")
     private native final int native_getScalingMode();
 
+    @GuardedBy("mStateLock")
     private native final int native_setMeasurementMode(int mode);
 
+    @GuardedBy("mStateLock")
     private native final int native_getMeasurementMode();
 
+    @GuardedBy("mStateLock")
     private native final int native_getSamplingRate();
 
+    @GuardedBy("mStateLock")
     private native final int native_getWaveForm(byte[] waveform);
 
+    @GuardedBy("mStateLock")
     private native final int native_getFft(byte[] fft);
 
+    @GuardedBy("mStateLock")
     private native final int native_getPeakRms(MeasurementPeakRms measurement);
 
+    @GuardedBy("mStateLock")
     private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft);
 
     //---------------------------------------------------------
@@ -776,17 +738,47 @@
     //--------------------
     @SuppressWarnings("unused")
     private static void postEventFromNative(Object effect_ref,
-            int what, int arg1, int arg2, Object obj) {
-        Visualizer visu = (Visualizer)((WeakReference)effect_ref).get();
-        if (visu == null) {
-            return;
-        }
+            int what, int samplingRate, byte[] data) {
+        final Visualizer visualizer = (Visualizer) ((WeakReference) effect_ref).get();
+        if (visualizer == null) return;
 
-        if (visu.mNativeEventHandler != null) {
-            Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
-            visu.mNativeEventHandler.sendMessage(m);
+        final Handler handler;
+        synchronized (visualizer.mListenerLock) {
+            handler = visualizer.mNativeEventHandler;
         }
+        if (handler == null) return;
 
+        switch (what) {
+            case NATIVE_EVENT_PCM_CAPTURE:
+            case NATIVE_EVENT_FFT_CAPTURE:
+                handler.post(() -> {
+                    final OnDataCaptureListener l;
+                    synchronized (visualizer.mListenerLock) {
+                        l = visualizer.mCaptureListener;
+                    }
+                    if (l != null) {
+                        if (what == NATIVE_EVENT_PCM_CAPTURE) {
+                            l.onWaveFormDataCapture(visualizer, data, samplingRate);
+                        } else { // what == NATIVE_EVENT_FFT_CAPTURE
+                            l.onFftDataCapture(visualizer, data, samplingRate);
+                        }
+                    }
+                });
+                break;
+            case NATIVE_EVENT_SERVER_DIED:
+                handler.post(() -> {
+                    final OnServerDiedListener l;
+                    synchronized (visualizer.mListenerLock) {
+                        l = visualizer.mServerDiedListener;
+                    }
+                    if (l != null) {
+                        l.onServerDied();
+                    }
+                });
+                break;
+            default:
+                Log.e(TAG, "Unknown native event in postEventFromNative: " + what);
+                break;
+        }
     }
 }
-
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index a62e847..75c6e1e 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -336,8 +336,8 @@
         if (refAttr.equals(sDefaultAttributes)) {
             return false;
         }
-        return ((refAttr.getUsage() == AudioAttributes.USAGE_UNKNOWN)
-                || (attr.getUsage() == refAttr.getUsage()))
+        return ((refAttr.getSystemUsage() == AudioAttributes.USAGE_UNKNOWN)
+                || (attr.getSystemUsage() == refAttr.getSystemUsage()))
             && ((refAttr.getContentType() == AudioAttributes.CONTENT_TYPE_UNKNOWN)
                 || (attr.getContentType() == refAttr.getContentType()))
             && ((refAttr.getAllFlags() == 0)
diff --git a/media/java/android/media/tv/OWNERS b/media/java/android/media/tv/OWNERS
index 64c0bb5..a891154 100644
--- a/media/java/android/media/tv/OWNERS
+++ b/media/java/android/media/tv/OWNERS
@@ -3,3 +3,7 @@
 shubang@google.com
 quxiangfang@google.com
 
+# For android remote service
+per-file ITvRemoteServiceInput.aidl = file:/media/lib/tvremote/OWNERS
+per-file ITvRemoteProvider.aidl = file:/media/lib/tvremote/OWNERS
+
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index 83f3b6e..efeb335 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -120,8 +120,9 @@
     }
 
     if (mCaptureThread != 0) {
+        sp<CaptureThread> t = mCaptureThread;
         mCaptureLock.unlock();
-        mCaptureThread->requestExitAndWait();
+        t->requestExitAndWait();
         mCaptureLock.lock();
     }
 
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 1362433..f9a77f4 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -196,7 +196,6 @@
                 callbackInfo->visualizer_ref,
                 NATIVE_EVENT_PCM_CAPTURE,
                 samplingrate,
-                0,
                 jArray);
         }
     }
@@ -217,7 +216,6 @@
                 callbackInfo->visualizer_ref,
                 NATIVE_EVENT_FFT_CAPTURE,
                 samplingrate,
-                0,
                 jArray);
         }
     }
@@ -286,7 +284,7 @@
     // Get the postEvent method
     fields.midPostNativeEvent = env->GetStaticMethodID(
             fields.clazzEffect,
-            "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+            "postEventFromNative", "(Ljava/lang/Object;II[B)V");
     if (fields.midPostNativeEvent == NULL) {
         ALOGE("Can't find Visualizer.%s", "postEventFromNative");
         return;
@@ -343,7 +341,7 @@
             fields.midPostNativeEvent,
             callbackInfo->visualizer_ref,
             NATIVE_EVENT_SERVER_DIED,
-            0, 0, NULL);
+            0, NULL);
     }
 }
 
diff --git a/media/mca/filterfw/Android.bp b/media/mca/filterfw/Android.bp
index 71899cf..0e0ecf3 100644
--- a/media/mca/filterfw/Android.bp
+++ b/media/mca/filterfw/Android.bp
@@ -65,6 +65,8 @@
         "-Wno-unused-parameter",
     ],
 
+    header_libs: ["jni_headers"],
+
     shared_libs: [
         "libmedia",
         "libgui",
diff --git a/media/tests/AudioPolicyTest/Android.bp b/media/tests/AudioPolicyTest/Android.bp
new file mode 100644
index 0000000..ed338375
--- /dev/null
+++ b/media/tests/AudioPolicyTest/Android.bp
@@ -0,0 +1,17 @@
+android_test {
+    name: "audiopolicytest",
+    srcs: ["**/*.java"],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: [
+        "mockito-target-minus-junit4",
+        "androidx.test.rules",
+        "android-ex-camera2",
+        "testng",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+    resource_dirs: ["res"],
+}
diff --git a/media/tests/AudioPolicyTest/AndroidManifest.xml b/media/tests/AudioPolicyTest/AndroidManifest.xml
new file mode 100644
index 0000000..ae5bfaf
--- /dev/null
+++ b/media/tests/AudioPolicyTest/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.audiopolicytest">
+
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:label="@string/app_name" android:name="AudioPolicyTest"
+                  android:screenOrientation="landscape">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <!--instrumentation android:name=".AudioPolicyTestRunner"
+            android:targetPackage="com.android.audiopolicytest"
+            android:label="AudioManager policy oriented integration tests InstrumentationRunner">
+    </instrumentation-->
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.audiopolicytest"
+            android:label="AudioManager policy oriented integration tests InstrumentationRunner">
+    </instrumentation>
+</manifest>
diff --git a/media/tests/AudioPolicyTest/AndroidTest.xml b/media/tests/AudioPolicyTest/AndroidTest.xml
new file mode 100644
index 0000000..f3ca9a1
--- /dev/null
+++ b/media/tests/AudioPolicyTest/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Media Framework Tests">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="audiopolicytest.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="AudioPolicyTest" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.audiopolicytest" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml b/media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml
new file mode 100644
index 0000000..17fdba6
--- /dev/null
+++ b/media/tests/AudioPolicyTest/res/layout/audiopolicytest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical">
+</LinearLayout>
diff --git a/media/tests/AudioPolicyTest/res/values/strings.xml b/media/tests/AudioPolicyTest/res/values/strings.xml
new file mode 100644
index 0000000..0365927
--- /dev/null
+++ b/media/tests/AudioPolicyTest/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- name of the app [CHAR LIMIT=25]-->
+    <string name="app_name">Audio Policy APIs Tests</string>
+</resources>
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
new file mode 100644
index 0000000..ff21f0a
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
@@ -0,0 +1,327 @@
+/*
+ * 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.audiopolicytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.testng.Assert.assertThrows;
+
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.util.Log;
+
+import com.google.common.primitives.Ints;
+
+import java.util.List;
+
+public class AudioManagerTest extends AudioVolumesTestBase {
+    private static final String TAG = "AudioManagerTest";
+
+    //-----------------------------------------------------------------
+    // Test getAudioProductStrategies and validate strategies
+    //-----------------------------------------------------------------
+    public void testGetAndValidateProductStrategies() throws Exception {
+        List<AudioProductStrategy> audioProductStrategies =
+                mAudioManager.getAudioProductStrategies();
+        assertTrue(audioProductStrategies.size() > 0);
+
+        List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+        assertTrue(audioVolumeGroups.size() > 0);
+
+        // Validate Audio Product Strategies
+        for (final AudioProductStrategy audioProductStrategy : audioProductStrategies) {
+            AudioAttributes attributes = audioProductStrategy.getAudioAttributes();
+            int strategyStreamType =
+                    audioProductStrategy.getLegacyStreamTypeForAudioAttributes(attributes);
+
+            assertTrue("Strategy shall support the attributes retrieved from its getter API",
+                    audioProductStrategy.supportsAudioAttributes(attributes));
+
+            int volumeGroupId =
+                    audioProductStrategy.getVolumeGroupIdForAudioAttributes(attributes);
+
+            // A strategy must be associated to a volume group
+            assertNotEquals("strategy not assigned to any volume group",
+                    volumeGroupId, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+
+            // Valid Group ?
+            AudioVolumeGroup audioVolumeGroup = null;
+            for (final AudioVolumeGroup avg : audioVolumeGroups) {
+                if (avg.getId() == volumeGroupId) {
+                    audioVolumeGroup = avg;
+                    break;
+                }
+            }
+            assertNotNull("Volume Group not found", audioVolumeGroup);
+
+            // Cross check: the group shall have at least one aa / stream types following the
+            // considered strategy
+            boolean strategyAttributesSupported = false;
+            for (final AudioAttributes aa : audioVolumeGroup.getAudioAttributes()) {
+                if (audioProductStrategy.supportsAudioAttributes(aa)) {
+                    strategyAttributesSupported = true;
+                    break;
+                }
+            }
+            assertTrue("Volume Group and Strategy mismatching", strategyAttributesSupported);
+
+            // Some Product strategy may not have corresponding stream types as they intends
+            // to address volume setting per attributes to avoid adding new stream type
+            // and going on deprecating the stream type even for volume
+            if (strategyStreamType != AudioSystem.STREAM_DEFAULT) {
+                boolean strategStreamTypeSupported = false;
+                for (final int vgStreamType : audioVolumeGroup.getLegacyStreamTypes()) {
+                    if (vgStreamType == strategyStreamType) {
+                        strategStreamTypeSupported = true;
+                        break;
+                    }
+                }
+                assertTrue("Volume Group and Strategy mismatching", strategStreamTypeSupported);
+            }
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // Test getAudioVolumeGroups and validate volume groups
+    //-----------------------------------------------------------------
+
+    public void testGetAndValidateVolumeGroups() throws Exception {
+        List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+        assertTrue(audioVolumeGroups.size() > 0);
+
+        List<AudioProductStrategy> audioProductStrategies =
+                mAudioManager.getAudioProductStrategies();
+        assertTrue(audioProductStrategies.size() > 0);
+
+        // Validate Audio Volume Groups, check all
+        for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
+            List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
+            int[] avgStreamTypes = audioVolumeGroup.getLegacyStreamTypes();
+
+            // for each volume group attributes, find the matching product strategy and ensure
+            // it is linked the considered volume group
+            for (final AudioAttributes aa : avgAttributes) {
+                if (aa.equals(sDefaultAttributes)) {
+                    // Some volume groups may not have valid attributes, used for internal
+                    // volume management like patch/rerouting
+                    // so bailing out strategy retrieval from attributes
+                    continue;
+                }
+                boolean isVolumeGroupAssociatedToStrategy = false;
+                for (final AudioProductStrategy strategy : audioProductStrategies) {
+                    int groupId = strategy.getVolumeGroupIdForAudioAttributes(aa);
+                    if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+
+                        assertEquals("Volume Group ID (" + audioVolumeGroup.toString()
+                                + "), and Volume group ID associated to Strategy ("
+                                + strategy.toString() + ") both supporting attributes "
+                                + aa.toString() + " are mismatching",
+                                audioVolumeGroup.getId(), groupId);
+                        isVolumeGroupAssociatedToStrategy = true;
+                        break;
+                    }
+                }
+                assertTrue("Volume Group (" + audioVolumeGroup.toString()
+                        + ") has no associated strategy for attributes " + aa.toString(),
+                        isVolumeGroupAssociatedToStrategy);
+            }
+
+            // for each volume group stream type, find the matching product strategy and ensure
+            // it is linked the considered volume group
+            for (final int avgStreamType : avgStreamTypes) {
+                if (avgStreamType == AudioSystem.STREAM_DEFAULT) {
+                    // Some Volume Groups may not have corresponding stream types as they
+                    // intends to address volume setting per attributes to avoid adding new
+                    //  stream type and going on deprecating the stream type even for volume
+                    // so bailing out strategy retrieval from stream type
+                    continue;
+                }
+                boolean isVolumeGroupAssociatedToStrategy = false;
+                for (final AudioProductStrategy strategy : audioProductStrategies) {
+                    Log.i(TAG, "strategy:" + strategy.toString());
+                    int groupId = strategy.getVolumeGroupIdForLegacyStreamType(avgStreamType);
+                    if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+
+                        assertEquals("Volume Group ID (" + audioVolumeGroup.toString()
+                                + "), and Volume group ID associated to Strategy ("
+                                + strategy.toString() + ") both supporting stream "
+                                + AudioSystem.streamToString(avgStreamType) + "("
+                                + avgStreamType + ") are mismatching",
+                                audioVolumeGroup.getId(), groupId);
+                        isVolumeGroupAssociatedToStrategy = true;
+                        break;
+                    }
+                }
+                assertTrue("Volume Group (" + audioVolumeGroup.toString()
+                        + ") has no associated strategy for stream "
+                        + AudioSystem.streamToString(avgStreamType) + "(" + avgStreamType + ")",
+                        isVolumeGroupAssociatedToStrategy);
+            }
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // Test Volume per Attributes setter/getters
+    //-----------------------------------------------------------------
+    public void testSetGetVolumePerAttributesWithInvalidAttributes() throws Exception {
+        AudioAttributes nullAttributes = null;
+
+        assertThrows(NullPointerException.class,
+                () -> mAudioManager.getMaxVolumeIndexForAttributes(nullAttributes));
+
+        assertThrows(NullPointerException.class,
+                () -> mAudioManager.getMinVolumeIndexForAttributes(nullAttributes));
+
+        assertThrows(NullPointerException.class,
+                () -> mAudioManager.getVolumeIndexForAttributes(nullAttributes));
+
+        assertThrows(NullPointerException.class,
+                () -> mAudioManager.setVolumeIndexForAttributes(
+                        nullAttributes, 0 /*index*/, 0/*flags*/));
+    }
+
+    public void testSetGetVolumePerAttributes() throws Exception {
+        for (int usage : AudioAttributes.SDK_USAGES) {
+            if (usage == AudioAttributes.USAGE_UNKNOWN) {
+                continue;
+            }
+            AudioAttributes aaForUsage = new AudioAttributes.Builder().setUsage(usage).build();
+            int indexMin = 0;
+            int indexMax = 0;
+            int index = 0;
+            Exception ex = null;
+            try {
+                indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aaForUsage);
+            } catch (Exception e) {
+                ex = e; // unexpected
+            }
+            assertNull("Exception was thrown for valid attributes", ex);
+            ex = null;
+            try {
+                indexMin = mAudioManager.getMinVolumeIndexForAttributes(aaForUsage);
+            } catch (Exception e) {
+                ex = e; // unexpected
+            }
+            assertNull("Exception was thrown for valid attributes", ex);
+            ex = null;
+            try {
+                index = mAudioManager.getVolumeIndexForAttributes(aaForUsage);
+            } catch (Exception e) {
+                ex = e; // unexpected
+            }
+            assertNull("Exception was thrown for valid attributes", ex);
+            ex = null;
+            try {
+                mAudioManager.setVolumeIndexForAttributes(aaForUsage, indexMin, 0/*flags*/);
+            } catch (Exception e) {
+                ex = e; // unexpected
+            }
+            assertNull("Exception was thrown for valid attributes", ex);
+
+            index = mAudioManager.getVolumeIndexForAttributes(aaForUsage);
+            assertEquals(index, indexMin);
+
+            mAudioManager.setVolumeIndexForAttributes(aaForUsage, indexMax, 0/*flags*/);
+            index = mAudioManager.getVolumeIndexForAttributes(aaForUsage);
+            assertEquals(index, indexMax);
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // Test register/unregister VolumeGroupCallback
+    //-----------------------------------------------------------------
+    public void testVolumeGroupCallback() throws Exception {
+        List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+        assertTrue(audioVolumeGroups.size() > 0);
+
+        AudioVolumeGroupCallbackHelper vgCbReceiver = new AudioVolumeGroupCallbackHelper();
+        mAudioManager.registerVolumeGroupCallback(mContext.getMainExecutor(), vgCbReceiver);
+
+        final List<Integer> publicStreams = Ints.asList(PUBLIC_STREAM_TYPES);
+        try {
+            // Validate Audio Volume Groups callback reception
+            for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
+                int volumeGroupId = audioVolumeGroup.getId();
+
+                // Set the receiver to filter only the current group callback
+                vgCbReceiver.setExpectedVolumeGroup(volumeGroupId);
+
+                List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
+                int[] avgStreamTypes = audioVolumeGroup.getLegacyStreamTypes();
+
+                int index = 0;
+                int indexMax = 0;
+                int indexMin = 0;
+
+                // Set the volume per attributes (if valid) and wait the callback
+                for (final AudioAttributes aa : avgAttributes) {
+                    if (aa.equals(sDefaultAttributes)) {
+                        // Some volume groups may not have valid attributes, used for internal
+                        // volume management like patch/rerouting
+                        // so bailing out strategy retrieval from attributes
+                        continue;
+                    }
+                    index = mAudioManager.getVolumeIndexForAttributes(aa);
+                    indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
+                    indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
+                    index = incrementVolumeIndex(index, indexMin, indexMax);
+
+                    vgCbReceiver.setExpectedVolumeGroup(volumeGroupId);
+                    mAudioManager.setVolumeIndexForAttributes(aa, index, 0/*flags*/);
+                    assertTrue(vgCbReceiver.waitForExpectedVolumeGroupChanged(
+                            AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
+
+                    int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
+                    assertEquals(readIndex, index);
+                }
+                // Set the volume per stream type (if valid) and wait the callback
+                for (final int avgStreamType : avgStreamTypes) {
+                    if (avgStreamType == AudioSystem.STREAM_DEFAULT) {
+                        // Some Volume Groups may not have corresponding stream types as they
+                        // intends to address volume setting per attributes to avoid adding new
+                        // stream type and going on deprecating the stream type even for volume
+                        // so bailing out strategy retrieval from stream type
+                        continue;
+                    }
+                    if (!publicStreams.contains(avgStreamType)
+                            || avgStreamType == AudioManager.STREAM_ACCESSIBILITY) {
+                        // Limit scope of test to public stream that do not require any
+                        // permission (e.g. Changing ACCESSIBILITY is subject to permission).
+                        continue;
+                    }
+                    index = mAudioManager.getStreamVolume(avgStreamType);
+                    indexMax = mAudioManager.getStreamMaxVolume(avgStreamType);
+                    indexMin = mAudioManager.getStreamMinVolumeInt(avgStreamType);
+                    index = incrementVolumeIndex(index, indexMin, indexMax);
+
+                    vgCbReceiver.setExpectedVolumeGroup(volumeGroupId);
+                    mAudioManager.setStreamVolume(avgStreamType, index, 0/*flags*/);
+                    assertTrue(vgCbReceiver.waitForExpectedVolumeGroupChanged(
+                            AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
+
+                    int readIndex = mAudioManager.getStreamVolume(avgStreamType);
+                    assertEquals(index, readIndex);
+                }
+            }
+        } finally {
+            mAudioManager.unregisterVolumeGroupCallback(vgCbReceiver);
+        }
+    }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioPolicyTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioPolicyTest.java
new file mode 100644
index 0000000..e0c7b22
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioPolicyTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.audiopolicytest;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class AudioPolicyTest extends Activity  {
+
+    public AudioPolicyTest() {
+    }
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.audiopolicytest);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
new file mode 100644
index 0000000..c0f596b
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.audiopolicytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.media.AudioAttributes;
+import android.media.AudioSystem;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.util.Log;
+
+import java.util.List;
+
+public class AudioProductStrategyTest extends AudioVolumesTestBase {
+    private static final String TAG = "AudioProductStrategyTest";
+
+    //-----------------------------------------------------------------
+    // Test getAudioProductStrategies and validate strategies
+    //-----------------------------------------------------------------
+    public void testGetProductStrategies() throws Exception {
+        List<AudioProductStrategy> audioProductStrategies =
+                AudioProductStrategy.getAudioProductStrategies();
+
+        assertNotNull(audioProductStrategies);
+        assertTrue(audioProductStrategies.size() > 0);
+
+        for (final AudioProductStrategy aps : audioProductStrategies) {
+            assertTrue(aps.getId() >= 0);
+
+            AudioAttributes aa = aps.getAudioAttributes();
+            assertNotNull(aa);
+
+            // Ensure API consistency
+            assertTrue(aps.supportsAudioAttributes(aa));
+
+            int streamType = aps.getLegacyStreamTypeForAudioAttributes(aa);
+            if (streamType == AudioSystem.STREAM_DEFAULT) {
+                // bailing out test for volume group APIs consistency
+                continue;
+            }
+            final int volumeGroupFromStream = aps.getVolumeGroupIdForLegacyStreamType(streamType);
+            final int volumeGroupFromAttributes = aps.getVolumeGroupIdForAudioAttributes(aa);
+            assertNotEquals(volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+            assertEquals(volumeGroupFromStream, volumeGroupFromAttributes);
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // Test stream to/from attributes conversion
+    //-----------------------------------------------------------------
+    public void testAudioAttributesFromStreamTypes() throws Exception {
+        List<AudioProductStrategy> audioProductStrategies =
+                AudioProductStrategy.getAudioProductStrategies();
+
+        assertNotNull(audioProductStrategies);
+        assertTrue(audioProductStrategies.size() > 0);
+
+        for (final int streamType : PUBLIC_STREAM_TYPES) {
+            AudioAttributes aaFromStreamType =
+                    AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
+                            streamType);
+
+            // No strategy found for this stream type or no attributes defined for the strategy
+            // hosting this stream type; Bailing out the test, just ensure that any request
+            // for reciproque API with the unknown attributes would return default stream
+            // for volume control, aka STREAM_MUSIC.
+            if (aaFromStreamType.equals(sInvalidAttributes)) {
+                assertEquals(AudioSystem.STREAM_MUSIC,
+                        AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
+                            aaFromStreamType));
+            } else {
+                // Attributes are valid, i.e. a strategy was found supporting this stream type
+                // with valid attributes. Ensure reciproque works fine
+                int streamTypeFromAttributes =
+                        AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
+                                aaFromStreamType);
+                assertEquals("stream " + AudioSystem.streamToString(streamType) + "("
+                        + streamType + ") expected to match attributes "
+                        + aaFromStreamType.toString() + " got instead stream "
+                        + AudioSystem.streamToString(streamTypeFromAttributes) + "("
+                        + streamTypeFromAttributes + ") expected to match attributes ",
+                        streamType, streamTypeFromAttributes);
+            }
+
+            // Now identify the strategy supporting this stream type, ensure uniqueness
+            boolean strategyFound = false;
+            for (final AudioProductStrategy aps : audioProductStrategies) {
+                AudioAttributes aaFromAps =
+                        aps.getAudioAttributesForLegacyStreamType(streamType);
+
+                if (aaFromAps == null) {
+                    // not this one...
+                    continue;
+                }
+                // Got it!
+                assertFalse("Unique ProductStrategy shall match for a given stream type",
+                        strategyFound);
+                strategyFound = true;
+
+                // Ensure getters aligned
+                assertEquals(aaFromStreamType, aaFromAps);
+                assertTrue(aps.supportsAudioAttributes(aaFromStreamType));
+
+                // Ensure reciproque works fine
+                assertEquals(streamType,
+                        aps.getLegacyStreamTypeForAudioAttributes(aaFromStreamType));
+
+                // Ensure consistency of volume group getter API
+                final int volumeGroupFromStream =
+                        aps.getVolumeGroupIdForLegacyStreamType(streamType);
+                final int volumeGroupFromAttributes =
+                        aps.getVolumeGroupIdForAudioAttributes(aaFromStreamType);
+                assertNotEquals(volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+                assertEquals(volumeGroupFromStream, volumeGroupFromAttributes);
+            }
+            if (!strategyFound) {
+                // No strategy found, ensure volume control is MUSIC
+                assertEquals(AudioSystem.STREAM_MUSIC,
+                        AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
+                            aaFromStreamType));
+            }
+        }
+    }
+
+    public void testAudioAttributesToStreamTypes() throws Exception {
+        List<AudioProductStrategy> audioProductStrategies =
+                AudioProductStrategy.getAudioProductStrategies();
+
+        assertNotNull(audioProductStrategies);
+        assertTrue(audioProductStrategies.size() > 0);
+
+        for (int usage : AudioAttributes.SDK_USAGES) {
+            AudioAttributes aaForUsage = new AudioAttributes.Builder().setUsage(usage).build();
+
+            int streamTypeFromUsage =
+                    AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(
+                            aaForUsage);
+
+            // Cannot be undefined, always shall fall back on a valid stream type
+            // to be able to control the volume
+            assertNotEquals(streamTypeFromUsage, AudioSystem.STREAM_DEFAULT);
+
+            Log.w(TAG, "GUSTAVE aaForUsage=" + aaForUsage.toString());
+
+            // Now identify the strategy hosting these Audio Attributes and ensure informations
+            // matches.
+            // Now identify the strategy supporting this stream type, ensure uniqueness
+            boolean strategyFound = false;
+            for (final AudioProductStrategy aps : audioProductStrategies) {
+                if (!aps.supportsAudioAttributes(aaForUsage)) {
+                    // Not this one
+                    continue;
+                }
+                // Got it!
+                String msg = "Unique ProductStrategy shall match for a given audio attributes "
+                        + aaForUsage.toString() + " already associated also matches with"
+                        + aps.toString();
+                assertFalse(msg, strategyFound);
+                strategyFound = true;
+
+                // It may not return the expected stream type if the strategy does not have
+                // associated stream type.
+                // Behavior of member function getLegacyStreamTypeForAudioAttributes is
+                // different than getLegacyStreamTypeForStrategyWithAudioAttributes since it
+                // does not fallback on MUSIC stream type for volume operation
+                int streamTypeFromAps = aps.getLegacyStreamTypeForAudioAttributes(aaForUsage);
+                if (streamTypeFromAps == AudioSystem.STREAM_DEFAULT) {
+                    // No stream type assigned to this strategy
+                    // Expect static API to return default stream type for volume (aka MUSIC)
+                    assertEquals("Strategy (" + aps.toString() + ") has no associated stream "
+                            + ", must fallback on MUSIC stream as default",
+                            streamTypeFromUsage, AudioSystem.STREAM_MUSIC);
+                } else {
+                    assertEquals("Attributes " + aaForUsage.toString() + " associated to stream "
+                            + AudioSystem.streamToString(streamTypeFromUsage)
+                            + " are supported by strategy (" + aps.toString() + ") which reports "
+                            + " these attributes are associated to stream "
+                            + AudioSystem.streamToString(streamTypeFromAps),
+                            streamTypeFromUsage, streamTypeFromAps);
+
+                    // Ensure consistency of volume group getter API
+                    int volumeGroupFromStream =
+                            aps.getVolumeGroupIdForLegacyStreamType(streamTypeFromAps);
+                    int volumeGroupFromAttributes =
+                            aps.getVolumeGroupIdForAudioAttributes(aaForUsage);
+                    assertNotEquals(
+                            volumeGroupFromStream, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+                    assertEquals(volumeGroupFromStream, volumeGroupFromAttributes);
+                }
+            }
+            if (!strategyFound) {
+                // No strategy found for the given attributes, the expected stream must be MUSIC
+                assertEquals(streamTypeFromUsage, AudioSystem.STREAM_MUSIC);
+            }
+        }
+    }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java
new file mode 100644
index 0000000..0c1d52c
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupCallbackHelper.java
@@ -0,0 +1,69 @@
+/*
+ * 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.audiopolicytest;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
+final class AudioVolumeGroupCallbackHelper extends AudioManager.VolumeGroupCallback {
+    private static final String TAG = "AudioVolumeGroupCallbackHelper";
+    public static final long ASYNC_TIMEOUT_MS = 800;
+
+    private int mExpectedVolumeGroupId;
+
+    private CountDownLatch mVolumeGroupChanged = null;
+
+    void setExpectedVolumeGroup(int group) {
+        mVolumeGroupChanged = new CountDownLatch(1);
+        mExpectedVolumeGroupId = group;
+    }
+
+    @Override
+    public void onAudioVolumeGroupChanged(int group, int flags) {
+        if (group != mExpectedVolumeGroupId) {
+            return;
+        }
+        if (mVolumeGroupChanged == null) {
+            Log.wtf(TAG, "Received callback but object not initialized");
+            return;
+        }
+        if (mVolumeGroupChanged.getCount() <= 0) {
+            Log.i(TAG, "callback for group: " + group + " already received");
+            return;
+        }
+        mVolumeGroupChanged.countDown();
+    }
+
+    public boolean waitForExpectedVolumeGroupChanged(long timeOutMs) {
+        assertNotNull("Call first setExpectedVolumeGroup before waiting...", mVolumeGroupChanged);
+        boolean timeoutReached = false;
+        if (mVolumeGroupChanged.getCount() == 0) {
+            // done already...
+            return true;
+        }
+        try {
+            timeoutReached = !mVolumeGroupChanged.await(ASYNC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) { }
+        return !timeoutReached;
+    }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java
new file mode 100644
index 0000000..221f1f7
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.audiopolicytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.testng.Assert.assertThrows;
+
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.media.audiopolicy.AudioVolumeGroupChangeHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AudioVolumeGroupChangeHandlerTest extends AudioVolumesTestBase {
+    private static final String TAG = "AudioVolumeGroupChangeHandlerTest";
+
+    public void testRegisterInvalidCallback() throws Exception {
+        final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+                new AudioVolumeGroupChangeHandler();
+
+        audioAudioVolumeGroupChangedHandler.init();
+
+        assertThrows(NullPointerException.class, () -> {
+            AudioManager.VolumeGroupCallback nullCb = null;
+            audioAudioVolumeGroupChangedHandler.registerListener(nullCb);
+        });
+    }
+
+    public void testUnregisterInvalidCallback() throws Exception {
+        final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+                new AudioVolumeGroupChangeHandler();
+
+        audioAudioVolumeGroupChangedHandler.init();
+
+        final AudioVolumeGroupCallbackHelper cb = new AudioVolumeGroupCallbackHelper();
+        audioAudioVolumeGroupChangedHandler.registerListener(cb);
+
+        assertThrows(NullPointerException.class, () -> {
+            AudioManager.VolumeGroupCallback nullCb = null;
+            audioAudioVolumeGroupChangedHandler.unregisterListener(nullCb);
+        });
+        audioAudioVolumeGroupChangedHandler.unregisterListener(cb);
+    }
+
+    public void testRegisterUnregisterCallback() throws Exception {
+        final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+                new AudioVolumeGroupChangeHandler();
+
+        audioAudioVolumeGroupChangedHandler.init();
+        final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper();
+
+        // Should not assert, otherwise test will fail
+        audioAudioVolumeGroupChangedHandler.registerListener(validCb);
+
+        // Should not assert, otherwise test will fail
+        audioAudioVolumeGroupChangedHandler.unregisterListener(validCb);
+    }
+
+    public void testCallbackReceived() throws Exception {
+        final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+                new AudioVolumeGroupChangeHandler();
+
+        audioAudioVolumeGroupChangedHandler.init();
+
+        final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper();
+        audioAudioVolumeGroupChangedHandler.registerListener(validCb);
+
+        List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+        assertTrue(audioVolumeGroups.size() > 0);
+
+        try {
+            for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
+                int volumeGroupId = audioVolumeGroup.getId();
+
+                List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
+                // Set the volume per attributes (if valid) and wait the callback
+                if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(sDefaultAttributes)) {
+                    // Some volume groups may not have valid attributes, used for internal
+                    // volume management like patch/rerouting
+                    // so bailing out strategy retrieval from attributes
+                    continue;
+                }
+                final AudioAttributes aa = avgAttributes.get(0);
+
+                int index = mAudioManager.getVolumeIndexForAttributes(aa);
+                int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
+                int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
+
+                final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax);
+
+                // Set the receiver to filter only the current group callback
+                validCb.setExpectedVolumeGroup(volumeGroupId);
+                mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/);
+                assertTrue(validCb.waitForExpectedVolumeGroupChanged(
+                        AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
+
+                final int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
+                assertEquals(readIndex, indexForAa);
+            }
+        } finally {
+            audioAudioVolumeGroupChangedHandler.unregisterListener(validCb);
+        }
+    }
+
+    public void testMultipleCallbackReceived() throws Exception {
+
+        final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
+                new AudioVolumeGroupChangeHandler();
+
+        audioAudioVolumeGroupChangedHandler.init();
+
+        final int callbackCount = 10;
+        final List<AudioVolumeGroupCallbackHelper> validCbs =
+                new ArrayList<AudioVolumeGroupCallbackHelper>();
+        for (int i = 0; i < callbackCount; i++) {
+            validCbs.add(new AudioVolumeGroupCallbackHelper());
+        }
+        for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
+            audioAudioVolumeGroupChangedHandler.registerListener(cb);
+        }
+
+        List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+        assertTrue(audioVolumeGroups.size() > 0);
+
+        try {
+            for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
+                int volumeGroupId = audioVolumeGroup.getId();
+
+                List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
+                // Set the volume per attributes (if valid) and wait the callback
+                if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(sDefaultAttributes)) {
+                    // Some volume groups may not have valid attributes, used for internal
+                    // volume management like patch/rerouting
+                    // so bailing out strategy retrieval from attributes
+                    continue;
+                }
+                AudioAttributes aa = avgAttributes.get(0);
+
+                int index = mAudioManager.getVolumeIndexForAttributes(aa);
+                int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
+                int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
+
+                final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax);
+
+                // Set the receiver to filter only the current group callback
+                for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
+                    cb.setExpectedVolumeGroup(volumeGroupId);
+                }
+                mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/);
+
+                for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
+                    assertTrue(cb.waitForExpectedVolumeGroupChanged(
+                            AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
+                }
+                int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
+                assertEquals(readIndex, indexForAa);
+            }
+        } finally {
+            for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
+                audioAudioVolumeGroupChangedHandler.unregisterListener(cb);
+            }
+        }
+    }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java
new file mode 100644
index 0000000..84b24b8
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.audiopolicytest;
+
+import static org.junit.Assert.assertNotEquals;
+
+import android.media.AudioAttributes;
+import android.media.AudioSystem;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.media.audiopolicy.AudioVolumeGroup;
+
+import java.util.List;
+
+public class AudioVolumeGroupTest extends AudioVolumesTestBase {
+    private static final String TAG = "AudioVolumeGroupTest";
+
+    //-----------------------------------------------------------------
+    // Test getAudioVolumeGroups and validate groud id
+    //-----------------------------------------------------------------
+    public void testGetVolumeGroupsFromNonServiceCaller() throws Exception {
+        // The transaction behind getAudioVolumeGroups will fail. Check is done at binder level
+        // with policy service. Error is not reported, the list is just empty.
+        // Request must come from service components
+        List<AudioVolumeGroup> audioVolumeGroup = AudioVolumeGroup.getAudioVolumeGroups();
+
+        assertNotNull(audioVolumeGroup);
+        assertEquals(audioVolumeGroup.size(), 0);
+    }
+
+    //-----------------------------------------------------------------
+    // Test getAudioVolumeGroups and validate groud id
+    //-----------------------------------------------------------------
+    public void testGetVolumeGroups() throws Exception {
+        // Through AudioManager, the transaction behind getAudioVolumeGroups will succeed
+        final List<AudioVolumeGroup> audioVolumeGroup = mAudioManager.getAudioVolumeGroups();
+        assertNotNull(audioVolumeGroup);
+        assertTrue(audioVolumeGroup.size() > 0);
+
+        final List<AudioProductStrategy> audioProductStrategies =
+                mAudioManager.getAudioProductStrategies();
+        assertTrue(audioProductStrategies.size() > 0);
+
+        for (final AudioVolumeGroup avg : audioVolumeGroup) {
+            int avgId = avg.getId();
+            assertNotEquals(avgId, AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
+
+            List<AudioAttributes> avgAttributes = avg.getAudioAttributes();
+            assertNotNull(avgAttributes);
+
+            final int[] avgStreamTypes = avg.getLegacyStreamTypes();
+            assertNotNull(avgStreamTypes);
+
+            // for each volume group attributes, find the matching product strategy and ensure
+            // it is linked the considered volume group
+            for (final AudioAttributes aa : avgAttributes) {
+                if (aa.equals(sDefaultAttributes)) {
+                    // Some volume groups may not have valid attributes, used for internal
+                    // volume management like patch/rerouting
+                    // so bailing out strategy retrieval from attributes
+                    continue;
+                }
+                boolean isVolumeGroupAssociatedToStrategy = false;
+                for (final AudioProductStrategy aps : audioProductStrategies) {
+                    int groupId = aps.getVolumeGroupIdForAudioAttributes(aa);
+                    if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+                        // Note that Audio Product Strategies are priority ordered, and the
+                        // the first one matching the AudioAttributes will be used to identify
+                        // the volume group associated to the request.
+                        assertTrue(aps.supportsAudioAttributes(aa));
+                        assertEquals("Volume Group ID (" + avg.toString()
+                                + "), and Volume group ID associated to Strategy ("
+                                + aps.toString() + ") both supporting attributes "
+                                + aa.toString() + " are mismatching",
+                                avgId, groupId);
+                        isVolumeGroupAssociatedToStrategy = true;
+                        break;
+                    }
+                }
+                assertTrue("Volume Group (" + avg.toString()
+                        + ") has no associated strategy for attributes " + aa.toString(),
+                        isVolumeGroupAssociatedToStrategy);
+            }
+
+            // for each volume group stream type, find the matching product strategy and ensure
+            // it is linked the considered volume group
+            for (final int avgStreamType : avgStreamTypes) {
+                if (avgStreamType == AudioSystem.STREAM_DEFAULT) {
+                    // Some Volume Groups may not have corresponding stream types as they
+                    // intends to address volume setting per attributes to avoid adding new
+                    // stream type and going on deprecating the stream type even for volume
+                    // so bailing out strategy retrieval from stream type
+                    continue;
+                }
+                boolean isVolumeGroupAssociatedToStrategy = false;
+                for (final AudioProductStrategy aps : audioProductStrategies) {
+                    int groupId = aps.getVolumeGroupIdForLegacyStreamType(avgStreamType);
+                    if (groupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
+
+                        assertEquals("Volume Group ID (" + avg.toString()
+                                + "), and Volume group ID associated to Strategy ("
+                                + aps.toString() + ") both supporting stream "
+                                + AudioSystem.streamToString(avgStreamType) + "("
+                                + avgStreamType + ") are mismatching",
+                                avgId, groupId);
+
+                        isVolumeGroupAssociatedToStrategy = true;
+                        break;
+                    }
+                }
+                assertTrue("Volume Group (" + avg.toString()
+                        + ") has no associated strategy for stream "
+                        + AudioSystem.streamToString(avgStreamType) + "(" + avgStreamType + ")",
+                        isVolumeGroupAssociatedToStrategy);
+            }
+        }
+    }
+}
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
new file mode 100644
index 0000000..a17d65c
--- /dev/null
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
@@ -0,0 +1,146 @@
+/*
+ * 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.audiopolicytest;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<AudioPolicyTest> {
+    public AudioManager mAudioManager;
+    Context mContext;
+    private Map<Integer, Integer> mOriginalStreamVolumes = new HashMap<>();
+    private Map<Integer, Integer> mOriginalVolumeGroupVolumes = new HashMap<>();
+
+    // Default matches the invalid (empty) attributes from native.
+    // The difference is the input source default which is not aligned between native and java
+    public static final AudioAttributes sDefaultAttributes =
+            AudioProductStrategy.sDefaultAttributes;
+
+    public static final AudioAttributes sInvalidAttributes = new AudioAttributes.Builder().build();
+
+    public final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
+            AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
+            AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
+            AudioManager.STREAM_DTMF,  AudioManager.STREAM_ACCESSIBILITY };
+
+    public AudioVolumesTestBase() {
+        super("com.android.audiopolicytest", AudioPolicyTest.class);
+    }
+
+    /**
+     * <p>Note: must be called with shell permission (MODIFY_AUDIO_ROUTING)
+     */
+    private void storeAllVolumes() {
+        List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+        for (final AudioVolumeGroup avg : audioVolumeGroups) {
+            if (avg.getAudioAttributes().isEmpty()) {
+                // some volume group may not supports volume control per attributes
+                // like rerouting/patch since these groups are internal to audio policy manager
+                continue;
+            }
+            AudioAttributes avgAttributes = sDefaultAttributes;
+            for (final AudioAttributes aa : avg.getAudioAttributes()) {
+                if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+                    avgAttributes = aa;
+                    break;
+                }
+            }
+            if (avgAttributes.equals(sDefaultAttributes)) {
+                // This shall not happen, however, not purpose of this base class.
+                // so bailing out.
+                continue;
+            }
+            mOriginalVolumeGroupVolumes.put(
+                    avg.getId(), mAudioManager.getVolumeIndexForAttributes(avgAttributes));
+        }
+    }
+
+    /**
+     * <p>Note: must be called with shell permission (MODIFY_AUDIO_ROUTING)
+     */
+    private void restoreAllVolumes() {
+        List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
+        for (Map.Entry<Integer, Integer> e : mOriginalVolumeGroupVolumes.entrySet()) {
+            for (final AudioVolumeGroup avg : audioVolumeGroups) {
+                if (avg.getId() == e.getKey()) {
+                    assertTrue(!avg.getAudioAttributes().isEmpty());
+                    AudioAttributes avgAttributes = sDefaultAttributes;
+                    for (final AudioAttributes aa : avg.getAudioAttributes()) {
+                        if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+                            avgAttributes = aa;
+                            break;
+                        }
+                    }
+                    assertTrue(!avgAttributes.equals(sDefaultAttributes));
+                    mAudioManager.setVolumeIndexForAttributes(
+                            avgAttributes, e.getValue(), AudioManager.FLAG_ALLOW_RINGER_MODES);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mContext = getActivity();
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+
+        assertEquals(PackageManager.PERMISSION_GRANTED,
+                mContext.checkSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+
+        // Store the original volumes that that they can be recovered in tearDown().
+        mOriginalStreamVolumes.clear();
+        for (int streamType : PUBLIC_STREAM_TYPES) {
+            mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType));
+        }
+        // Store the original volume per attributes so that they can be recovered in tearDown()
+        mOriginalVolumeGroupVolumes.clear();
+        storeAllVolumes();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        // Recover the volume and the ringer mode that the test may have overwritten.
+        for (Map.Entry<Integer, Integer> e : mOriginalStreamVolumes.entrySet()) {
+            mAudioManager.setStreamVolume(e.getKey(), e.getValue(),
+                                          AudioManager.FLAG_ALLOW_RINGER_MODES);
+        }
+
+        // Recover the original volume per attributes
+        restoreAllVolumes();
+    }
+
+    public static int resetVolumeIndex(int indexMin, int indexMax) {
+        return (indexMax + indexMin) / 2;
+    }
+
+    public static int incrementVolumeIndex(int index, int indexMin, int indexMax) {
+        return (index + 1 > indexMax) ? resetVolumeIndex(indexMin, indexMax) : ++index;
+    }
+}
diff --git a/media/tests/EffectsTest/res/layout/visualizertest.xml b/media/tests/EffectsTest/res/layout/visualizertest.xml
index 50ac7bb..18d7a36 100644
--- a/media/tests/EffectsTest/res/layout/visualizertest.xml
+++ b/media/tests/EffectsTest/res/layout/visualizertest.xml
@@ -56,6 +56,37 @@
          android:layout_height="wrap_content"
          android:scaleType="fitXY"/>
 
+    <LinearLayout android:id="@+id/visuMultithreadedLayout"
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="10dip"
+        android:layout_marginTop="10dip"
+        android:layout_marginRight="10dip"
+        android:layout_marginBottom="10dip" >
+
+        <TextView android:id="@+id/visuMultithreaded"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_weight="1.0"
+            android:layout_gravity="center_vertical|left"
+            android:text="@string/effect_multithreaded"
+            style="@android:style/TextAppearance.Medium" />
+
+        <ToggleButton android:id="@+id/visuMultithreadedOnOff"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:layout_gravity="center_vertical|right"
+            android:layout_weight="0.0" />
+
+    </LinearLayout>
+
+    <ImageView
+         android:src="@android:drawable/divider_horizontal_dark"
+         android:layout_width="fill_parent"
+         android:layout_height="wrap_content"
+         android:scaleType="fitXY"/>
+
     <LinearLayout android:id="@+id/visuControlLayout"
         android:orientation="horizontal"
         android:layout_width="fill_parent"
diff --git a/media/tests/EffectsTest/res/values/strings.xml b/media/tests/EffectsTest/res/values/strings.xml
index 2a85184..7c12da1 100644
--- a/media/tests/EffectsTest/res/values/strings.xml
+++ b/media/tests/EffectsTest/res/values/strings.xml
@@ -35,4 +35,6 @@
     <string name="effect_attach_off">Attach</string>
     <string name="effect_attach_on">Detach</string>
     <string name="send_level_name">Send Level</string>
+    <!-- Toggles use of a multi-threaded client for an effect [CHAR LIMIT=24] -->
+    <string name="effect_multithreaded">Multithreaded Use</string>
 </resources>
diff --git a/telephony/java/android/telephony/DisplayInfo.aidl b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstance.java
similarity index 70%
copy from telephony/java/android/telephony/DisplayInfo.aidl
copy to media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstance.java
index 861b0fe..817bd3d 100644
--- a/telephony/java/android/telephony/DisplayInfo.aidl
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstance.java
@@ -13,6 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.telephony;
 
-parcelable DisplayInfo;
+package com.android.effectstest;
+
+interface VisualizerInstance {
+    void enableDataCaptureListener(boolean enable);
+    boolean getEnabled();
+    void release();
+    void setEnabled(boolean enabled);
+    void startStopCapture(boolean start);
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceMT.java b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceMT.java
new file mode 100644
index 0000000..89cfbeb
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceMT.java
@@ -0,0 +1,113 @@
+/*
+ * 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.effectstest;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+class VisualizerInstanceMT implements VisualizerInstance {
+
+    private static final String TAG = "VisualizerInstanceMT";
+
+    private final Object mLock = new Object();
+    private final int mThreadCount;
+    @GuardedBy("mLock")
+    private Handler mVisualizerHandler;
+    @GuardedBy("mLock")
+    private VisualizerInstanceSync mVisualizer;
+
+    VisualizerInstanceMT(int session, Handler uiHandler, int extraThreadCount) {
+        Log.d(TAG, "Multi-threaded constructor");
+        mThreadCount = 1 + extraThreadCount;
+        Thread t = new Thread() {
+            @Override public void run() {
+                Looper.prepare();
+                VisualizerInstanceSync v = new VisualizerInstanceSync(session, uiHandler);
+                synchronized (mLock) {
+                    mVisualizerHandler = new Handler();
+                    mVisualizer = v;
+                }
+                Looper.loop();
+            }
+        };
+        t.start();
+    }
+
+    private VisualizerInstance getVisualizer() {
+        synchronized (mLock) {
+            return mVisualizer != null ? new VisualizerInstanceSync(mVisualizer) : null;
+        }
+    }
+
+    private interface VisualizerOperation {
+        void run(VisualizerInstance v);
+    }
+
+    private void runOperationMt(VisualizerOperation op) {
+        final VisualizerInstance v = getVisualizer();
+        if (v == null) return;
+        for (int i = 0; i < mThreadCount; ++i) {
+            Thread t = new Thread() {
+                @Override
+                public void run() {
+                    op.run(v);
+                }
+            };
+            t.start();
+        }
+    }
+
+    @Override
+    public void enableDataCaptureListener(boolean enable) {
+        runOperationMt(v -> v.enableDataCaptureListener(enable));
+    }
+
+    @Override
+    public boolean getEnabled() {
+        final VisualizerInstance v = getVisualizer();
+        return v != null ? v.getEnabled() : false;
+    }
+
+    @Override
+    public void release() {
+        runOperationMt(v -> v.release());
+        synchronized (mLock) {
+            if (mVisualizerHandler == null) return;
+            mVisualizerHandler.post(() -> {
+                synchronized (mLock) {
+                    mVisualizerHandler = null;
+                    mVisualizer = null;
+                    Looper.myLooper().quitSafely();
+                }
+                Log.d(TAG, "Exiting looper");
+            });
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        runOperationMt(v -> v.setEnabled(enabled));
+    }
+
+    @Override
+    public void startStopCapture(boolean start) {
+        runOperationMt(v -> v.startStopCapture(start));
+    }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceSync.java b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceSync.java
new file mode 100644
index 0000000..e64f4e5
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerInstanceSync.java
@@ -0,0 +1,170 @@
+/*
+ * 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.effectstest;
+
+import android.media.audiofx.Visualizer;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+// This class only has `final' members, thus any thread-safety concerns
+// can only come from the Visualizer effect class.
+class VisualizerInstanceSync implements VisualizerInstance {
+
+    private static final String TAG = "VisualizerInstance";
+
+    private final Handler mUiHandler;
+    private final Visualizer mVisualizer;
+    private final VisualizerTestHandler mVisualizerTestHandler;
+    private final VisualizerListener mVisualizerListener;
+
+    VisualizerInstanceSync(int session, Handler uiHandler) {
+        mUiHandler = uiHandler;
+        try {
+            mVisualizer = new Visualizer(session);
+        } catch (UnsupportedOperationException e) {
+            Log.e(TAG, "Visualizer library not loaded");
+            throw new RuntimeException("Cannot initialize effect");
+        } catch (RuntimeException e) {
+            throw e;
+        }
+        mVisualizerTestHandler = new VisualizerTestHandler();
+        mVisualizerListener = new VisualizerListener();
+    }
+
+    // Not a "deep" copy, only copies the references.
+    VisualizerInstanceSync(VisualizerInstanceSync other) {
+        mUiHandler = other.mUiHandler;
+        mVisualizer = other.mVisualizer;
+        mVisualizerTestHandler = other.mVisualizerTestHandler;
+        mVisualizerListener = other.mVisualizerListener;
+    }
+
+    @Override
+    public void enableDataCaptureListener(boolean enable) {
+        mVisualizer.setDataCaptureListener(enable ? mVisualizerListener : null,
+                10000, enable, enable);
+    }
+
+    @Override
+    public boolean getEnabled() {
+        return mVisualizer.getEnabled();
+    }
+
+    @Override
+    public void release() {
+        mVisualizer.release();
+        Log.d(TAG, "Visualizer released");
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        mVisualizer.setEnabled(enabled);
+    }
+
+    @Override
+    public void startStopCapture(boolean start) {
+        mVisualizerTestHandler.sendMessage(mVisualizerTestHandler.obtainMessage(
+                        start ? MSG_START_CAPTURE : MSG_STOP_CAPTURE));
+    }
+
+    private static final int MSG_START_CAPTURE = 0;
+    private static final int MSG_STOP_CAPTURE = 1;
+    private static final int MSG_NEW_CAPTURE = 2;
+    private static final int CAPTURE_PERIOD_MS = 100;
+
+    private static int[] dataToMinMaxCenter(byte[] data, int len) {
+        int[] minMaxCenter = new int[3];
+        minMaxCenter[0] = data[0];
+        minMaxCenter[1] = data[len - 1];
+        minMaxCenter[2] = data[len / 2];
+        return minMaxCenter;
+    }
+
+    private class VisualizerTestHandler extends Handler {
+        private final int mCaptureSize;
+        private boolean mActive = false;
+
+        VisualizerTestHandler() {
+            mCaptureSize = mVisualizer.getCaptureSize();
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_START_CAPTURE:
+                    if (!mActive) {
+                        Log.d(TAG, "Start capture");
+                        mActive = true;
+                        sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE), CAPTURE_PERIOD_MS);
+                    }
+                    break;
+                case MSG_STOP_CAPTURE:
+                    if (mActive) {
+                        Log.d(TAG, "Stop capture");
+                        mActive = false;
+                    }
+                    break;
+                case MSG_NEW_CAPTURE:
+                    if (mActive) {
+                        if (mCaptureSize > 0) {
+                            byte[] data = new byte[mCaptureSize];
+                            if (mVisualizer.getWaveForm(data) == Visualizer.SUCCESS) {
+                                int len = data.length < mCaptureSize ? data.length : mCaptureSize;
+                                mUiHandler.sendMessage(
+                                        mUiHandler.obtainMessage(
+                                                VisualizerTest.MSG_DISPLAY_WAVEFORM_VAL,
+                                                dataToMinMaxCenter(data, len)));
+                            }
+                            if (mVisualizer.getFft(data) == Visualizer.SUCCESS) {
+                                int len = data.length < mCaptureSize ? data.length : mCaptureSize;
+                                mUiHandler.sendMessage(
+                                        mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_FFT_VAL,
+                                                dataToMinMaxCenter(data, len)));
+                            }
+                        }
+                        sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE), CAPTURE_PERIOD_MS);
+                    }
+                    break;
+            }
+        }
+    }
+
+    private class VisualizerListener implements Visualizer.OnDataCaptureListener {
+        @Override
+        public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform,
+                int samplingRate) {
+            if (visualizer == mVisualizer && waveform.length > 0) {
+                Log.d(TAG, "onWaveFormDataCapture(): " + waveform[0]
+                        + " smp rate: " + samplingRate / 1000);
+                mUiHandler.sendMessage(
+                        mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_WAVEFORM_VAL,
+                                dataToMinMaxCenter(waveform, waveform.length)));
+            }
+        }
+
+        @Override
+        public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
+            if (visualizer == mVisualizer && fft.length > 0) {
+                Log.d(TAG, "onFftDataCapture(): " + fft[0]);
+                mUiHandler.sendMessage(
+                        mUiHandler.obtainMessage(VisualizerTest.MSG_DISPLAY_FFT_VAL,
+                                dataToMinMaxCenter(fft, fft.length)));
+            }
+        }
+    }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
index 7db1d8d..2e141c5 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
@@ -17,51 +17,42 @@
 package com.android.effectstest;
 
 import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.media.audiofx.Visualizer;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.view.Menu;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.EditText;
 import android.widget.TextView;
 import android.widget.ToggleButton;
-import android.widget.SeekBar;
 
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
 import java.util.HashMap;
-import java.util.Map;
 
 public class VisualizerTest extends Activity implements OnCheckedChangeListener {
 
     private final static String TAG = "Visualizer Test";
 
-    private Visualizer mVisualizer;
+    private VisualizerInstance mVisualizer;
+    ToggleButton mMultithreadedButton;
     ToggleButton mOnOffButton;
     ToggleButton mReleaseButton;
+    boolean mUseMTInstance;
     boolean mEnabled;
     EditText mSessionText;
     static int sSession = 0;
-    int mCaptureSize;
     ToggleButton mCallbackButton;
     boolean mCallbackOn;
-    VisualizerListener mVisualizerListener;
-    private static HashMap<Integer, Visualizer> sInstances = new HashMap<Integer, Visualizer>(10);
-    private VisualizerTestHandler mVisualizerTestHandler = null;
+    private static HashMap<Integer, VisualizerInstance> sInstances =
+            new HashMap<Integer, VisualizerInstance>(10);
+    private Handler mUiHandler;
 
     public VisualizerTest() {
         Log.d(TAG, "contructor");
+        mUiHandler = new UiHandler(Looper.getMainLooper());
     }
 
     @Override
@@ -76,109 +67,45 @@
         mSessionText.setOnKeyListener(mSessionKeyListener);
         mSessionText.setText(Integer.toString(sSession));
 
-        mReleaseButton = (ToggleButton)findViewById(R.id.visuReleaseButton);
-        mOnOffButton = (ToggleButton)findViewById(R.id.visualizerOnOff);
-        mCallbackButton = (ToggleButton)findViewById(R.id.visuCallbackOnOff);
+        mMultithreadedButton = (ToggleButton) findViewById(R.id.visuMultithreadedOnOff);
+        mReleaseButton = (ToggleButton) findViewById(R.id.visuReleaseButton);
+        mOnOffButton = (ToggleButton) findViewById(R.id.visualizerOnOff);
+        mCallbackButton = (ToggleButton) findViewById(R.id.visuCallbackOnOff);
         mCallbackOn = false;
         mCallbackButton.setChecked(mCallbackOn);
 
-        mVisualizerTestHandler = new VisualizerTestHandler();
-        mVisualizerListener = new VisualizerListener();
-
-        getEffect(sSession);
-
-        if (mVisualizer != null) {
+        mMultithreadedButton.setOnCheckedChangeListener(this);
+        if (getEffect(sSession) != null) {
             mReleaseButton.setOnCheckedChangeListener(this);
             mOnOffButton.setOnCheckedChangeListener(this);
             mCallbackButton.setOnCheckedChangeListener(this);
         }
     }
 
-    private static final int MSG_START_CAPTURE = 0;
-    private static final int MSG_STOP_CAPTURE = 1;
-    private static final int MSG_NEW_CAPTURE = 2;
-    private static final int CAPTURE_PERIOD_MS = 100;
+    public static final int MSG_DISPLAY_WAVEFORM_VAL = 0;
+    public static final int MSG_DISPLAY_FFT_VAL = 1;
 
-    private class VisualizerTestHandler extends Handler {
-        boolean mActive = false;
+    private class UiHandler extends Handler {
+        UiHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-            case MSG_START_CAPTURE:
-                if (!mActive) {
-                    Log.d(TAG, "Start capture");
-                    mActive = true;
-                    sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE, 0, 0, null), CAPTURE_PERIOD_MS);
+                case MSG_DISPLAY_WAVEFORM_VAL:
+                case MSG_DISPLAY_FFT_VAL:
+                    int[] minMaxCenter = (int[]) msg.obj;
+                    boolean waveform = msg.what == MSG_DISPLAY_WAVEFORM_VAL;
+                    displayVal(waveform ? R.id.waveformMin : R.id.fftMin, minMaxCenter[0]);
+                    displayVal(waveform ? R.id.waveformMax : R.id.fftMax, minMaxCenter[1]);
+                    displayVal(waveform ? R.id.waveformCenter : R.id.fftCenter, minMaxCenter[2]);
+                    break;
                 }
-                break;
-            case MSG_STOP_CAPTURE:
-                if (mActive) {
-                    Log.d(TAG, "Stop capture");
-                    mActive = false;
-                }
-                break;
-            case MSG_NEW_CAPTURE:
-                if (mActive && mVisualizer != null) {
-                    if (mCaptureSize > 0) {
-                        byte[] data = new byte[mCaptureSize];
-                        if (mVisualizer.getWaveForm(data) == Visualizer.SUCCESS) {
-                            int len = data.length < mCaptureSize ? data.length : mCaptureSize;
-                            displayVal(R.id.waveformMin, data[0]);
-                            displayVal(R.id.waveformMax, data[len-1]);
-                            displayVal(R.id.waveformCenter, data[len/2]);
-                        };
-                        if (mVisualizer.getFft(data) == Visualizer.SUCCESS) {
-                            int len = data.length < mCaptureSize ? data.length : mCaptureSize;
-                            displayVal(R.id.fftMin, data[0]);
-                            displayVal(R.id.fftMax, data[len-1]);
-                            displayVal(R.id.fftCenter, data[len/2]);
-                        };
-                    }
-                    sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE, 0, 0, null), CAPTURE_PERIOD_MS);
-                }
-                break;
-            }
         }
     }
 
-    private class VisualizerListener implements Visualizer.OnDataCaptureListener {
-
-        public VisualizerListener() {
-        }
-        public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
-            if (visualizer == mVisualizer) {
-                if (waveform.length > 0) {
-                    Log.d(TAG, "onWaveFormDataCapture(): "+waveform[0]+" smp rate: "+samplingRate/1000);
-                    displayVal(R.id.waveformMin, waveform[0]);
-                    displayVal(R.id.waveformMax, waveform[waveform.length - 1]);
-                    displayVal(R.id.waveformCenter, waveform[waveform.length/2]);
-                }
-            }
-        }
-        public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
-            if (visualizer == mVisualizer) {
-                if (fft.length > 0) {
-                    Log.d(TAG, "onFftDataCapture(): "+fft[0]);
-                    displayVal(R.id.fftMin, fft[0]);
-                    displayVal(R.id.fftMax, fft[fft.length - 1]);
-                    displayVal(R.id.fftCenter, fft[fft.length/2]);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-    }
-
-    private View.OnKeyListener mSessionKeyListener
-    = new View.OnKeyListener() {
+    private View.OnKeyListener mSessionKeyListener = new View.OnKeyListener() {
         public boolean onKey(View v, int keyCode, KeyEvent event) {
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
                 switch (keyCode) {
@@ -199,29 +126,26 @@
     };
 
     // OnCheckedChangeListener
+    @Override
     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        if (buttonView.getId() == R.id.visuMultithreadedOnOff) {
+            mUseMTInstance = isChecked;
+            Log.d(TAG, "Multi-threaded client: " + (isChecked ? "enabled" : "disabled"));
+        }
         if (buttonView.getId() == R.id.visualizerOnOff) {
             if (mVisualizer != null) {
                 mEnabled = isChecked;
                 mCallbackButton.setEnabled(!mEnabled);
                 if (mCallbackOn && mEnabled) {
-                    mVisualizer.setDataCaptureListener(mVisualizerListener,
-                            10000,
-                            true,
-                            true);
+                    mVisualizer.enableDataCaptureListener(true);
                 }
                 mVisualizer.setEnabled(mEnabled);
                 if (mCallbackOn) {
                     if (!mEnabled) {
-                        mVisualizer.setDataCaptureListener(null,
-                                10000,
-                                false,
-                                false);
+                        mVisualizer.enableDataCaptureListener(false);
                     }
                 } else {
-                    int msg = isChecked ? MSG_START_CAPTURE : MSG_STOP_CAPTURE;
-                    mVisualizerTestHandler.sendMessage(
-                            mVisualizerTestHandler.obtainMessage(msg, 0, 0, null));
+                    mVisualizer.startStopCapture(isChecked);
                 }
             }
         }
@@ -248,16 +172,15 @@
     }
 
 
-    private void getEffect(int session) {
+    private VisualizerInstance getEffect(int session) {
         synchronized (sInstances) {
             if (sInstances.containsKey(session)) {
                 mVisualizer = sInstances.get(session);
             } else {
-                try{
-                    mVisualizer = new Visualizer(session);
-                } catch (UnsupportedOperationException e) {
-                    Log.e(TAG,"Visualizer library not loaded");
-                    throw (new RuntimeException("Cannot initialize effect"));
+                try {
+                    mVisualizer = mUseMTInstance
+                            ? new VisualizerInstanceMT(session, mUiHandler, 0 /*extraThreadCount*/)
+                            : new VisualizerInstanceSync(session, mUiHandler);
                 } catch (RuntimeException e) {
                     throw e;
                 }
@@ -267,8 +190,6 @@
         mReleaseButton.setEnabled(false);
         mOnOffButton.setEnabled(false);
         if (mVisualizer != null) {
-            mCaptureSize = mVisualizer.getCaptureSize();
-
             mReleaseButton.setChecked(true);
             mReleaseButton.setEnabled(true);
 
@@ -278,6 +199,7 @@
 
             mCallbackButton.setEnabled(!mEnabled);
         }
+        return mVisualizer;
     }
 
     private void putEffect(int session) {
@@ -286,9 +208,8 @@
         synchronized (sInstances) {
             if (mVisualizer != null) {
                 mVisualizer.release();
-                Log.d(TAG,"Visualizer released");
-                mVisualizer = null;
                 sInstances.remove(session);
+                mVisualizer = null;
             }
         }
     }
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index cf55eba..199f0eb 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -32,6 +32,7 @@
 /**
  * Manages MMS operations such as sending multimedia messages.
  * Get this object by calling Context#getSystemService(Context#MMS_SERVICE).
+ * @hide
  */
 @SystemService(Context.MMS_SERVICE)
 public class MmsManager {
diff --git a/native/android/Android.bp b/native/android/Android.bp
index ae8cb3a..8f36dece 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -137,4 +137,4 @@
         "aidl/com/android/internal/compat/IPlatformCompatNative.aidl",
     ],
     path: "aidl",
-}
\ No newline at end of file
+}
diff --git a/native/android/sharedmem.cpp b/native/android/sharedmem.cpp
index 4410bd6..338b280 100644
--- a/native/android/sharedmem.cpp
+++ b/native/android/sharedmem.cpp
@@ -16,6 +16,9 @@
 
 #include <jni.h>
 
+#include <fcntl.h>
+#include <unistd.h>
+
 #include <android/sharedmem.h>
 #include <android/sharedmem_jni.h>
 #include <cutils/ashmem.h>
@@ -23,7 +26,6 @@
 #include <utils/Errors.h>
 
 #include <mutex>
-#include <unistd.h>
 
 static struct {
     jclass clazz;
diff --git a/native/webview/loader/Android.bp b/native/webview/loader/Android.bp
index 0ba256f..dfa5bdd 100644
--- a/native/webview/loader/Android.bp
+++ b/native/webview/loader/Android.bp
@@ -24,6 +24,8 @@
 
     cflags: ["-Werror"],
 
+    header_libs: ["jni_headers"],
+
     shared_libs: [
         "libdl",
         "liblog",
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 5054281..6fab9e4 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -106,6 +106,7 @@
         webSettings.setSupportZoom(true);
         webSettings.setBuiltInZoomControls(true);
         webSettings.setDomStorageEnabled(true);
+        webSettings.setAllowFileAccess(false);
         mWebViewClient = new MyWebViewClient();
         mWebView.setWebViewClient(mWebViewClient);
         mWebView.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/CtsShim/Android.bp b/packages/CtsShim/Android.bp
index 7728464..3487803 100644
--- a/packages/CtsShim/Android.bp
+++ b/packages/CtsShim/Android.bp
@@ -43,6 +43,15 @@
         },
     },
     presigned: true,
+
+    apex_available: [
+        "com.android.apex.cts.shim.v1",
+        "com.android.apex.cts.shim.v2",
+        "com.android.apex.cts.shim.v2_legacy",
+        "com.android.apex.cts.shim.v2_no_hashtree",
+        "com.android.apex.cts.shim.v2_sdk_target_p",
+        "com.android.apex.cts.shim.v3",
+    ],
 }
 
 //##########################################################
@@ -71,4 +80,13 @@
         },
     },
     presigned: true,
+
+    apex_available: [
+        "com.android.apex.cts.shim.v1",
+        "com.android.apex.cts.shim.v2",
+        "com.android.apex.cts.shim.v2_legacy",
+        "com.android.apex.cts.shim.v2_no_hashtree",
+        "com.android.apex.cts.shim.v2_sdk_target_p",
+        "com.android.apex.cts.shim.v3",
+    ],
 }
diff --git a/packages/DynamicSystemInstallationService/res/values/strings.xml b/packages/DynamicSystemInstallationService/res/values/strings.xml
index e124be6..719fc73 100644
--- a/packages/DynamicSystemInstallationService/res/values/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values/strings.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <!-- application name [CHAR LIMIT=32] -->
+    <!-- application name [DO NOT TRANSLATE] -->
     <string name="app_name">Dynamic System Updates</string>
 
-    <!-- notification channel name [CHAR LIMIT=32] -->
+    <!-- notification channel name [DO NOT TRANSLATE] -->
     <string name="notification_channel_name">Dynamic System Updates</string>
 
-    <!-- password page title [CHAR LIMIT=32] -->
+    <!-- password page title [DO NOT TRANSLATE] -->
     <string name="keyguard_title">Dynamic System Updates</string>
 
     <!-- password page description [CHAR LIMIT=128] -->
@@ -23,19 +23,19 @@
     <!-- 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>
 
-    <!-- Action on notification: Cancel installation [CHAR LIMIT=16] -->
+    <!-- Action on notification: Cancel installation [CHAR LIMIT=24] -->
     <string name="notification_action_cancel">Cancel</string>
-    <!-- Action on notification: Discard installation [CHAR LIMIT=16] -->
+    <!-- Action on notification: Discard installation [CHAR LIMIT=24] -->
     <string name="notification_action_discard">Discard</string>
-    <!-- Action on notification: Restart to Dynamic System [CHAR LIMIT=16] -->
+    <!-- Action on notification: Restart to Dynamic System [CHAR LIMIT=24] -->
     <string name="notification_action_reboot_to_dynsystem">Restart</string>
-    <!-- Action on notification: Restart to original Android version [CHAR LIMIT=16] -->
+    <!-- Action on notification: Restart to original Android version [CHAR LIMIT=24] -->
     <string name="notification_action_reboot_to_origin">Restart</string>
 
 
-    <!-- Toast when installed Dynamic System is discarded [CHAR LIMIT=64] -->
+    <!-- Toast when installed Dynamic System is discarded [CHAR LIMIT=128] -->
     <string name="toast_dynsystem_discarded">Discarded dynamic system</string>
-    <!-- Toast when we fail to launch into Dynamic System [CHAR LIMIT=64] -->
+    <!-- Toast when we fail to launch into Dynamic System [CHAR LIMIT=128] -->
     <string name="toast_failed_to_reboot_to_dynsystem">Can\u2019t restart or load dynamic system</string>
 
     <!-- URL of Dynamic System Key Revocation List [DO NOT TRANSLATE] -->
diff --git a/packages/InputDevices/res/raw/keyboard_layout_belarusian.kcm b/packages/InputDevices/res/raw/keyboard_layout_belarusian.kcm
new file mode 100644
index 0000000..3deb9dd
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_belarusian.kcm
@@ -0,0 +1,343 @@
+# 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.
+
+# Belarusian keyboard layout.
+# This is a typical Belarusian PC keyboard layout.
+# As an added convenience, English characters are accessible using ralt (Alt Gr).
+#
+
+type OVERLAY
+map key 86 BACKSLASH
+### ROW 1
+key GRAVE {
+    label:                              '\u0401'
+    base:                               '\u0451'
+    shift, capslock:                    '\u0401'
+    ralt:                               '`'
+    ralt+shift:                         '~'
+}
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+    ralt:                               '!'
+}
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '"'
+    ralt:                               '@'
+}
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '\u2116'
+    ralt:                               '#'
+}
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              ';'
+    ralt:                               '$'
+}
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+    ralt:                               '%'
+}
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              ':'
+    ralt:                               '^'
+}
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '?'
+    ralt:                               '&'
+}
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '*'
+    ralt:                               '*'
+}
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              '('
+    ralt:                               '('
+}
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              ')'
+    ralt:                               ')'
+}
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+    ralt:                               '-'
+    ralt+shift:                         '_'
+}
+key EQUALS {
+    label:                              '='
+    base:                               '='
+    shift:                              '+'
+    ralt:                               '='
+    ralt+shift:                         '+'
+}
+### ROW 2
+key Q {
+    label:                              '\u0419'
+    base:                               '\u0439'
+    shift, capslock:                    '\u0419'
+    ralt:                               'q'
+    ralt+shift, ralt+capslock:          'Q'
+}
+key W {
+    label:                              '\u0426'
+    base:                               '\u0446'
+    shift, capslock:                    '\u0426'
+    ralt:                               'w'
+    ralt+shift, ralt+capslock:          'W'
+}
+key E {
+    label:                              '\u0423'
+    base:                               '\u0443'
+    shift, capslock:                    '\u0423'
+    ralt:                               'e'
+    ralt+shift, ralt+capslock:          'E'
+}
+key R {
+    label:                              '\u041a'
+    base:                               '\u043a'
+    shift, capslock:                    '\u041a'
+    ralt:                               'r'
+    ralt+shift, ralt+capslock:          'R'
+}
+key T {
+    label:                              '\u0415'
+    base:                               '\u0435'
+    shift, capslock:                    '\u0415'
+    ralt:                               't'
+    ralt+shift, ralt+capslock:          'T'
+}
+key Y {
+    label:                              '\u041d'
+    base:                               '\u043d'
+    shift, capslock:                    '\u041d'
+    ralt:                               'y'
+    ralt+shift, ralt+capslock:          'Y'
+}
+key U {
+    label:                              '\u0413'
+    base:                               '\u0433'
+    shift, capslock:                    '\u0413'
+    ralt:                               'u'
+    ralt+shift, ralt+capslock:          'U'
+}
+key I {
+    label:                              '\u0428'
+    base:                               '\u0448'
+    shift, capslock:                    '\u0428'
+    ralt:                               'i'
+    ralt+shift, ralt+capslock:          'I'
+}
+key O {
+    label:                              '\u040E'
+    base:                               '\u045E'
+    shift, capslock:                    '\u040E'
+    ralt:                               'o'
+    ralt+shift, ralt+capslock:          'O'
+}
+key P {
+    label:                              '\u0417'
+    base:                               '\u0437'
+    shift, capslock:                    '\u0417'
+    ralt:                               'p'
+    ralt+shift, ralt+capslock:          'P'
+}
+key LEFT_BRACKET {
+    label:                              '\u0425'
+    base:                               '\u0445'
+    shift, capslock:                    '\u0425'
+    ralt:                               '['
+    ralt+shift:                         '{'
+}
+key RIGHT_BRACKET {
+    label:                              '\u0027'
+    base:                               '\u0027'
+    shift, capslock:                    '\u0027'
+    ralt:                               ']'
+    ralt+shift:                         '}'
+}
+### ROW 3
+key A {
+    label:                              '\u0424'
+    base:                               '\u0444'
+    shift, capslock:                    '\u0424'
+    ralt:                               'a'
+    ralt+shift, ralt+capslock:          'A'
+}
+key S {
+    label:                              '\u042b'
+    base:                               '\u044b'
+    shift, capslock:                    '\u042b'
+    ralt:                               's'
+    ralt+shift, ralt+capslock:          'S'
+}
+key D {
+    label:                              '\u0412'
+    base:                               '\u0432'
+    shift, capslock:                    '\u0412'
+    ralt:                               'd'
+    ralt+shift, ralt+capslock:          'D'
+}
+key F {
+    label:                              '\u0410'
+    base:                               '\u0430'
+    shift, capslock:                    '\u0410'
+    ralt:                               'f'
+    ralt+shift, ralt+capslock:          'F'
+}
+key G {
+    label:                              '\u041f'
+    base:                               '\u043f'
+    shift, capslock:                    '\u041f'
+    ralt:                               'g'
+    ralt+shift, ralt+capslock:          'G'
+}
+key H {
+    label:                              '\u0420'
+    base:                               '\u0440'
+    shift, capslock:                    '\u0420'
+    ralt:                               'h'
+    ralt+shift, ralt+capslock:          'H'
+}
+key J {
+    label:                              '\u041e'
+    base:                               '\u043e'
+    shift, capslock:                    '\u041e'
+    ralt:                               'j'
+    ralt+shift, ralt+capslock:          'J'
+}
+key K {
+    label:                              '\u041b'
+    base:                               '\u043b'
+    shift, capslock:                    '\u041b'
+    ralt:                               'k'
+    ralt+shift, ralt+capslock:          'K'
+}
+key L {
+    label:                              '\u0414'
+    base:                               '\u0434'
+    shift, capslock:                    '\u0414'
+    ralt:                               'l'
+    ralt+shift, ralt+capslock:          'L'
+}
+key SEMICOLON {
+    label:                              '\u0416'
+    base:                               '\u0436'
+    shift, capslock:                    '\u0416'
+    ralt:                               ';'
+    ralt+shift:                         ':'
+}
+key APOSTROPHE {
+    label:                              '\u042d'
+    base:                               '\u044d'
+    shift, capslock:                    '\u042d'
+    ralt:                               '\''
+    ralt+shift:                         '"'
+}
+key BACKSLASH {
+    label:                              '\\'
+    base:                               '\\'
+    shift:                              '/'
+    ralt:                               '|'
+}
+### ROW 4
+key Z {
+    label:                              '\u042f'
+    base:                               '\u044f'
+    shift, capslock:                    '\u042f'
+    ralt:                               'z'
+    ralt+shift, ralt+capslock:          'Z'
+}
+key X {
+    label:                              '\u0427'
+    base:                               '\u0447'
+    shift, capslock:                    '\u0427'
+    ralt:                               'x'
+    ralt+shift, ralt+capslock:          'X'
+}
+key C {
+    label:                              '\u0421'
+    base:                               '\u0441'
+    shift, capslock:                    '\u0421'
+    ralt:                               'c'
+    ralt+shift, ralt+capslock:          'C'
+}
+key V {
+    label:                              '\u041c'
+    base:                               '\u043c'
+    shift, capslock:                    '\u041c'
+    ralt:                               'v'
+    ralt+shift, ralt+capslock:          'V'
+}
+key B {
+    label:                              '\u0406'
+    base:                               '\u0456'
+    shift, capslock:                    '\u0406'
+    ralt:                               'b'
+    ralt+shift, ralt+capslock:          'B'
+}
+key N {
+    label:                              '\u0422'
+    base:                               '\u0442'
+    shift, capslock:                    '\u0422'
+    ralt:                               'n'
+    ralt+shift, ralt+capslock:          'N'
+}
+key M {
+    label:                              '\u042c'
+    base:                               '\u044c'
+    shift, capslock:                    '\u042c'
+    ralt:                               'm'
+    ralt+shift, ralt+capslock:          'M'
+}
+key COMMA {
+    label:                              '\u0411'
+    base:                               '\u0431'
+    shift, capslock:                    '\u0411'
+    ralt:                               ','
+    ralt+shift:                         '<'
+}
+key PERIOD {
+    label:                              '\u042e'
+    base:                               '\u044e'
+    shift, capslock:                    '\u042e'
+    ralt:                               '.'
+    ralt+shift:                         '>'
+}
+key SLASH {
+    label:                              '.'
+    base:                               '.'
+    shift:                              ','
+    ralt:                               '/'
+    ralt+shift:                         '?'
+}
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 5fdc4a6..ac70c94 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -128,4 +128,7 @@
 
     <!-- Polish keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_polish">Polish</string>
+
+    <!-- Belarusian keyboard layout label. [CHAR LIMIT=35] -->
+    <string name="keyboard_layout_belarusian">Belarusian</string>
 </resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 1807aea..68ca093 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -163,4 +163,8 @@
     <keyboard-layout android:name="keyboard_layout_polish"
             android:label="@string/keyboard_layout_polish"
             android:keyboardLayout="@raw/keyboard_layout_polish" />
+
+    <keyboard-layout android:name="keyboard_layout_belarusian"
+            android:label="@string/keyboard_layout_belarusian"
+            android:keyboardLayout="@raw/keyboard_layout_belarusian" />
 </keyboard-layouts>
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index a28ba85..cb36c4c 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -4,10 +4,8 @@
 emilychuang@google.com
 evanlaird@google.com
 leifhendrik@google.com
-rafftsai@google.com
 tmfang@google.com
 virgild@google.com
-zhfan@google.com
 
 # Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
 per-file *.xml=*
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index c9c847f..3c78560 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -218,24 +218,32 @@
     }
 
     public boolean supportsHighQualityAudio(BluetoothDevice device) {
-        int support = mService.supportsOptionalCodecs(device);
+        BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+        if (bluetoothDevice == null) {
+            return false;
+        }
+        int support = mService.isOptionalCodecsSupported(bluetoothDevice);
         return support == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED;
     }
 
     public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
-        int enabled = mService.getOptionalCodecsEnabled(device);
+        BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+        if (bluetoothDevice == null) {
+            return false;
+        }
+        int enabled = mService.isOptionalCodecsEnabled(bluetoothDevice);
         if (enabled != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN) {
             return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED;
-        } else if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED &&
-                supportsHighQualityAudio(device)) {
+        } else if (getConnectionStatus(bluetoothDevice) != BluetoothProfile.STATE_CONNECTED
+                && supportsHighQualityAudio(bluetoothDevice)) {
             // Since we don't have a stored preference and the device isn't connected, just return
             // true since the default behavior when the device gets connected in the future would be
             // to have optional codecs enabled.
             return true;
         }
         BluetoothCodecConfig codecConfig = null;
-        if (mService.getCodecStatus(device) != null) {
-            codecConfig = mService.getCodecStatus(device).getCodecConfig();
+        if (mService.getCodecStatus(bluetoothDevice) != null) {
+            codecConfig = mService.getCodecStatus(bluetoothDevice).getCodecConfig();
         }
         if (codecConfig != null)  {
             return !codecConfig.isMandatoryCodec();
@@ -245,23 +253,28 @@
     }
 
     public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) {
+        BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+        if (bluetoothDevice == null) {
+            return;
+        }
         int prefValue = enabled
                 ? BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED
                 : BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED;
-        mService.setOptionalCodecsEnabled(device, prefValue);
-        if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
+        mService.setOptionalCodecsEnabled(bluetoothDevice, prefValue);
+        if (getConnectionStatus(bluetoothDevice) != BluetoothProfile.STATE_CONNECTED) {
             return;
         }
         if (enabled) {
-            mService.enableOptionalCodecs(device);
+            mService.enableOptionalCodecs(bluetoothDevice);
         } else {
-            mService.disableOptionalCodecs(device);
+            mService.disableOptionalCodecs(bluetoothDevice);
         }
     }
 
     public String getHighQualityAudioOptionLabel(BluetoothDevice device) {
+        BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
         int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec;
-        if (!supportsHighQualityAudio(device)
+        if (bluetoothDevice == null || !supportsHighQualityAudio(device)
                 || getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
             return mContext.getString(unknownCodecId);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index bb9d42e..994a9b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -611,8 +611,8 @@
          * If a connect was attempted earlier without any UUID, we will do the connect now.
          * Otherwise, allow the connect on UUID change.
          */
-        if (!mProfiles.isEmpty()
-                && ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime())) {
+        if ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) {
+            Log.d(TAG, "onUuidChanged: triggering connectAllEnabledProfiles");
             connectAllEnabledProfiles();
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index d17f242..a1fba4a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -174,7 +174,7 @@
 
     @Override
     public boolean isEnabled(BluetoothDevice device) {
-        if (mService == null) {
+        if (mService == null || device == null) {
             return false;
         }
         return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
@@ -182,7 +182,7 @@
 
     @Override
     public int getConnectionPolicy(BluetoothDevice device) {
-        if (mService == null) {
+        if (mService == null || device == null) {
             return CONNECTION_POLICY_FORBIDDEN;
         }
         return mService.getConnectionPolicy(device);
@@ -191,7 +191,7 @@
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
         boolean isEnabled = false;
-        if (mService == null) {
+        if (mService == null || device == null) {
             return false;
         }
         if (enabled) {
@@ -213,7 +213,7 @@
     }
 
     public long getHiSyncId(BluetoothDevice device) {
-        if (mService == null) {
+        if (mService == null || device == null) {
             return BluetoothHearingAid.HI_SYNC_ID_INVALID;
         }
         return mService.getHiSyncId(device);
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index d48aa24..231809b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -242,7 +242,16 @@
         final TimeZoneNames.NameType nameType =
                 tz.inDaylightTime(now) ? TimeZoneNames.NameType.LONG_DAYLIGHT
                         : TimeZoneNames.NameType.LONG_STANDARD;
-        return names.getDisplayName(tz.getID(), nameType, now.getTime());
+        return names.getDisplayName(getCanonicalZoneId(tz), nameType, now.getTime());
+    }
+
+    private static String getCanonicalZoneId(TimeZone timeZone) {
+        final String id = timeZone.getID();
+        final String canonicalId = android.icu.util.TimeZone.getCanonicalID(id);
+        if (canonicalId != null) {
+            return canonicalId;
+        }
+        return id;
     }
 
     private static void appendWithTtsSpan(SpannableStringBuilder builder, CharSequence content,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 414c39b..9afdd43c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -69,30 +69,31 @@
         mProfile = new A2dpProfile(mContext, mDeviceManager, mProfileManager);
         mServiceListener = mShadowBluetoothAdapter.getServiceListener();
         mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp);
+        when(mBluetoothA2dp.getActiveDevice()).thenReturn(mDevice);
     }
 
     @Test
     public void supportsHighQualityAudio() {
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
         assertThat(mProfile.supportsHighQualityAudio(mDevice)).isTrue();
 
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
         assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
 
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
         assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
     }
 
     @Test
     public void isHighQualityAudioEnabled() {
-        when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsEnabled(mDevice)).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
 
-        when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsEnabled(mDevice)).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
 
@@ -100,16 +101,16 @@
         // then isHighQualityAudioEnabled() should return true or false based on whether optional
         // codecs are supported. If the device is connected then we should ask it directly, but if
         // the device isn't connected then rely on the stored pref about such support.
-        when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsEnabled(mDevice)).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
         when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
                 BluetoothProfile.STATE_DISCONNECTED);
 
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
 
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(mDevice)).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
         assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
 
@@ -151,14 +152,14 @@
 
         // Most tests want to simulate optional codecs being supported by the device, so do that
         // by default here.
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
     }
 
     @Test
     public void getLableCodecsNotSupported() {
         setupLabelTest();
-        when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+        when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
                 BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
         assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL);
     }
diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS
index 2054129..b2ac4f4 100644
--- a/packages/SettingsProvider/OWNERS
+++ b/packages/SettingsProvider/OWNERS
@@ -1,3 +1,5 @@
 hackbod@google.com
+narayan@google.com
 svetoslavganov@google.com
-moltmann@google.com
+schfan@google.com
+toddke@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 6821942..64a2d20 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -41,6 +41,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
+import android.sysprop.TelephonyProperties;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -2496,9 +2497,7 @@
 
             // Data roaming default, based on build
             loadSetting(stmt, Settings.Global.DATA_ROAMING,
-                    "true".equalsIgnoreCase(
-                            SystemProperties.get("ro.com.android.dataroaming",
-                                    "false")) ? 1 : 0);
+                    TelephonyProperties.data_roaming().orElse(false) ? 1 : 0);
 
             loadBooleanSetting(stmt, Settings.Global.DEVICE_PROVISIONED,
                     R.bool.def_device_provisioned);
@@ -2519,9 +2518,7 @@
 
             // Mobile Data default, based on build
             loadSetting(stmt, Settings.Global.MOBILE_DATA,
-                    "true".equalsIgnoreCase(
-                            SystemProperties.get("ro.com.android.mobiledata",
-                                    "true")) ? 1 : 0);
+                    TelephonyProperties.mobile_data().orElse(true) ? 1 : 0);
 
             loadBooleanSetting(stmt, Settings.Global.NETSTATS_ENABLED,
                     R.bool.def_netstats_enabled);
@@ -2575,20 +2572,22 @@
 
             // Set the preferred network mode to target desired value or Default
             // value defined in system property
-            String val = "";
-            String mode;
-            for (int phoneId = 0;
-                    phoneId < getTelephonyManager().getPhoneCount(); phoneId++) {
-                mode = TelephonyManager.getTelephonyProperty(phoneId,
-                        "ro.telephony.default_network",
-                        Integer.toString(RILConstants.PREFERRED_NETWORK_MODE));
-                if (phoneId == 0) {
-                    val = mode;
-                } else {
-                    val = val + "," + mode;
-                }
+            StringBuilder val = new StringBuilder();
+            List<Integer> defaultNetworks = TelephonyProperties.default_network();
+            int phoneCount = 1;
+            TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+            if (telephonyManager != null) {
+                phoneCount = telephonyManager.getSupportedModemCount();
             }
-            loadSetting(stmt, Settings.Global.PREFERRED_NETWORK_MODE, val);
+            for (int phoneId = 0; phoneId < phoneCount; phoneId++) {
+                int mode = defaultNetworks.size() <= phoneId
+                        || defaultNetworks.get(phoneId) == null
+                        ? TelephonyManager.DEFAULT_PREFERRED_NETWORK_MODE
+                        : defaultNetworks.get(phoneId);
+                if (phoneId > 0) val.append(',');
+                val.append(mode);
+            }
+            loadSetting(stmt, Settings.Global.PREFERRED_NETWORK_MODE, val.toString());
 
             // Set the preferred cdma subscription source to target desired value or default
             // value defined in Phone
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 21129f9..65f68cc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1352,9 +1352,6 @@
                 Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
                 GlobalSettingsProto.Sys.STORAGE_CACHE_MAX_BYTES);
         dumpSetting(s, p,
-                Settings.Global.SYS_VDSO,
-                GlobalSettingsProto.Sys.VDSO);
-        dumpSetting(s, p,
                 Settings.Global.SYS_UIDCPUPOWER,
                 GlobalSettingsProto.Sys.UIDCPUPOWER);
         p.end(sysToken);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 044ebda..c1ec9b0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -192,7 +192,6 @@
 
     <!-- Permission needed to run network tests in CTS -->
     <uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" />
-    <uses-permission android:name="android.permission.NETWORK_STACK" />
     <!-- Permission needed to test tcp keepalive offload. -->
     <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
 
@@ -220,6 +219,9 @@
     <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
 
+    <!-- Permission required for CTS test - BatterySaverTest -->
+    <uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+
     <!-- Permission required for CTS test - UiModeManagerTest -->
     <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
 
@@ -242,6 +244,15 @@
     <!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
     <uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL" />
 
+    <!-- Permission needed to use wifi usability API's for CtsNetTestCases -->
+    <uses-permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE" />
+
+    <!-- Permission required for testing system audio effect APIs. -->
+    <uses-permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"/>
+
+    <!-- Permissions needed to test shared libraries -->
+    <uses-permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
@@ -288,22 +299,6 @@
                   android:excludeFromRecents="true"
                   android:exported="false" />
 
-        <!--
-        The following is used as a no-op/null home activity when
-        no other MAIN/HOME activity is present (e.g., in CSI).
-        -->
-        <activity android:name=".NullHome"
-                  android:excludeFromRecents="true"
-                  android:label=""
-                  android:screenOrientation="nosensor">
-            <!-- The priority here is set to be lower than that for Settings -->
-            <intent-filter android:priority="-1100">
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.HOME" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
         <receiver
             android:name=".BugreportReceiver"
             android:permission="android.permission.DUMP">
diff --git a/packages/Shell/src/com/android/shell/NullHome.java b/packages/Shell/src/com/android/shell/NullHome.java
deleted file mode 100644
index bd97561..0000000
--- a/packages/Shell/src/com/android/shell/NullHome.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.shell;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-/**
- * This covers the fallback case where no launcher is available.
- * Usually Settings.apk has one fallback home activity.
- * Settings.apk, however, is not part of CSI, which needs to be
- * standalone (bootable and testable).
- */
-public class NullHome extends Activity {
-    private static final String TAG = "NullHome";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Log.i(TAG, "onCreate");
-        setContentView(R.layout.null_home_finishing_boot);
-    }
-
-    protected void onDestroy() {
-        super.onDestroy();
-        Log.i(TAG, "onDestroy");
-    }
-}
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 03f1dc5..304f81b 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -4,7 +4,6 @@
 
 adamcohen@google.com
 asc@google.com
-ashaikh@google.com
 beverlyt@google.com
 brockman@google.com
 cinek@google.com
@@ -12,8 +11,8 @@
 dupin@google.com
 ethibodeau@google.com
 evanlaird@google.com
+hwwang@google.com
 hyunyoungs@google.com
-jmonk@google.com
 jaggies@google.com
 jjaggi@google.com
 joshmcgrath@google.com
@@ -29,16 +28,16 @@
 mrenouf@google.com
 nbenbernou@google.com
 nesciosquid@google.com
-ngmatthew@google.com
 ogunwale@google.com
+peanutbutter@google.com
 pixel@google.com
 roosa@google.com
-shahrk@google.com
 snoeberger@google.com
 steell@google.com
 stwu@google.com
 sunnygoyal@google.com
 susikp@google.com
+tracyzhou@google.com
 tsuji@google.com
 twickham@google.com
 winsonc@google.com
diff --git a/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml b/packages/SystemUI/res/drawable/ic_qs_nfc.xml
similarity index 95%
rename from packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml
rename to packages/SystemUI/res/drawable/ic_qs_nfc.xml
index becb18a..2c08096 100644
--- a/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_nfc.xml
@@ -14,8 +14,8 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
+    android:width="48dp"
+    android:height="48dp"
     android:viewportWidth="24"
     android:viewportHeight="24">
 
diff --git a/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml
deleted file mode 100644
index 558f3d0..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-     Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:pathData="M4 20h16V4H4v16z" />
-    <path
-        android:fillColor="#4DFFFFFF"
-        android:pathData="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1 .9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0
-18H4V4h16v16zM18 6h-5c-1.1 0-2 .9-2 2v2.28c-.6 .35 -1 .98-1 1.72 0 1.1 .9 2 2
-2s2-.9 2-2c0-.74-.4-1.38-1-1.72V8h3v8H8V8h2V6H6v12h12V6z" />
-    <path
-        android:pathData="M0 0h24v24H0z" />
-</vector>
diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml
index 24374e8..df717f6 100644
--- a/packages/SystemUI/res/layout/menu_ime.xml
+++ b/packages/SystemUI/res/layout/menu_ime.xml
@@ -17,13 +17,13 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/menu_container"
-    android:layout_width="@dimen/navigation_key_width"
+    android:layout_width="@dimen/navigation_side_padding"
     android:layout_height="match_parent"
     android:importantForAccessibility="no"
     >
     <!-- Use nav button width & height=match_parent for parent FrameLayout and buttons because they
     are placed inside a view that has a size controlled by weight. Ensure weight is large enough to
-    support icon size. -->
+    support icon size. Use layout_width=navigation_side_padding like other navbar buttons. -->
 
     <com.android.systemui.statusbar.policy.KeyButtonView
         android:id="@+id/menu"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5dfb21b..8d505e7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -29,7 +29,6 @@
 import static android.os.BatteryManager.EXTRA_PLUGGED;
 import static android.os.BatteryManager.EXTRA_STATUS;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
-import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -527,7 +526,7 @@
      */
     public List<SubscriptionInfo> getFilteredSubscriptionInfo(boolean forceReload) {
         List<SubscriptionInfo> subscriptions = getSubscriptionInfo(false);
-        if (subscriptions.size() == MODEM_COUNT_DUAL_MODEM) {
+        if (subscriptions.size() == 2) {
             SubscriptionInfo info1 = subscriptions.get(0);
             SubscriptionInfo info2 = subscriptions.get(1);
             if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 7a74dba..b2eedaf 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -195,20 +195,32 @@
             mNotedItems.remove(item);
             if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
         }
-        notifySuscribers(code, uid, packageName, false);
+        boolean active;
+        // Check if the item is also active
+        synchronized (mActiveItems) {
+            active = getAppOpItem(mActiveItems, code, uid, packageName) != null;
+        }
+        if (!active) {
+            notifySuscribers(code, uid, packageName, false);
+        }
     }
 
-    private void addNoted(int code, int uid, String packageName) {
+    private boolean addNoted(int code, int uid, String packageName) {
         AppOpItem item;
+        boolean createdNew = false;
         synchronized (mNotedItems) {
             item = getAppOpItem(mNotedItems, code, uid, packageName);
             if (item == null) {
                 item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
                 mNotedItems.add(item);
                 if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
+                createdNew = true;
             }
         }
+        // We should keep this so we make sure it cannot time out.
+        mBGHandler.removeCallbacksAndMessages(item);
         mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS);
+        return createdNew;
     }
 
     /**
@@ -255,23 +267,46 @@
 
     @Override
     public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
-        if (updateActives(code, uid, packageName, active)) {
-            notifySuscribers(code, uid, packageName, active);
+        if (DEBUG) {
+            Log.w(TAG, String.format("onActiveChanged(%d,%d,%s,%s", code, uid, packageName,
+                    Boolean.toString(active)));
+        }
+        boolean activeChanged = updateActives(code, uid, packageName, active);
+        if (!activeChanged) return; // early return
+        // Check if the item is also noted, in that case, there's no update.
+        boolean alsoNoted;
+        synchronized (mNotedItems) {
+            alsoNoted = getAppOpItem(mNotedItems, code, uid, packageName) != null;
+        }
+        // If active is true, we only send the update if the op is not actively noted (already true)
+        // If active is false, we only send the update if the op is not actively noted (prevent
+        // early removal)
+        if (!alsoNoted) {
+            mBGHandler.post(() -> notifySuscribers(code, uid, packageName, active));
         }
     }
 
     @Override
     public void onOpNoted(int code, int uid, String packageName, int result) {
         if (DEBUG) {
-            Log.w(TAG, "Op: " + code + " with result " + AppOpsManager.MODE_NAMES[result]);
+            Log.w(TAG, "Noted op: " + code + " with result "
+                    + AppOpsManager.MODE_NAMES[result] + " for package " + packageName);
         }
         if (result != AppOpsManager.MODE_ALLOWED) return;
-        addNoted(code, uid, packageName);
-        notifySuscribers(code, uid, packageName, true);
+        boolean notedAdded = addNoted(code, uid, packageName);
+        if (!notedAdded) return; // early return
+        boolean alsoActive;
+        synchronized (mActiveItems) {
+            alsoActive = getAppOpItem(mActiveItems, code, uid, packageName) != null;
+        }
+        if (!alsoActive) {
+            mBGHandler.post(() -> notifySuscribers(code, uid, packageName, true));
+        }
     }
 
     private void notifySuscribers(int code, int uid, String packageName, boolean active) {
         if (mCallbacksByCode.containsKey(code)) {
+            if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
             for (Callback cb: mCallbacksByCode.get(code)) {
                 cb.onActiveStateChanged(code, uid, packageName, active);
             }
@@ -295,7 +330,7 @@
 
     }
 
-    protected final class H extends Handler {
+    protected class H extends Handler {
         H(Looper looper) {
             super(looper);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 79996bc..29c6a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -70,11 +70,13 @@
         dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
         dialog.setMessage(com.android.internal.R.string.data_saver_description);
         dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
-                (OnClickListener) (dialogInterface, which) -> toggleDataSaver());
+                (OnClickListener) (dialogInterface, which) -> {
+                    toggleDataSaver();
+                    Prefs.putBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, true);
+                });
         dialog.setNegativeButton(com.android.internal.R.string.cancel, null);
         dialog.setShowForAllUsers(true);
         dialog.show();
-        Prefs.putBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, true);
     }
 
     private void toggleDataSaver() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 476a239..d0bd073 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -38,6 +38,8 @@
 /** Quick settings tile: Enable/Disable NFC **/
 public class NfcTile extends QSTileImpl<BooleanState> {
 
+    private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc);
+
     private NfcAdapter mAdapter;
 
     private boolean mListening;
@@ -105,8 +107,7 @@
         state.state = getAdapter() == null
                 ? Tile.STATE_UNAVAILABLE
                 : state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
-        state.icon = ResourceIcon.get(
-                state.value ? R.drawable.ic_qs_nfc_enabled : R.drawable.ic_qs_nfc_disabled);
+        state.icon = mIcon;
         state.label = mContext.getString(R.string.quick_settings_nfc_label);
         state.expandedAccessibilityClassName = Switch.class.getName();
         state.contentDescription = state.label;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index ef09434..7f7db2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Build;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -53,7 +54,7 @@
 
     private static final String TAG = "InterruptionStateProvider";
     private static final boolean DEBUG = false;
-    private static final boolean DEBUG_HEADS_UP = true;
+    private static final boolean DEBUG_HEADS_UP = Build.IS_DEBUGGABLE;
     private static final boolean ENABLE_HEADS_UP = true;
     private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 688e8eb..7c49c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -566,6 +566,8 @@
         mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
         mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
         addOnExpandedHeightChangedListener(mRoundnessManager::setExpanded);
+        mLockscreenUserManager.addUserChangedListener(userId ->
+                updateSensitiveness(false /* animated */));
         setOutlineProvider(mOutlineProvider);
 
         // Blocking helper manager wants to know the expanded state, update as well.
@@ -4602,7 +4604,8 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private void setHideSensitive(boolean hideSensitive, boolean animate) {
+    private void updateSensitiveness(boolean animate) {
+        boolean hideSensitive = mLockscreenUserManager.isAnyProfilePublicMode();
         if (hideSensitive != mAmbientState.isHideSensitive()) {
             int childCount = getChildCount();
             for (int i = 0; i < childCount; i++) {
@@ -5306,7 +5309,7 @@
 
         SysuiStatusBarStateController state = (SysuiStatusBarStateController)
                 Dependency.get(StatusBarStateController.class);
-        setHideSensitive(publicMode, state.goingToFullShade() /* animate */);
+        updateSensitiveness(state.goingToFullShade() /* animate */);
         setDimmed(onKeyguard, state.fromShadeLocked() /* animate */);
         setExpandingEnabled(!onKeyguard);
         ActivatableNotificationView activatedChild = getActivatedChild();
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 7892381..17481bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -25,12 +25,12 @@
 import android.telephony.Annotation;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
-import android.telephony.DisplayInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.text.Html;
 import android.text.TextUtils;
@@ -75,8 +75,9 @@
     // this could potentially become part of MobileState for simplification/complication
     // of code.
     private int mDataState = TelephonyManager.DATA_DISCONNECTED;
-    private DisplayInfo mDisplayInfo = new DisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
-            DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+    private TelephonyDisplayInfo mTelephonyDisplayInfo =
+            new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                    TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
     private ServiceState mServiceState;
     private SignalStrength mSignalStrength;
     private MobileIconGroup mDefaultIcons;
@@ -196,8 +197,13 @@
                 TelephonyIcons.THREE_G);
         mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EHRPD),
                 TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
+        if (mConfig.show4gFor3g) {
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
+                TelephonyIcons.FOUR_G);
+        } else {
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
                 TelephonyIcons.THREE_G);
+        }
         mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_TD_SCDMA),
                 TelephonyIcons.THREE_G);
 
@@ -240,41 +246,52 @@
         mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPAP), hPlusGroup);
 
         if (mConfig.show4gForLte) {
-            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+            mNetworkToIconLookup.put(toIconKey(
+                    TelephonyManager.NETWORK_TYPE_LTE),
                     TelephonyIcons.FOUR_G);
             if (mConfig.hideLtePlus) {
                 mNetworkToIconLookup.put(toDisplayIconKey(
-                        DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.FOUR_G);
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA),
+                        TelephonyIcons.FOUR_G);
             } else {
                 mNetworkToIconLookup.put(toDisplayIconKey(
-                        DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.FOUR_G_PLUS);
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA),
+                        TelephonyIcons.FOUR_G_PLUS);
             }
         } else {
-            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+            mNetworkToIconLookup.put(toIconKey(
+                    TelephonyManager.NETWORK_TYPE_LTE),
                     TelephonyIcons.LTE);
             if (mConfig.hideLtePlus) {
                 mNetworkToIconLookup.put(toDisplayIconKey(
-                        DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.LTE);
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA),
+                        TelephonyIcons.LTE);
             } else {
                 mNetworkToIconLookup.put(toDisplayIconKey(
-                        DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA), TelephonyIcons.LTE_PLUS);
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA),
+                        TelephonyIcons.LTE_PLUS);
             }
         }
-        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_IWLAN),
+        mNetworkToIconLookup.put(toIconKey(
+                TelephonyManager.NETWORK_TYPE_IWLAN),
                 TelephonyIcons.WFC);
         mNetworkToIconLookup.put(toDisplayIconKey(
-                DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO), TelephonyIcons.LTE_CA_5G_E);
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO),
+                TelephonyIcons.LTE_CA_5G_E);
         mNetworkToIconLookup.put(toDisplayIconKey(
-                DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA), TelephonyIcons.NR_5G);
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA),
+                TelephonyIcons.NR_5G);
         mNetworkToIconLookup.put(toDisplayIconKey(
-                DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE), TelephonyIcons.NR_5G_PLUS);
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE),
+                TelephonyIcons.NR_5G_PLUS);
     }
 
     private String getIconKey() {
-        if (mDisplayInfo.getOverrideNetworkType() == DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE) {
-            return toIconKey(mDisplayInfo.getNetworkType());
+        if (mTelephonyDisplayInfo.getOverrideNetworkType()
+                == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE) {
+            return toIconKey(mTelephonyDisplayInfo.getNetworkType());
         } else {
-            return toDisplayIconKey(mDisplayInfo.getOverrideNetworkType());
+            return toDisplayIconKey(mTelephonyDisplayInfo.getOverrideNetworkType());
         }
     }
 
@@ -284,13 +301,13 @@
 
     private String toDisplayIconKey(@Annotation.OverrideNetworkType int displayNetworkType) {
         switch (displayNetworkType) {
-            case DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA:
+            case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA:
                 return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA";
-            case DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO:
+            case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO:
                 return toIconKey(TelephonyManager.NETWORK_TYPE_LTE) + "_CA_Plus";
-            case DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA:
+            case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA:
                 return "5G";
-            case DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE:
+            case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE:
                 return "5G_Plus";
             default:
                 return "unsupported";
@@ -502,14 +519,14 @@
 
     /**
      * Updates the current state based on mServiceState, mSignalStrength, mDataState,
-     * mDisplayInfo, and mSimState.  It should be called any time one of these is updated.
+     * mTelephonyDisplayInfo, and mSimState.  It should be called any time one of these is updated.
      * This will call listeners if necessary.
      */
     private final void updateTelephony() {
         if (DEBUG) {
             Log.d(mTag, "updateTelephonySignalStrength: hasService=" +
                     Utils.isInService(mServiceState) + " ss=" + mSignalStrength
-                    + " displayInfo=" + mDisplayInfo);
+                    + " displayInfo=" + mTelephonyDisplayInfo);
         }
         checkDefaultData();
         mCurrentState.connected = Utils.isInService(mServiceState) && mSignalStrength != null;
@@ -577,7 +594,7 @@
     }
 
     boolean isDataDisabled() {
-        return !mPhone.isDataConnectionEnabled();
+        return !mPhone.isDataConnectionAllowed();
     }
 
     @VisibleForTesting
@@ -595,7 +612,7 @@
         pw.println("  mSubscription=" + mSubscriptionInfo + ",");
         pw.println("  mServiceState=" + mServiceState + ",");
         pw.println("  mSignalStrength=" + mSignalStrength + ",");
-        pw.println("  mDisplayInfo=" + mDisplayInfo + ",");
+        pw.println("  mTelephonyDisplayInfo=" + mTelephonyDisplayInfo + ",");
         pw.println("  mDataState=" + mDataState + ",");
         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
@@ -634,8 +651,9 @@
                         + " type=" + networkType);
             }
             mDataState = state;
-            if (networkType != mDisplayInfo.getNetworkType()) {
-                mDisplayInfo = new DisplayInfo(networkType, DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+            if (networkType != mTelephonyDisplayInfo.getNetworkType()) {
+                mTelephonyDisplayInfo = new TelephonyDisplayInfo(networkType,
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
             }
             updateTelephony();
         }
@@ -665,11 +683,11 @@
         }
 
         @Override
-        public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+        public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
             if (DEBUG) {
-                Log.d(mTag, "onDisplayInfoChanged: displayInfo=" + displayInfo);
+                Log.d(mTag, "onDisplayInfoChanged: telephonyDisplayInfo=" + telephonyDisplayInfo);
             }
-            mDisplayInfo = displayInfo;
+            mTelephonyDisplayInfo = telephonyDisplayInfo;
             updateTelephony();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index c08726d..2ced30f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -17,12 +17,12 @@
 package com.android.systemui.statusbar.policy;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
-import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
 
 import static com.android.systemui.Dependency.BG_LOOPER_NAME;
 
@@ -160,6 +160,7 @@
     ServiceState mLastServiceState;
     private boolean mUserSetup;
     private boolean mSimDetected;
+    private boolean mForceCellularValidated;
 
     /**
      * Construct this controller object and register for updates.
@@ -275,12 +276,41 @@
         mPhoneStateListener = new PhoneStateListener(bgLooper) {
             @Override
             public void onActiveDataSubscriptionIdChanged(int subId) {
+                // For data switching from A to B, we assume B is validated for up to 2 seconds iff:
+                // 1) A and B are in the same subscription group e.g. CBRS data switch. And
+                // 2) A was validated before the switch.
+                // This is to provide smooth transition for UI without showing cross during data
+                // switch.
+                if (keepCellularValidationBitInSwitch(mActiveMobileDataSubscription, subId)) {
+                    if (DEBUG) Log.d(TAG, ": mForceCellularValidated to true.");
+                    mForceCellularValidated = true;
+                    mReceiverHandler.removeCallbacks(mClearForceValidated);
+                    mReceiverHandler.postDelayed(mClearForceValidated, 2000);
+                }
                 mActiveMobileDataSubscription = subId;
                 doUpdateMobileControllers();
             }
         };
     }
 
+    private final Runnable mClearForceValidated = () -> {
+        if (DEBUG) Log.d(TAG, ": mClearForceValidated");
+        mForceCellularValidated = false;
+        updateConnectivity();
+    };
+
+    boolean isInGroupDataSwitch(int subId1, int subId2) {
+        SubscriptionInfo info1 = mSubscriptionManager.getActiveSubscriptionInfo(subId1);
+        SubscriptionInfo info2 = mSubscriptionManager.getActiveSubscriptionInfo(subId2);
+        return (info1 != null && info2 != null && info1.getGroupUuid() != null
+            && info1.getGroupUuid().equals(info2.getGroupUuid()));
+    }
+
+    boolean keepCellularValidationBitInSwitch(int sourceSubId, int destSubId) {
+        return mValidatedTransports.get(TRANSPORT_CELLULAR)
+                && isInGroupDataSwitch(sourceSubId, destSubId);
+    }
+
     public DataSaverController getDataSaverController() {
         return mDataSaverController;
     }
@@ -576,7 +606,7 @@
     }
 
     private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) {
-        if (subscriptions.size() == MODEM_COUNT_DUAL_MODEM) {
+        if (subscriptions.size() == 2) {
             SubscriptionInfo info1 = subscriptions.get(0);
             SubscriptionInfo info2 = subscriptions.get(1);
             if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
@@ -794,6 +824,8 @@
             }
         }
 
+        if (mForceCellularValidated) mValidatedTransports.set(TRANSPORT_CELLULAR);
+
         if (CHATTY) {
             Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports);
             Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 5a8ff4b..7f4d614 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -19,7 +19,7 @@
 
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
 import static android.telephony.SubscriptionManager.DATA_ROAMING_ENABLE;
-import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT;
+import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
 
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.assertFalse;
@@ -79,14 +79,14 @@
     private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
     private static final int TEST_CARRIER_ID = 1;
     private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0,
-            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
             DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, null,
             TEST_CARRIER_ID, 0);
     private static final SubscriptionInfo TEST_SUBSCRIPTION_NULL = new SubscriptionInfo(0, "", 0,
-            TEST_CARRIER, null, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_DISABLE,
+            TEST_CARRIER, null, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "", DATA_ROAMING_DISABLE,
             null, null, null, null, false, null, "");
     private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0,
-            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
             DATA_ROAMING_ENABLE, null, null, null, null, false, null, "");
     @Mock
     private WifiManager mWifiManager;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 78687a2..9271caf 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -17,7 +17,7 @@
 package com.android.keyguard;
 
 import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
-import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT;
+import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -83,11 +83,11 @@
     private static final int TEST_CARRIER_ID = 1;
     private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
     private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0,
-            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
             DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
             TEST_CARRIER_ID, 0);
     private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0,
-            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT, 0xFFFFFF, "",
+            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
             DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
             TEST_CARRIER_ID, 0);
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 59d5c24..a1842f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -28,6 +28,8 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import static java.lang.Thread.sleep;
+
 import android.app.AppOpsManager;
 import android.content.pm.PackageManager;
 import android.os.UserHandle;
@@ -36,7 +38,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
@@ -45,6 +46,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -63,14 +66,16 @@
     private AppOpsControllerImpl.H mMockHandler;
 
     private AppOpsControllerImpl mController;
+    private TestableLooper mTestableLooper;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mTestableLooper = TestableLooper.get(this);
 
         getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
 
-        mController = new AppOpsControllerImpl(mContext, Dependency.get(Dependency.BG_LOOPER));
+        mController = new AppOpsControllerImpl(mContext, mTestableLooper.getLooper());
     }
 
     @Test
@@ -94,6 +99,7 @@
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                 AppOpsManager.MODE_ALLOWED);
+        mTestableLooper.processAllMessages();
         verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID, TEST_PACKAGE_NAME, true);
     }
@@ -103,6 +109,7 @@
         mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
         verify(mCallback, never()).onActiveStateChanged(
                 anyInt(), anyInt(), anyString(), anyBoolean());
     }
@@ -113,6 +120,7 @@
         mController.removeCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
         verify(mCallback, never()).onActiveStateChanged(
                 anyInt(), anyInt(), anyString(), anyBoolean());
     }
@@ -123,6 +131,7 @@
         mController.removeCallback(new int[]{AppOpsManager.OP_CAMERA}, mCallback);
         mController.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
         verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO,
                 TEST_UID, TEST_PACKAGE_NAME, true);
     }
@@ -185,4 +194,129 @@
         verify(mMockHandler).removeCallbacksAndMessages(null);
         assertTrue(mController.getActiveAppOps().isEmpty());
     }
+
+    @Test
+    public void noDoubleUpdateOnOpNoted() {
+        mController.setBGHandler(mMockHandler);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        // Only one post to notify subscribers
+        verify(mMockHandler, times(1)).post(any());
+
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+    }
+
+    @Test
+    public void onDoubleOPNoted_scheduleTwiceForRemoval() {
+        mController.setBGHandler(mMockHandler);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        // Only one post to notify subscribers
+        verify(mMockHandler, times(2)).scheduleRemoval(any(), anyLong());
+    }
+
+    @Test
+    public void testActiveOpNotRemovedAfterNoted() throws InterruptedException {
+        // Replaces the timeout delay with 5 ms
+        AppOpsControllerImpl.H testHandler = mController.new H(mTestableLooper.getLooper()) {
+            @Override
+            public void scheduleRemoval(AppOpItem item, long timeToRemoval) {
+                super.scheduleRemoval(item, 5L);
+            }
+        };
+
+        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+        mController.setBGHandler(testHandler);
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        mTestableLooper.processAllMessages();
+        List<AppOpItem> list = mController.getActiveAppOps();
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        // Duplicates are not removed between active and noted
+        assertEquals(2, list.size());
+
+        sleep(10L);
+
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback, never()).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
+        list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+    }
+
+    @Test
+    public void testNotedNotRemovedAfterActive() {
+        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        mTestableLooper.processAllMessages();
+        List<AppOpItem> list = mController.getActiveAppOps();
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        // Duplicates are not removed between active and noted
+        assertEquals(2, list.size());
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
+
+        mTestableLooper.processAllMessages();
+
+        verify(mCallback, never()).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, false);
+        list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+    }
+
+    @Test
+    public void testNotedAndActiveOnlyOneCall() {
+        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        mTestableLooper.processAllMessages();
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+    }
+
+    @Test
+    public void testActiveAndNotedOnlyOneCall() {
+        mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
+
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+
+        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+                AppOpsManager.MODE_ALLOWED);
+
+        mTestableLooper.processAllMessages();
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 31054260..f2ed3e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -57,6 +57,8 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
@@ -119,6 +121,8 @@
     @Mock private MetricsLogger mMetricsLogger;
     @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
     @Mock private KeyguardBypassController mKeyguardBypassController;
+    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+    private UserChangedListener mUserChangedListener;
     private TestableNotificationEntryManager mEntryManager;
     private int mOriginalInterruptionModelSetting;
 
@@ -137,7 +141,9 @@
         mDependency.injectTestDependency(
                 NotificationBlockingHelperManager.class,
                 mBlockingHelperManager);
-        mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
+        mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
+                mLockscreenUserManager);
+        mDependency.injectTestDependency(StatusBarStateController.class, mBarState);
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         mDependency.injectTestDependency(NotificationRemoteInputManager.class,
                 mRemoteInputManager);
@@ -152,6 +158,8 @@
 
 
         NotificationShelf notificationShelf = mock(NotificationShelf.class);
+        ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor
+                .forClass(UserChangedListener.class);
 
         // The actual class under test.  You may need to work with this class directly when
         // testing anonymous class members of mStackScroller, like mMenuEventListener,
@@ -174,6 +182,8 @@
         mStackScroller.setGroupManager(mGroupManager);
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
         mStackScroller.setIconAreaController(mNotificationIconAreaController);
+        verify(mLockscreenUserManager).addUserChangedListener(userChangedCaptor.capture());
+        mUserChangedListener = userChangedCaptor.getValue();
 
         // Stub out functionality that isn't necessary to test.
         doNothing().when(mBar)
@@ -247,6 +257,12 @@
     }
 
     @Test
+    public void testOnStatePostChange_verifyIfProfileIsPublic() {
+        mUserChangedListener.onUserChanged(0);
+        verify(mLockscreenUserManager).isAnyProfilePublicMode();
+    }
+
+    @Test
     public void manageNotifications_visible() {
         FooterView view = mock(FooterView.class);
         mStackScroller.setFooterView(view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 2649054..3c3f2fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -42,13 +42,13 @@
 import android.os.Handler;
 import android.provider.Settings;
 import android.provider.Settings.Global;
-import android.telephony.DisplayInfo;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
@@ -94,7 +94,7 @@
     protected PhoneStateListener mPhoneStateListener;
     protected SignalStrength mSignalStrength;
     protected ServiceState mServiceState;
-    protected DisplayInfo mDisplayInfo;
+    protected TelephonyDisplayInfo mTelephonyDisplayInfo;
     protected ConnectivityManager mMockCm;
     protected WifiManager mMockWm;
     protected SubscriptionManager mMockSm;
@@ -145,7 +145,7 @@
 
         mSignalStrength = mock(SignalStrength.class);
         mServiceState = mock(ServiceState.class);
-        mDisplayInfo = mock(DisplayInfo.class);
+        mTelephonyDisplayInfo = mock(TelephonyDisplayInfo.class);
 
         mConfig = new Config();
         mConfig.hspaDataDistinguishable = true;
@@ -176,7 +176,7 @@
     protected void setupNetworkController() {
         // For now just pretend to be the data sim, so we can test that too.
         mSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(true);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(true);
         setDefaultSubId(mSubId);
         setSubscriptions(mSubId);
         mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
@@ -319,7 +319,7 @@
     protected void updateServiceState() {
         Log.d(TAG, "Sending Service State: " + mServiceState);
         mPhoneStateListener.onServiceStateChanged(mServiceState);
-        mPhoneStateListener.onDisplayInfoChanged(mDisplayInfo);
+        mPhoneStateListener.onDisplayInfoChanged(mTelephonyDisplayInfo);
     }
 
     public void updateCallState(int state) {
@@ -335,7 +335,7 @@
                 .build();
         when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
                 .thenReturn(fakeRegInfo);
-        when(mDisplayInfo.getNetworkType()).thenReturn(dataNetType);
+        when(mTelephonyDisplayInfo.getNetworkType()).thenReturn(dataNetType);
         mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 6b02ff0..242d85c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -119,7 +119,7 @@
     @Test
     public void testNoInternetIcon_withDefaultSub() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -133,7 +133,7 @@
     @Test
     public void testDataDisabledIcon_withDefaultSub() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -147,7 +147,7 @@
     @Test
     public void testNonDefaultSIM_showsFullSignal_connected() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         setDefaultSubId(mSubId + 1);
         updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
@@ -162,7 +162,7 @@
     @Test
     public void testNonDefaultSIM_showsFullSignal_disconnected() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         setDefaultSubId(mSubId + 1);
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
@@ -177,7 +177,7 @@
     @Test
     public void testDataDisabledIcon_UserNotSetup() {
         setupNetworkController();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -192,7 +192,7 @@
     @Test
     public void testAlwaysShowDataRatIcon() {
         setupDefaultSignal();
-        when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
+        when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED,
                 TelephonyManager.NETWORK_TYPE_GSM);
 
@@ -235,7 +235,7 @@
                 .build();
         when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN))
                 .thenReturn(fakeRegInfo);
-        when(mDisplayInfo.getNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_HSPA);
+        when(mTelephonyDisplayInfo.getNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_HSPA);
         updateServiceState();
         verifyDataIndicators(TelephonyIcons.ICON_H);
     }
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 0c37235..eb72b81 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -16,9 +16,7 @@
 
 java_defaults {
     name: "TetheringAndroidLibraryDefaults",
-    // 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",
+    sdk_version: "module_current",
     srcs: [
         "src/**/*.java",
         ":framework-tethering-shared-srcs",
@@ -27,23 +25,16 @@
     ],
     static_libs: [
         "androidx.annotation_annotation",
-        "netd_aidl_interface-unstable-java",
+        "netd_aidl_interface-V3-java",
         "netlink-client",
-        "networkstack-aidl-interfaces-unstable-java",
+        "networkstack-aidl-interfaces-java",
         "android.hardware.tetheroffload.config-V1.0-java",
         "android.hardware.tetheroffload.control-V1.0-java",
         "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",
@@ -59,6 +50,11 @@
 cc_library {
     name: "libtetherutilsjni",
     sdk_version: "current",
+    apex_available: [
+        "//apex_available:platform", // Used by InProcessTethering
+        "com.android.tethering",
+    ],
+    min_sdk_version: "current",
     srcs: [
         "jni/android_net_util_TetheringUtils.cpp",
     ],
@@ -91,9 +87,7 @@
 // Common defaults for compiling the actual APK.
 java_defaults {
     name: "TetheringAppDefaults",
-    // 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",
+    sdk_version: "module_current",
     privileged: true,
     jni_libs: [
         "libtetherutilsjni",
@@ -102,12 +96,7 @@
         "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: {
@@ -124,6 +113,8 @@
     manifest: "AndroidManifest_InProcess.xml",
     // InProcessTethering is a replacement for Tethering
     overrides: ["Tethering"],
+    apex_available: ["com.android.tethering"],
+    min_sdk_version: "current",
 }
 
 // Updatable tethering packaged as an application
@@ -137,4 +128,5 @@
     // The permission configuration *must* be included to ensure security of the device
     required: ["NetworkPermissionConfig"],
     apex_available: ["com.android.tethering"],
+    min_sdk_version: "current",
 }
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index c71d0d7..2b2fe45 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -27,24 +27,28 @@
          added to the privileged permissions whitelist for that package. -->
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
-    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USB" />
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 
+    <protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
+
     <application
         android:process="com.android.networkstack.process"
         android:extractNativeLibs="false"
         android:persistent="true">
-        <service android:name="com.android.server.connectivity.tethering.TetheringService"
-                 android:permission="android.permission.MAINLINE_NETWORK_STACK">
+        <service android:name="com.android.networkstack.tethering.TetheringService"
+                 android:permission="android.permission.MAINLINE_NETWORK_STACK"
+                 android:exported="true">
             <intent-filter>
                 <action android:name="android.net.ITetheringConnector"/>
             </intent-filter>
diff --git a/packages/Tethering/AndroidManifest_InProcess.xml b/packages/Tethering/AndroidManifest_InProcess.xml
index 02ea551..b1f1240 100644
--- a/packages/Tethering/AndroidManifest_InProcess.xml
+++ b/packages/Tethering/AndroidManifest_InProcess.xml
@@ -22,9 +22,10 @@
           android:process="system">
     <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
     <application>
-        <service android:name="com.android.server.connectivity.tethering.TetheringService"
+        <service android:name="com.android.networkstack.tethering.TetheringService"
                  android:process="system"
-                 android:permission="android.permission.MAINLINE_NETWORK_STACK">
+                 android:permission="android.permission.MAINLINE_NETWORK_STACK"
+                 android:exported="true">
             <intent-filter>
                 <action android:name="android.net.ITetheringConnector.InProcess"/>
             </intent-filter>
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index 96a4d20..67097a7 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -17,6 +17,7 @@
 apex {
     name: "com.android.tethering",
     updatable: true,
+    min_sdk_version: "current",
     java_libs: ["framework-tethering"],
     apps: ["Tethering"],
     manifest: "manifest.json",
@@ -35,3 +36,12 @@
     name: "com.android.tethering.certificate",
     certificate: "com.android.tethering",
 }
+
+override_apex {
+    name: "com.android.tethering.inprocess",
+    base: "com.android.tethering",
+    package_name: "com.android.tethering.inprocess",
+    apps: [
+        "InProcessTethering",
+    ],
+}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 5b73dd5..79a1782 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -13,42 +13,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// AIDL interfaces between the core system and the tethering mainline module.
-aidl_interface {
-    name: "tethering-aidl-interfaces",
-    local_include_dir: "src",
-    include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
-    srcs: [
-        // @JavaOnlyStableParcelable aidl declarations must not be listed here, as this would cause
-        // compilation to fail (b/148001843).
-        "src/android/net/IIntResultListener.aidl",
-        "src/android/net/ITetheringConnector.aidl",
-        "src/android/net/ITetheringEventCallback.aidl",
-        "src/android/net/TetheringCallbackStartedParcel.aidl",
-        "src/android/net/TetheringConfigurationParcel.aidl",
-        "src/android/net/TetheringRequestParcel.aidl",
-        "src/android/net/TetherStatesParcel.aidl",
-    ],
-    backend: {
-        ndk: {
-            enabled: false,
-        },
-        cpp: {
-            enabled: false,
-        },
-    },
-}
-
 java_library {
     name: "framework-tethering",
     sdk_version: "module_current",
     srcs: [
-        "src/android/net/TetheredClient.java",
-        "src/android/net/TetheringManager.java",
-        "src/android/net/TetheringConstants.java",
-    ],
-    static_libs: [
-        "tethering-aidl-interfaces-java",
+        ":framework-tethering-srcs",
     ],
     jarjar_rules: "jarjar-rules.txt",
     installable: true,
@@ -60,28 +29,13 @@
     hostdex: true, // for hiddenapi check
     visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
     apex_available: ["com.android.tethering"],
+    permitted_packages: ["android.net"],
 }
 
-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",
+stubs_defaults {
+    name: "framework-tethering-stubs-defaults",
+    srcs: [":framework-tethering-srcs"],
+    dist: { dest: "framework-tethering.txt" },
 }
 
 filegroup {
@@ -101,3 +55,56 @@
     ],
     path: "src"
 }
+
+droidstubs {
+    name: "framework-tethering-stubs-srcs-publicapi",
+    defaults: [
+        "framework-module-stubs-defaults-publicapi",
+        "framework-tethering-stubs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-tethering-stubs-srcs-systemapi",
+    defaults: [
+        "framework-module-stubs-defaults-systemapi",
+        "framework-tethering-stubs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-tethering-api-module_libs_api",
+    defaults: [
+        "framework-module-api-defaults-module_libs_api",
+        "framework-tethering-stubs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-tethering-stubs-srcs-module_libs_api",
+    defaults: [
+        "framework-module-stubs-defaults-module_libs_api",
+        "framework-tethering-stubs-defaults",
+    ],
+}
+
+java_library {
+    name: "framework-tethering-stubs-publicapi",
+    srcs: [":framework-tethering-stubs-srcs-publicapi"],
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
+    dist: { dest: "framework-tethering.jar" },
+}
+
+java_library {
+    name: "framework-tethering-stubs-systemapi",
+    srcs: [":framework-tethering-stubs-srcs-systemapi"],
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
+    dist: { dest: "framework-tethering.jar" },
+}
+
+java_library {
+    name: "framework-tethering-stubs-module_libs_api",
+    srcs: [":framework-tethering-stubs-srcs-module_libs_api"],
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
+    dist: { dest: "framework-tethering.jar" },
+}
diff --git a/packages/Tethering/common/TetheringLib/api/current.txt b/packages/Tethering/common/TetheringLib/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
new file mode 100644
index 0000000..754584e
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -0,0 +1,129 @@
+// Signature format: 2.0
+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 final 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 public boolean isTetheringSupported(@NonNull String);
+    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.Manifest.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.Manifest.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.Manifest.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.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+    method @Deprecated public int tether(@NonNull String);
+    method @RequiresPermission(anyOf={android.Manifest.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_FORWARDING_ERROR = 9; // 0x9
+    field public static final int TETHER_ERROR_ENABLE_FORWARDING_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_INTERNAL_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_PROVISIONING_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_UNKNOWN_TYPE = 16; // 0x10
+    field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+    field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+    field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
+    field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
+    field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
+  }
+
+  public static interface TetheringManager.OnTetheringEntitlementResultListener {
+    method public void onTetheringEntitlementResult(int);
+  }
+
+  public static interface TetheringManager.StartTetheringCallback {
+    method public default void onTetheringFailed(int);
+    method public default void onTetheringStarted();
+  }
+
+  public static interface TetheringManager.TetheringEventCallback {
+    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+    method public default void onError(@NonNull String, int);
+    method public default void onOffloadStatusChanged(int);
+    method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheringSupported(boolean);
+    method public default void onUpstreamChanged(@Nullable android.net.Network);
+  }
+
+  public static class TetheringManager.TetheringInterfaceRegexps {
+    method @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+    method @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+    method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+  }
+
+  public static class TetheringManager.TetheringRequest {
+    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
+    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
+    method public boolean getShouldShowEntitlementUi();
+    method public int getTetheringType();
+    method public boolean isExemptFromEntitlementCheck();
+  }
+
+  public static class TetheringManager.TetheringRequest.Builder {
+    ctor public TetheringManager.TetheringRequest.Builder(int);
+    method @NonNull public android.net.TetheringManager.TetheringRequest build();
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
+  }
+
+}
+
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt b/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/api/removed.txt b/packages/Tethering/common/TetheringLib/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/api/system-current.txt b/packages/Tethering/common/TetheringLib/api/system-current.txt
new file mode 100644
index 0000000..edd1ebb5
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/system-current.txt
@@ -0,0 +1,99 @@
+// Signature format: 2.0
+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 TetheringManager {
+    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.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+    method @RequiresPermission(anyOf={android.Manifest.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.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+    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_FORWARDING_ERROR = 9; // 0x9
+    field public static final int TETHER_ERROR_ENABLE_FORWARDING_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_INTERNAL_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_PROVISIONING_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_UNKNOWN_TYPE = 16; // 0x10
+    field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+    field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+    field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
+    field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
+    field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
+  }
+
+  public static interface TetheringManager.OnTetheringEntitlementResultListener {
+    method public void onTetheringEntitlementResult(int);
+  }
+
+  public static interface TetheringManager.StartTetheringCallback {
+    method public default void onTetheringFailed(int);
+    method public default void onTetheringStarted();
+  }
+
+  public static interface TetheringManager.TetheringEventCallback {
+    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+    method public default void onError(@NonNull String, int);
+    method public default void onOffloadStatusChanged(int);
+    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheringSupported(boolean);
+    method public default void onUpstreamChanged(@Nullable android.net.Network);
+  }
+
+  public static class TetheringManager.TetheringRequest {
+    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
+    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
+    method public boolean getShouldShowEntitlementUi();
+    method public int getTetheringType();
+    method public boolean isExemptFromEntitlementCheck();
+  }
+
+  public static class TetheringManager.TetheringRequest.Builder {
+    ctor public TetheringManager.TetheringRequest.Builder(int);
+    method @NonNull public android.net.TetheringManager.TetheringRequest build();
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
+  }
+
+}
+
diff --git a/packages/Tethering/common/TetheringLib/api/system-removed.txt b/packages/Tethering/common/TetheringLib/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index 8be7964..cf094aa 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -22,25 +22,31 @@
 
 /** @hide */
 oneway interface ITetheringConnector {
-    void tether(String iface, String callerPkg, IIntResultListener receiver);
-
-    void untether(String iface, String callerPkg, IIntResultListener receiver);
-
-    void setUsbTethering(boolean enable, String callerPkg, IIntResultListener receiver);
-
-    void startTethering(in TetheringRequestParcel request, String callerPkg,
+    void tether(String iface, String callerPkg, String callingAttributionTag,
             IIntResultListener receiver);
 
-    void stopTethering(int type, String callerPkg, IIntResultListener receiver);
+    void untether(String iface, String callerPkg, String callingAttributionTag,
+            IIntResultListener receiver);
+
+    void setUsbTethering(boolean enable, String callerPkg,
+            String callingAttributionTag, IIntResultListener receiver);
+
+    void startTethering(in TetheringRequestParcel request, String callerPkg,
+            String callingAttributionTag, IIntResultListener receiver);
+
+    void stopTethering(int type, String callerPkg, String callingAttributionTag,
+            IIntResultListener receiver);
 
     void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
-            boolean showEntitlementUi, String callerPkg);
+            boolean showEntitlementUi, String callerPkg, String callingAttributionTag);
 
     void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
 
     void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
 
-    void isTetheringSupported(String callerPkg, IIntResultListener receiver);
+    void isTetheringSupported(String callerPkg, String callingAttributionTag,
+            IIntResultListener receiver);
 
-    void stopAllTethering(String callerPkg, IIntResultListener receiver);
+    void stopAllTethering(String callerPkg, String callingAttributionTag,
+            IIntResultListener receiver);
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 8b8b9e5..48be0d9 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -64,16 +64,26 @@
         dest.writeInt(mTetheringType);
     }
 
+    /**
+     * Get the MAC address used to identify the client.
+     */
     @NonNull
     public MacAddress getMacAddress() {
         return mMacAddress;
     }
 
+    /**
+     * Get information on the list of addresses that are associated with the client.
+     */
     @NonNull
     public List<AddressInfo> getAddresses() {
         return new ArrayList<>(mAddresses);
     }
 
+    /**
+     * Get the type of tethering used by the client.
+     * @return one of the {@code TetheringManager#TETHERING_*} constants.
+     */
     public int getTetheringType() {
         return mTetheringType;
     }
@@ -115,45 +125,47 @@
         private final LinkAddress mAddress;
         @Nullable
         private final String mHostname;
-        // TODO: use LinkAddress expiration time once it is supported
-        private final long mExpirationTime;
 
         /** @hide */
         public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) {
-            this(address, hostname, 0);
-        }
-
-        /** @hide */
-        public AddressInfo(@NonNull LinkAddress address, String hostname, long expirationTime) {
             this.mAddress = address;
             this.mHostname = hostname;
-            this.mExpirationTime = expirationTime;
         }
 
         private AddressInfo(Parcel in) {
-            this(in.readParcelable(null),  in.readString(), in.readLong());
+            this(in.readParcelable(null),  in.readString());
         }
 
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeParcelable(mAddress, flags);
             dest.writeString(mHostname);
-            dest.writeLong(mExpirationTime);
         }
 
+        /**
+         * Get the link address (including prefix length and lifetime) used by the client.
+         *
+         * This may be an IPv4 or IPv6 address.
+         */
         @NonNull
         public LinkAddress getAddress() {
             return mAddress;
         }
 
+        /**
+         * Get the hostname that was advertised by the client when obtaining its address, if any.
+         */
         @Nullable
         public String getHostname() {
             return mHostname;
         }
 
-        /** @hide TODO: use expiration time in LinkAddress */
+        /**
+         * Get the expiration time of the address assigned to the client.
+         * @hide
+         */
         public long getExpirationTime() {
-            return mExpirationTime;
+            return mAddress.getExpirationTime();
         }
 
         @Override
@@ -163,7 +175,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(mAddress, mHostname, mExpirationTime);
+            return Objects.hash(mAddress, mHostname);
         }
 
         @Override
@@ -173,8 +185,7 @@
             // Use .equals() for addresses as all changes, including address expiry changes,
             // should be included.
             return other.mAddress.equals(mAddress)
-                    && Objects.equals(mHostname, other.mHostname)
-                    && mExpirationTime == other.mExpirationTime;
+                    && Objects.equals(mHostname, other.mHostname);
         }
 
         @NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index a18f5da..fd6f171 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -32,7 +32,7 @@
  * @hide
  */
 @SystemApi(client = MODULE_LIBRARIES)
-public class TetheringConstants {
+public final class TetheringConstants {
     /** An explicit private class to avoid exposing constructor.*/
     private TetheringConstants() { }
 
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 5d680b0..04ca033 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -115,6 +115,19 @@
      */
     public static final String EXTRA_ERRORED_TETHER = "erroredArray";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, value = {
+            TETHERING_WIFI,
+            TETHERING_USB,
+            TETHERING_BLUETOOTH,
+            TETHERING_WIFI_P2P,
+            TETHERING_NCM,
+            TETHERING_ETHERNET,
+    })
+    public @interface TetheringType {
+    }
+
     /**
      * Invalid tethering type.
      * @see #startTethering.
@@ -158,22 +171,60 @@
      */
     public static final int TETHERING_ETHERNET = 5;
 
-    public static final int TETHER_ERROR_NO_ERROR           = 0;
-    public static final int TETHER_ERROR_UNKNOWN_IFACE      = 1;
-    public static final int TETHER_ERROR_SERVICE_UNAVAIL    = 2;
-    public static final int TETHER_ERROR_UNSUPPORTED        = 3;
-    public static final int TETHER_ERROR_UNAVAIL_IFACE      = 4;
-    public static final int TETHER_ERROR_MASTER_ERROR       = 5;
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            TETHER_ERROR_NO_ERROR,
+            TETHER_ERROR_PROVISIONING_FAILED,
+            TETHER_ERROR_ENTITLEMENT_UNKNOWN,
+    })
+    public @interface EntitlementResult {
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            TETHER_ERROR_NO_ERROR,
+            TETHER_ERROR_UNKNOWN_IFACE,
+            TETHER_ERROR_SERVICE_UNAVAIL,
+            TETHER_ERROR_INTERNAL_ERROR,
+            TETHER_ERROR_TETHER_IFACE_ERROR,
+            TETHER_ERROR_ENABLE_FORWARDING_ERROR,
+            TETHER_ERROR_DISABLE_FORWARDING_ERROR,
+            TETHER_ERROR_IFACE_CFG_ERROR,
+            TETHER_ERROR_DHCPSERVER_ERROR,
+    })
+    public @interface TetheringIfaceError {
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            TETHER_ERROR_SERVICE_UNAVAIL,
+            TETHER_ERROR_INTERNAL_ERROR,
+            TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION,
+            TETHER_ERROR_UNKNOWN_TYPE,
+    })
+    public @interface StartTetheringError {
+    }
+
+    public static final int TETHER_ERROR_NO_ERROR = 0;
+    public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
+    public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
+    public static final int TETHER_ERROR_UNSUPPORTED = 3;
+    public static final int TETHER_ERROR_UNAVAIL_IFACE = 4;
+    public static final int TETHER_ERROR_INTERNAL_ERROR = 5;
     public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
     public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
-    public static final int TETHER_ERROR_ENABLE_NAT_ERROR     = 8;
-    public static final int TETHER_ERROR_DISABLE_NAT_ERROR    = 9;
-    public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;
-    public static final int TETHER_ERROR_PROVISION_FAILED     = 11;
-    public static final int TETHER_ERROR_DHCPSERVER_ERROR     = 12;
+    public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8;
+    public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9;
+    public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
+    public static final int TETHER_ERROR_PROVISIONING_FAILED = 11;
+    public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
     public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13;
     public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14;
     public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15;
+    public static final int TETHER_ERROR_UNKNOWN_TYPE = 16;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -433,7 +484,7 @@
 
         return dispatcher.waitForResult((connector, listener) -> {
             try {
-                connector.tether(iface, callerPkg, listener);
+                connector.tether(iface, callerPkg, getAttributionTag(), listener);
             } catch (RemoteException e) {
                 throw new IllegalStateException(e);
             }
@@ -441,6 +492,13 @@
     }
 
     /**
+     * @return the context's attribution tag
+     */
+    private @Nullable String getAttributionTag() {
+        return null;
+    }
+
+    /**
      * Stop tethering the named interface.
      *
      * @deprecated The only usages is PanService. It uses this for legacy reasons
@@ -458,7 +516,7 @@
 
         return dispatcher.waitForResult((connector, listener) -> {
             try {
-                connector.untether(iface, callerPkg, listener);
+                connector.untether(iface, callerPkg, getAttributionTag(), listener);
             } catch (RemoteException e) {
                 throw new IllegalStateException(e);
             }
@@ -485,7 +543,8 @@
 
         return dispatcher.waitForResult((connector, listener) -> {
             try {
-                connector.setUsbTethering(enable, callerPkg, listener);
+                connector.setUsbTethering(enable, callerPkg, getAttributionTag(),
+                        listener);
             } catch (RemoteException e) {
                 throw new IllegalStateException(e);
             }
@@ -508,23 +567,36 @@
             private final TetheringRequestParcel mBuilderParcel;
 
             /** Default constructor of Builder. */
-            public Builder(final int type) {
+            public Builder(@TetheringType final int type) {
                 mBuilderParcel = new TetheringRequestParcel();
                 mBuilderParcel.tetheringType = type;
                 mBuilderParcel.localIPv4Address = null;
+                mBuilderParcel.staticClientAddress = null;
                 mBuilderParcel.exemptFromEntitlementCheck = false;
                 mBuilderParcel.showProvisioningUi = true;
             }
 
             /**
-             * Configure tethering with static IPv4 assignment (with DHCP disabled).
+             * Configure tethering with static IPv4 assignment.
              *
-             * @param localIPv4Address The preferred local IPv4 address to use.
+             * A DHCP server will be started, but will only be able to offer the client address.
+             * The two addresses must be in the same prefix.
+             *
+             * @param localIPv4Address The preferred local IPv4 link address to use.
+             * @param clientAddress The static client address.
              */
             @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
             @NonNull
-            public Builder useStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address) {
+            public Builder setStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address,
+                    @NonNull final LinkAddress clientAddress) {
+                Objects.requireNonNull(localIPv4Address);
+                Objects.requireNonNull(clientAddress);
+                if (!checkStaticAddressConfiguration(localIPv4Address, clientAddress)) {
+                    throw new IllegalArgumentException("Invalid server or client addresses");
+                }
+
                 mBuilderParcel.localIPv4Address = localIPv4Address;
+                mBuilderParcel.staticClientAddress = clientAddress;
                 return this;
             }
 
@@ -536,11 +608,14 @@
                 return this;
             }
 
-            /** Start tethering without showing the provisioning UI. */
+            /**
+             * If an entitlement check is needed, sets whether to show the entitlement UI or to
+             * perform a silent entitlement check. By default, the entitlement UI is shown.
+             */
             @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
             @NonNull
-            public Builder setSilentProvisioning(boolean silent) {
-                mBuilderParcel.showProvisioningUi = silent;
+            public Builder setShouldShowEntitlementUi(boolean showUi) {
+                mBuilderParcel.showProvisioningUi = showUi;
                 return this;
             }
 
@@ -552,6 +627,53 @@
         }
 
         /**
+         * Get the local IPv4 address, if one was configured with
+         * {@link Builder#setStaticIpv4Addresses}.
+         */
+        @Nullable
+        public LinkAddress getLocalIpv4Address() {
+            return mRequestParcel.localIPv4Address;
+        }
+
+        /**
+         * Get the static IPv4 address of the client, if one was configured with
+         * {@link Builder#setStaticIpv4Addresses}.
+         */
+        @Nullable
+        public LinkAddress getClientStaticIpv4Address() {
+            return mRequestParcel.staticClientAddress;
+        }
+
+        /** Get tethering type. */
+        @TetheringType
+        public int getTetheringType() {
+            return mRequestParcel.tetheringType;
+        }
+
+        /** Check if exempt from entitlement check. */
+        public boolean isExemptFromEntitlementCheck() {
+            return mRequestParcel.exemptFromEntitlementCheck;
+        }
+
+        /** Check if show entitlement ui.  */
+        public boolean getShouldShowEntitlementUi() {
+            return mRequestParcel.showProvisioningUi;
+        }
+
+        /**
+         * Check whether the two addresses are ipv4 and in the same prefix.
+         * @hide
+         */
+        public static boolean checkStaticAddressConfiguration(
+                @NonNull final LinkAddress localIPv4Address,
+                @NonNull final LinkAddress clientAddress) {
+            return localIPv4Address.getPrefixLength() == clientAddress.getPrefixLength()
+                    && localIPv4Address.isIpv4() && clientAddress.isIpv4()
+                    && new IpPrefix(localIPv4Address.toString()).equals(
+                    new IpPrefix(clientAddress.toString()));
+        }
+
+        /**
          * Get a TetheringRequestParcel from the configuration
          * @hide
          */
@@ -563,6 +685,7 @@
         public String toString() {
             return "TetheringRequest [ type= " + mRequestParcel.tetheringType
                     + ", localIPv4Address= " + mRequestParcel.localIPv4Address
+                    + ", staticClientAddress= " + mRequestParcel.staticClientAddress
                     + ", exemptFromEntitlementCheck= "
                     + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= "
                     + mRequestParcel.showProvisioningUi + " ]";
@@ -572,18 +695,18 @@
     /**
      * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
      */
-    public abstract static class StartTetheringCallback {
+    public interface StartTetheringCallback {
         /**
          * Called when tethering has been successfully started.
          */
-        public void onTetheringStarted() {}
+        default void onTetheringStarted() {}
 
         /**
          * Called when starting tethering failed.
          *
-         * @param resultCode One of the {@code TETHER_ERROR_*} constants.
+         * @param error The error that caused the failure.
          */
-        public void onTetheringFailed(final int resultCode) {}
+        default void onTetheringFailed(@StartTetheringError final int error) {}
     }
 
     /**
@@ -620,7 +743,8 @@
                 });
             }
         };
-        getConnector(c -> c.startTethering(request.getParcel(), callerPkg, listener));
+        getConnector(c -> c.startTethering(request.getParcel(), callerPkg,
+                getAttributionTag(), listener));
     }
 
     /**
@@ -633,11 +757,13 @@
      * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants.
      * @param executor {@link Executor} to specify the thread upon which the callback of
      *         TetheringRequest will be invoked.
+     * @hide
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.TETHER_PRIVILEGED,
             android.Manifest.permission.WRITE_SETTINGS
     })
+    @SystemApi(client = MODULE_LIBRARIES)
     public void startTethering(int type, @NonNull final Executor executor,
             @NonNull final StartTetheringCallback callback) {
         startTethering(new TetheringRequest.Builder(type).build(), executor, callback);
@@ -654,11 +780,12 @@
             android.Manifest.permission.TETHER_PRIVILEGED,
             android.Manifest.permission.WRITE_SETTINGS
     })
-    public void stopTethering(final int type) {
+    public void stopTethering(@TetheringType final int type) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "stopTethering caller:" + callerPkg);
 
-        getConnector(c -> c.stopTethering(type, callerPkg, new IIntResultListener.Stub() {
+        getConnector(c -> c.stopTethering(type, callerPkg, getAttributionTag(),
+                new IIntResultListener.Stub() {
             @Override
             public void onResult(int resultCode) {
                 // TODO: provide an API to obtain result
@@ -679,10 +806,10 @@
          *
          * @param resultCode an int value of entitlement result. It may be one of
          *         {@link #TETHER_ERROR_NO_ERROR},
-         *         {@link #TETHER_ERROR_PROVISION_FAILED}, or
+         *         {@link #TETHER_ERROR_PROVISIONING_FAILED}, or
          *         {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN}.
          */
-        void onTetheringEntitlementResult(int resultCode);
+        void onTetheringEntitlementResult(@EntitlementResult int result);
     }
 
     /**
@@ -697,7 +824,8 @@
      * fail if a tethering entitlement check is required.
      *
      * @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants.
-     * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
+     * @param showEntitlementUi a boolean indicating whether to check result for the UI-based
+     *         entitlement check or the silent entitlement check.
      * @param executor the executor on which callback will be invoked.
      * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to
      *         notify the caller of the result of entitlement check. The listener may be called zero
@@ -707,7 +835,8 @@
             android.Manifest.permission.TETHER_PRIVILEGED,
             android.Manifest.permission.WRITE_SETTINGS
     })
-    public void requestLatestTetheringEntitlementResult(int type, boolean showEntitlementUi,
+    public void requestLatestTetheringEntitlementResult(@TetheringType int type,
+            boolean showEntitlementUi,
             @NonNull Executor executor,
             @NonNull final OnTetheringEntitlementResultListener listener) {
         if (listener == null) {
@@ -736,20 +865,20 @@
      */
     // TODO: improve the usage of ResultReceiver, b/145096122
     @SystemApi(client = MODULE_LIBRARIES)
-    public void requestLatestTetheringEntitlementResult(final int type,
+    public void requestLatestTetheringEntitlementResult(@TetheringType final int type,
             @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg);
 
         getConnector(c -> c.requestLatestTetheringEntitlementResult(
-                type, receiver, showEntitlementUi, callerPkg));
+                type, receiver, showEntitlementUi, callerPkg, getAttributionTag()));
     }
 
     /**
      * Callback for use with {@link registerTetheringEventCallback} to find out tethering
      * upstream status.
      */
-    public abstract static class TetheringEventCallback {
+    public interface TetheringEventCallback {
         /**
          * Called when tethering supported status changed.
          *
@@ -761,7 +890,7 @@
          *
          * @param supported The new supported status
          */
-        public void onTetheringSupported(boolean supported) {}
+        default void onTetheringSupported(boolean supported) {}
 
         /**
          * Called when tethering upstream changed.
@@ -772,7 +901,7 @@
          * @param network the {@link Network} of tethering upstream. Null means tethering doesn't
          * have any upstream.
          */
-        public void onUpstreamChanged(@Nullable Network network) {}
+        default void onUpstreamChanged(@Nullable Network network) {}
 
         /**
          * Called when there was a change in tethering interface regular expressions.
@@ -780,28 +909,30 @@
          * <p>This will be called immediately after the callback is registered, and may be called
          * multiple times later upon changes.
          * @param reg The new regular expressions.
-         * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism.
+         *
+         * @hide
          */
-        @Deprecated
-        public void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
+        @SystemApi(client = MODULE_LIBRARIES)
+        default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
 
         /**
-         * Called when there was a change in the list of tetherable interfaces.
+         * Called when there was a change in the list of tetherable interfaces. Tetherable
+         * interface means this interface is available and can be used for tethering.
          *
          * <p>This will be called immediately after the callback is registered, and may be called
          * multiple times later upon changes.
-         * @param interfaces The list of tetherable interfaces.
+         * @param interfaces The list of tetherable interface names.
          */
-        public void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
+        default void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
 
         /**
          * Called when there was a change in the list of tethered interfaces.
          *
          * <p>This will be called immediately after the callback is registered, and may be called
          * multiple times later upon changes.
-         * @param interfaces The list of tethered interfaces.
+         * @param interfaces The list of 0 or more String of currently tethered interface names.
          */
-        public void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
+        default void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
 
         /**
          * Called when an error occurred configuring tethering.
@@ -811,7 +942,7 @@
          * @param ifName Name of the interface.
          * @param error One of {@code TetheringManager#TETHER_ERROR_*}.
          */
-        public void onError(@NonNull String ifName, int error) {}
+        default void onError(@NonNull String ifName, @TetheringIfaceError int error) {}
 
         /**
          * Called when the list of tethered clients changes.
@@ -824,7 +955,7 @@
          * determine if they are still connected.
          * @param clients The new set of tethered clients; the collection is not ordered.
          */
-        public void onClientsChanged(@NonNull Collection<TetheredClient> clients) {}
+        default void onClientsChanged(@NonNull Collection<TetheredClient> clients) {}
 
         /**
          * Called when tethering offload status changes.
@@ -832,19 +963,20 @@
          * <p>This will be called immediately after the callback is registered.
          * @param status The offload status.
          */
-        public void onOffloadStatusChanged(@TetherOffloadStatus int status) {}
+        default void onOffloadStatusChanged(@TetherOffloadStatus int status) {}
     }
 
     /**
      * Regular expressions used to identify tethering interfaces.
-     * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism.
+     * @hide
      */
-    @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public static class TetheringInterfaceRegexps {
         private final String[] mTetherableBluetoothRegexs;
         private final String[] mTetherableUsbRegexs;
         private final String[] mTetherableWifiRegexs;
 
+        /** @hide */
         public TetheringInterfaceRegexps(@NonNull String[] tetherableBluetoothRegexs,
                 @NonNull String[] tetherableUsbRegexs, @NonNull String[] tetherableWifiRegexs) {
             mTetherableBluetoothRegexs = tetherableBluetoothRegexs.clone();
@@ -1190,7 +1322,7 @@
         final RequestDispatcher dispatcher = new RequestDispatcher();
         final int ret = dispatcher.waitForResult((connector, listener) -> {
             try {
-                connector.isTetheringSupported(callerPkg, listener);
+                connector.isTetheringSupported(callerPkg, getAttributionTag(), listener);
             } catch (RemoteException e) {
                 throw new IllegalStateException(e);
             }
@@ -1213,14 +1345,15 @@
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "stopAllTethering caller:" + callerPkg);
 
-        getConnector(c -> c.stopAllTethering(callerPkg, new IIntResultListener.Stub() {
-            @Override
-            public void onResult(int resultCode) {
-                // TODO: add an API parameter to send result to caller.
-                // This has never been possible as stopAllTethering has always been void and never
-                // taken a callback object. The only indication that callers have is if the call
-                // results in a TETHER_STATE_CHANGE broadcast.
-            }
-        }));
+        getConnector(c -> c.stopAllTethering(callerPkg, getAttributionTag(),
+                new IIntResultListener.Stub() {
+                    @Override
+                    public void onResult(int resultCode) {
+                        // TODO: add an API parameter to send result to caller.
+                        // This has never been possible as stopAllTethering has always been void
+                        // and never taken a callback object. The only indication that callers have
+                        // is if the call results in a TETHER_STATE_CHANGE broadcast.
+                    }
+                }));
     }
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
index bf19d85f..c0280d3 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
@@ -25,6 +25,7 @@
 parcelable TetheringRequestParcel {
     int tetheringType;
     LinkAddress localIPv4Address;
+    LinkAddress staticClientAddress;
     boolean exemptFromEntitlementCheck;
     boolean showProvisioningUi;
 }
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
index c6efa41..e90a2cc 100644
--- a/packages/Tethering/jarjar-rules.txt
+++ b/packages/Tethering/jarjar-rules.txt
@@ -8,7 +8,6 @@
 rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1
 rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1
 rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1
-rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1
 rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
 rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
diff --git a/packages/Tethering/proguard.flags b/packages/Tethering/proguard.flags
index 1f83a66..051fbd1 100644
--- a/packages/Tethering/proguard.flags
+++ b/packages/Tethering/proguard.flags
@@ -1,5 +1,5 @@
 # Keep class's integer static field for MessageUtils to parsing their name.
--keep class com.android.server.connectivity.tethering.Tethering$TetherMasterSM {
+-keep class com.android.networkstack.tethering.Tethering$TetherMasterSM {
     static final int CMD_*;
     static final int EVENT_*;
 }
diff --git a/packages/Tethering/res/values-af/strings.xml b/packages/Tethering/res/values-af/strings.xml
index 1258805..056168b 100644
--- a/packages/Tethering/res/values-af/strings.xml
+++ b/packages/Tethering/res/values-af/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Verbinding of Wi-Fi-warmkol aktief"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tik om op te stel."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Verbinding is gedeaktiveer"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontak jou administrateur vir besonderhede"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Verbinding of warmkol is aktief"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Tik om op te stel."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Verbinding is gedeaktiveer"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontak jou administrateur vir besonderhede"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Warmkol- en verbindingstatus"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-am/strings.xml b/packages/Tethering/res/values-am/strings.xml
index 9c36192..ac468dd 100644
--- a/packages/Tethering/res/values-am/strings.xml
+++ b/packages/Tethering/res/values-am/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"መሰካት ወይም ገባሪ ድረስ ነጥብ"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"ለማዋቀር መታ ያድርጉ።"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"እንደ ሞደም መሰካት ተሰናክሏል"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"እንደ ሞደም መሰካት ወይም መገናኛ ነጥብ ገባሪ"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"ለማዋቀር መታ ያድርጉ።"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"እንደ ሞደም መሰካት ተሰናክሏል"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"መገናኛ ነጥብ እና እንደ ሞደም የመሰካት ሁኔታ"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ar/strings.xml b/packages/Tethering/res/values-ar/strings.xml
index 9f84ce4..7d5bad3 100644
--- a/packages/Tethering/res/values-ar/strings.xml
+++ b/packages/Tethering/res/values-ar/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"النطاق أو نقطة الاتصال نشطة"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"انقر للإعداد."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"تم إيقاف التوصيل"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"اتصل بالمشرف للحصول على التفاصيل"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"النطاق نشط أو نقطة الاتصال نشطة"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"انقر للإعداد."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"التوصيل متوقف."</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"تواصَل مع المشرف للحصول على التفاصيل."</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"حالة نقطة الاتصال والتوصيل"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-as/strings.xml b/packages/Tethering/res/values-as/strings.xml
index 8855822..0913504 100644
--- a/packages/Tethering/res/values-as/strings.xml
+++ b/packages/Tethering/res/values-as/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"ছেট আপ কৰিবলৈ টিপক।"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"টেডাৰিং অক্ষম কৰি থোৱা হৈছে"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"টে\'ডাৰিং অথবা হ\'টস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"ছেট আপ কৰিবলৈ টিপক।"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"টে\'ডাৰিঙৰ সুবিধাটো অক্ষম কৰি থোৱা হৈছে"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"হ’টস্প\'ট আৰু টে\'ডাৰিঙৰ স্থিতি"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-az/strings.xml b/packages/Tethering/res/values-az/strings.xml
index eba50eb..dce70da 100644
--- a/packages/Tethering/res/values-az/strings.xml
+++ b/packages/Tethering/res/values-az/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tezerinq və ya hotspot aktivdir"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Quraşdırmaq üçün tıklayın."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Birləşmə deaktivdir"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Məlumat üçün adminlə əlaqə saxlayın"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Birləşmə və ya hotspot aktivdir"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Ayarlamaq üçün toxunun."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Birləşmə deaktivdir"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Detallar üçün adminlə əlaqə saxlayın"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot &amp; birləşmə statusu"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-b+sr+Latn/strings.xml b/packages/Tethering/res/values-b+sr+Latn/strings.xml
index 5b0e488..b0774ec 100644
--- a/packages/Tethering/res/values-b+sr+Latn/strings.xml
+++ b/packages/Tethering/res/values-b+sr+Latn/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Aktivno povezivanje sa internetom preko mobilnog uređaja ili hotspot"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite da biste podesili."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Privezivanje je onemogućeno"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Potražite detalje od administratora"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Privezivanje ili hotspot je aktivan"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste podesili."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Privezivanje je onemogućeno"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Potražite detalje od administratora"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status hotspota i privezivanja"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-be/strings.xml b/packages/Tethering/res/values-be/strings.xml
index 5966c71..a8acebe 100644
--- a/packages/Tethering/res/values-be/strings.xml
+++ b/packages/Tethering/res/values-be/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"USB-мадэм або хот-спот Wi-Fi актыўныя"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Дакраніцеся, каб наладзіць."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Рэжым мадэма адключаны"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Мадэм або хот-спот актыўныя"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Дакраніцеся, каб наладзіць."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Рэжым мадэма выключаны"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Стан \"Хот-спот і мадэм\""</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-bg/strings.xml b/packages/Tethering/res/values-bg/strings.xml
index ed58d73..94fb2d8 100644
--- a/packages/Tethering/res/values-bg/strings.xml
+++ b/packages/Tethering/res/values-bg/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Има активна споделена връзка или безжична точка за достъп"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Докоснете, за да настроите."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Функцията за тетъринг е деактивирана"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Свържете се с администратора си за подробности"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Има активна споделена връзка или точка за достъп"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Докоснете, за да настроите."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Функцията за тетъринг е деактивирана"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Свържете се с администратора си за подробности"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Състояние на функцията за точка за достъп и тетъринг"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-bn/strings.xml b/packages/Tethering/res/values-bn/strings.xml
index 8d9880a..aea02b9 100644
--- a/packages/Tethering/res/values-bn/strings.xml
+++ b/packages/Tethering/res/values-bn/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"টিথারিং বা হটস্পট সক্রিয় আছে"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"সেট-আপ করার জন্য আলতো চাপুন৷"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"টিথারিং অক্ষম করা আছে"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"বিশদ বিবরণের জন্য প্রশাসকের সাথে যোগাযোগ করুন"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"টিথারিং বা হটস্পট চালু আছে"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"সেট-আপ করতে ট্যাপ করুন।"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"টিথারিং বন্ধ করা আছে"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"বিশদে জানতে অ্যাডমিনের সাথে যোগাযোগ করুন"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"হটস্পট ও টিথারিং স্ট্যাটাস"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-bs/strings.xml b/packages/Tethering/res/values-bs/strings.xml
index 2361b9d..de23272 100644
--- a/packages/Tethering/res/values-bs/strings.xml
+++ b/packages/Tethering/res/values-bs/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Uređaj dijeli vezu ili djeluje kao pristupna tačka"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite za postavke"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Povezivanje putem mobitela je onemogućeno"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontaktirajte svog administratora za dodatne detalje"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Aktivno je povezivanje putem mobitela ili pristupna tačka"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da postavite."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Povezivanje putem mobitela je onemogućeno"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontaktirajte svog administratora za detalje"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status pristupne tačke i povezivanja putem mobitela"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ca/strings.xml b/packages/Tethering/res/values-ca/strings.xml
index 6752b51..88b795c 100644
--- a/packages/Tethering/res/values-ca/strings.xml
+++ b/packages/Tethering/res/values-ca/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Compartició de xarxa o punt d\'accés Wi-Fi activat"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Toca per configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"La compartició de xarxa està desactivada"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacta amb el teu administrador per obtenir més informació"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Compartició de xarxa o punt d\'accés Wi‑Fi actius"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Toca per configurar."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"La compartició de xarxa està desactivada"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta amb el teu administrador per obtenir més informació"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estat del punt d\'accés Wi‑Fi i de la compartició de xarxa"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-cs/strings.xml b/packages/Tethering/res/values-cs/strings.xml
index 5fdd53a..8c1b83b 100644
--- a/packages/Tethering/res/values-cs/strings.xml
+++ b/packages/Tethering/res/values-cs/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Sdílené připojení nebo hotspot je aktivní."</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Klepnutím zahájíte nastavení."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering je zakázán"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"O podrobnosti požádejte administrátora"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering nebo hotspot je aktivní"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Klepnutím zahájíte nastavení."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering je zakázán"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"O podrobnosti požádejte administrátora"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stav hotspotu a tetheringu"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-da/strings.xml b/packages/Tethering/res/values-da/strings.xml
index 2775dfa..f413e70 100644
--- a/packages/Tethering/res/values-da/strings.xml
+++ b/packages/Tethering/res/values-da/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Netdeling eller hotspot er aktivt"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tryk for at konfigurere"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Netdeling er deaktiveret"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakt din administrator for at få oplysninger"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Netdeling eller hotspot er aktivt"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Tryk for at konfigurere."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Netdeling er deaktiveret"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakt din administrator for at få oplysninger"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status for hotspot og netdeling"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-de/strings.xml b/packages/Tethering/res/values-de/strings.xml
index 9046cd5..f057d78 100644
--- a/packages/Tethering/res/values-de/strings.xml
+++ b/packages/Tethering/res/values-de/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oder Hotspot aktiv"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Zum Einrichten tippen."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering ist deaktiviert"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Bitte wende dich für weitere Informationen an den Administrator"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering oder Hotspot aktiv"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Zum Einrichten tippen."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering ist deaktiviert"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Bitte wende dich für weitere Informationen an den Administrator"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot- und Tethering-Status"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-el/strings.xml b/packages/Tethering/res/values-el/strings.xml
index 3b9f537..b3c986b 100644
--- a/packages/Tethering/res/values-el/strings.xml
+++ b/packages/Tethering/res/values-el/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Πατήστε για ρύθμιση."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Η σύνδεση είναι απενεργοποιημένη"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Πατήστε για ρύθμιση."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Η σύνδεση είναι απενεργοποιημένη"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Κατάσταση σημείου πρόσβασης Wi-Fi και σύνδεσης"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-en-rAU/strings.xml b/packages/Tethering/res/values-en-rAU/strings.xml
index 56b88a5..769e0120 100644
--- a/packages/Tethering/res/values-en-rAU/strings.xml
+++ b/packages/Tethering/res/values-en-rAU/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-en-rCA/strings.xml b/packages/Tethering/res/values-en-rCA/strings.xml
index 56b88a5..769e0120 100644
--- a/packages/Tethering/res/values-en-rCA/strings.xml
+++ b/packages/Tethering/res/values-en-rCA/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-en-rGB/strings.xml b/packages/Tethering/res/values-en-rGB/strings.xml
index 56b88a5..769e0120 100644
--- a/packages/Tethering/res/values-en-rGB/strings.xml
+++ b/packages/Tethering/res/values-en-rGB/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-en-rIN/strings.xml b/packages/Tethering/res/values-en-rIN/strings.xml
index 56b88a5..769e0120 100644
--- a/packages/Tethering/res/values-en-rIN/strings.xml
+++ b/packages/Tethering/res/values-en-rIN/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-en-rXC/strings.xml b/packages/Tethering/res/values-en-rXC/strings.xml
index 7f47fc8..f1674be 100644
--- a/packages/Tethering/res/values-en-rXC/strings.xml
+++ b/packages/Tethering/res/values-en-rXC/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎Tethering or hotspot active‎‏‎‎‏‎"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎Tap to set up.‎‏‎‎‏‎"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎Tethering is disabled‎‏‎‎‏‎"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎Contact your admin for details‎‏‎‎‏‎"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‎Tethering or hotspot active‎‏‎‎‏‎"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎Tap to set up.‎‏‎‎‏‎"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎Tethering is disabled‎‏‎‎‏‎"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎Contact your admin for details‎‏‎‎‏‎"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎Hotspot &amp; tethering status‎‏‎‎‏‎"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-es-rUS/strings.xml b/packages/Tethering/res/values-es-rUS/strings.xml
index e4618b8..63689f4 100644
--- a/packages/Tethering/res/values-es-rUS/strings.xml
+++ b/packages/Tethering/res/values-es-rUS/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Anclaje a red o zona activa conectados"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Presiona para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Se inhabilitó la conexión mediante dispositivo portátil"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Para obtener más información, comunícate con el administrador"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión a red o hotspot conectados"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Presiona para configurar esta opción."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Se inhabilitó la conexión mediante dispositivo portátil"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Para obtener más información, comunícate con el administrador"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del hotspot y la conexión mediante dispositivo portátil"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-es/strings.xml b/packages/Tethering/res/values-es/strings.xml
index 8dc1575..9a34ed5 100644
--- a/packages/Tethering/res/values-es/strings.xml
+++ b/packages/Tethering/res/values-es/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Compartir conexión/Zona Wi-Fi activada"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Toca para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"La conexión compartida está inhabilitada"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ponte en contacto con el administrador para obtener más información"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida o punto de acceso activos"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"La conexión compartida está inhabilitada"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Solicita más información a tu administrador"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del punto de acceso y de la conexión compartida"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-et/strings.xml b/packages/Tethering/res/values-et/strings.xml
index 872c8a7..0970341 100644
--- a/packages/Tethering/res/values-et/strings.xml
+++ b/packages/Tethering/res/values-et/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Jagamine või kuumkoht on aktiivne"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Puudutage seadistamiseks."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Jagamine on keelatud"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Jagamine või kuumkoht on aktiivne"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Puudutage seadistamiseks."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Jagamine on keelatud"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Kuumkoha ja jagamise olek"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-eu/strings.xml b/packages/Tethering/res/values-eu/strings.xml
index 6c4605e..632019e 100644
--- a/packages/Tethering/res/values-eu/strings.xml
+++ b/packages/Tethering/res/values-eu/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Konexioa partekatzea edo sare publikoa aktibo"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Sakatu konfiguratzeko."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Desgaituta dago konexioa partekatzeko aukera"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Konexioa partekatzea edo wifi-gunea aktibo dago"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Sakatu konfiguratzeko."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Desgaituta dago konexioa partekatzeko aukera"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Wifi-gunearen eta konexioa partekatzeko eginbidearen egoera"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-fa/strings.xml b/packages/Tethering/res/values-fa/strings.xml
index bc2ee23..2e21c85 100644
--- a/packages/Tethering/res/values-fa/strings.xml
+++ b/packages/Tethering/res/values-fa/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"اشتراک‌گذاری اینترنت یا نقطه اتصال فعال"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"برای راه‌اندازی ضربه بزنید."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"اشتراک‌گذاری اینترنت غیرفعال است"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"برای جزئیات، با سرپرستتان تماس بگیرید"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"اشتراک‌گذاری اینترنت یا نقطه اتصال فعال"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"برای راه‌اندازی ضربه بزنید."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"اشتراک‌گذاری اینترنت غیرفعال است"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"برای جزئیات، با سرپرستتان تماس بگیرید"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"وضعیت نقطه اتصال و اشتراک‌گذاری اینترنت"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-fi/strings.xml b/packages/Tethering/res/values-fi/strings.xml
index ff0fca6..413db3f 100644
--- a/packages/Tethering/res/values-fi/strings.xml
+++ b/packages/Tethering/res/values-fi/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Internetin jakaminen tai yhteyspiste käytössä"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Määritä napauttamalla."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Yhteyden jakaminen poistettu käytöstä"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kysy lisätietoja järjestelmänvalvojalta."</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Yhteyden jakaminen tai hotspot käytössä"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Ota käyttöön napauttamalla."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Yhteyden jakaminen on poistettu käytöstä"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Pyydä lisätietoja järjestelmänvalvojalta"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspotin ja yhteyden jakamisen tila"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-fr-rCA/strings.xml b/packages/Tethering/res/values-fr-rCA/strings.xml
index 1f5df0ee..eb2e4ba 100644
--- a/packages/Tethering/res/values-fr-rCA/strings.xml
+++ b/packages/Tethering/res/values-fr-rCA/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Partage de connexion ou point d\'accès sans fil activé"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Touchez pour configurer."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Le partage de connexion est désactivé"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Communiquez avec votre administrateur pour obtenir plus de détails"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès sans fil activé"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Touchez pour configurer."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Communiquez avec votre administrateur pour obtenir plus de détails"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Point d\'accès et partage de connexion"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-fr/strings.xml b/packages/Tethering/res/values-fr/strings.xml
index daf7c9d..22259c5 100644
--- a/packages/Tethering/res/values-fr/strings.xml
+++ b/packages/Tethering/res/values-fr/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Partage de connexion ou point d\'accès sans fil activé"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Appuyez ici pour configurer."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Le partage de connexion est désactivé"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Pour en savoir plus, contactez votre administrateur"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès activé"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Appuyez pour effectuer la configuration."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Pour en savoir plus, contactez votre administrateur"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"État du point d\'accès et du partage de connexion"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-gl/strings.xml b/packages/Tethering/res/values-gl/strings.xml
index 0d16a1d..ded82fc 100644
--- a/packages/Tethering/res/values-gl/strings.xml
+++ b/packages/Tethering/res/values-gl/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Conexión compartida ou zona wifi activada"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tocar para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"A conexión compartida está desactivada"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacta co administrador para obter información"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida ou zona wifi activada"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"A conexión compartida está desactivada"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta co administrador para obter información"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado da zona wifi e da conexión compartida"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-gu/strings.xml b/packages/Tethering/res/values-gu/strings.xml
index 9d6b02f..7cbbc2d 100644
--- a/packages/Tethering/res/values-gu/strings.xml
+++ b/packages/Tethering/res/values-gu/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ટિથરિંગ અથવા હૉટસ્પૉટ સક્રિય"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"સેટ કરવા માટે ટૅપ કરો."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ટિથરિંગ અક્ષમ કરેલ છે"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ઇન્ટરનેટ શેર કરવાની સુવિધા અથવા હૉટસ્પૉટ સક્રિય છે"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"સેટઅપ કરવા માટે ટૅપ કરો."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરી છે"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"હૉટસ્પૉટ અને ઇન્ટરનેટ શેર કરવાની સુવિધાનું સ્ટેટસ"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-hi/strings.xml b/packages/Tethering/res/values-hi/strings.xml
index 9c29d9a..08af81b 100644
--- a/packages/Tethering/res/values-hi/strings.xml
+++ b/packages/Tethering/res/values-hi/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग या हॉटस्‍पॉट सक्रिय"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"सेट करने के लिए टैप करें."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग अक्षम है"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिंग या हॉटस्पॉट चालू है"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"सेट अप करने के लिए टैप करें."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिंग बंद है"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हॉटस्पॉट और टेदरिंग की स्थिति"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-hr/strings.xml b/packages/Tethering/res/values-hr/strings.xml
index d0d25bb..827c135 100644
--- a/packages/Tethering/res/values-hr/strings.xml
+++ b/packages/Tethering/res/values-hr/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Ograničenje ili aktivan hotspot"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite da biste postavili."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Modemsko je povezivanje onemogućeno"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Obratite se administratoru da biste saznali pojedinosti"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Modemsko povezivanje ili žarišna točka aktivni"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste postavili."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Modemsko je povezivanje onemogućeno"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Obratite se administratoru da biste saznali pojedinosti"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status žarišne točke i modemskog povezivanja"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-hu/strings.xml b/packages/Tethering/res/values-hu/strings.xml
index 3129659..eb68d6b 100644
--- a/packages/Tethering/res/values-hu/strings.xml
+++ b/packages/Tethering/res/values-hu/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Megosztás vagy aktív hotspot"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Koppintson a beállításhoz."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Az internetmegosztás le van tiltva"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"A részletekért forduljon rendszergazdájához"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Megosztás vagy aktív hotspot"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Koppintson a beállításhoz."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Az internetmegosztás le van tiltva"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"A részletekért forduljon rendszergazdájához"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot és internetmegosztás állapota"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-hy/strings.xml b/packages/Tethering/res/values-hy/strings.xml
index 8ba6435..912941e 100644
--- a/packages/Tethering/res/values-hy/strings.xml
+++ b/packages/Tethering/res/values-hy/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Մոդեմի ռեժիմը միացված է"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Հպեք՝ կարգավորելու համար:"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Մոդեմի ռեժիմն անջատված է"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Մոդեմի ռեժիմը միացված է"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Հպեք՝ կարգավորելու համար։"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Մոդեմի ռեժիմն անջատված է"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Թեժ կետի և մոդեմի ռեժիմի կարգավիճակը"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-in/strings.xml b/packages/Tethering/res/values-in/strings.xml
index 1e093ab..a4e175a 100644
--- a/packages/Tethering/res/values-in/strings.xml
+++ b/packages/Tethering/res/values-in/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering (Penambatan) atau hotspot aktif"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Ketuk untuk menyiapkan."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering dinonaktifkan"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hubungi admin untuk mengetahui detailnya"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering atau hotspot aktif"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Ketuk untuk menyiapkan."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering dinonaktifkan"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hubungi admin untuk mengetahui detailnya"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status hotspot &amp; tethering"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-is/strings.xml b/packages/Tethering/res/values-is/strings.xml
index f5769d5..e9f6670 100644
--- a/packages/Tethering/res/values-is/strings.xml
+++ b/packages/Tethering/res/values-is/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Kveikt á tjóðrun eða aðgangsstað"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Ýttu til að setja upp."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Slökkt er á tjóðrun"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Kveikt á tjóðrun eða aðgangsstað"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Ýttu til að setja upp."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Slökkt er á tjóðrun"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Staða heits reits og tjóðrunar"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-it/strings.xml b/packages/Tethering/res/values-it/strings.xml
index e0b37243..ffb9196 100644
--- a/packages/Tethering/res/values-it/strings.xml
+++ b/packages/Tethering/res/values-it/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oppure hotspot attivo"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tocca per impostare."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering disattivato"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contatta il tuo amministratore per avere informazioni dettagliate"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Hotspot o tethering attivo"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Tocca per impostare."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering disattivato"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contatta il tuo amministratore per avere informazioni dettagliate"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stato hotspot e tethering"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-iw/strings.xml b/packages/Tethering/res/values-iw/strings.xml
index c002c44..7adcb47 100644
--- a/packages/Tethering/res/values-iw/strings.xml
+++ b/packages/Tethering/res/values-iw/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"שיתוף אינטרנט פעיל"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"הקש כדי להגדיר."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"שיתוף האינטרנט בין ניידים מושבת"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"לפרטים, יש לפנות למנהל המערכת"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"נקודה לשיתוף אינטרנט או שיתוף אינטרנט בין מכשירים: בסטטוס פעיל"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"יש להקיש כדי להגדיר."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"שיתוף האינטרנט בין מכשירים מושבת"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"לפרטים, יש לפנות למנהל המערכת"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"סטטוס של נקודה לשיתוף אינטרנט ושיתוף אינטרנט בין מכשירים"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ja/strings.xml b/packages/Tethering/res/values-ja/strings.xml
index 314bde0..f68a730 100644
--- a/packages/Tethering/res/values-ja/strings.xml
+++ b/packages/Tethering/res/values-ja/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"テザリングまたはアクセスポイントが有効です"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"タップしてセットアップします。"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"テザリングは無効に設定されています"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"詳しくは、管理者にお問い合わせください"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"テザリングまたはアクセス ポイントが有効です"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"タップしてセットアップします。"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"テザリングは無効に設定されています"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"詳しくは、管理者にお問い合わせください"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"アクセス ポイントとテザリングのステータス"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ka/strings.xml b/packages/Tethering/res/values-ka/strings.xml
index 7bbd81d..7c22e82 100644
--- a/packages/Tethering/res/values-ka/strings.xml
+++ b/packages/Tethering/res/values-ka/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"შეეხეთ დასაყენებლად."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ტეტერინგი გათიშულია"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"შეეხეთ დასაყენებლად."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ტეტერინგი გათიშულია"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"უსადენო ქსელის და ტეტერინგის სტატუსი"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-kk/strings.xml b/packages/Tethering/res/values-kk/strings.xml
index 7fd87a1..0857d06 100644
--- a/packages/Tethering/res/values-kk/strings.xml
+++ b/packages/Tethering/res/values-kk/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Тетеринг немесе хотспот қосулы"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Реттеу үшін түртіңіз."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Тетеринг өшірілді"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Мәліметтерді әкімшіден алыңыз"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Тетеринг немесе хотспот қосулы"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Реттеу үшін түртіңіз."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Тетеринг өшірілді."</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Мәліметтерді әкімшіден алыңыз."</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Хотспот және тетеринг күйі"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-km/strings.xml b/packages/Tethering/res/values-km/strings.xml
index 2f85224..536e3d1 100644
--- a/packages/Tethering/res/values-km/strings.xml
+++ b/packages/Tethering/res/values-km/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"ប៉ះដើម្បីកំណត់"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ការភ្ជាប់​ត្រូវបានបិទ"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នកសម្រាប់​ព័ត៌មានលម្អិត"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ការភ្ជាប់ ឬហតស្ប៉ត​កំពុងដំណើរការ"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"ចុច​ដើម្បី​រៀបចំ។"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ការភ្ជាប់​ត្រូវបានបិទ"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"ទាក់ទងអ្នកគ្រប់គ្រង​របស់អ្នក ដើម្បីទទួលបានព័ត៌មានលម្អិត"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ស្ថានភាពនៃការភ្ជាប់ និងហតស្ប៉ត"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-kn/strings.xml b/packages/Tethering/res/values-kn/strings.xml
index f11a83ea..32f5492 100644
--- a/packages/Tethering/res/values-kn/strings.xml
+++ b/packages/Tethering/res/values-kn/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್‌ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"ಸೆಟಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ಹಾಟ್‌ಸ್ಪಾಟ್ ಮತ್ತು ಟೆಥರಿಂಗ್‌ ಸ್ಥಿತಿ"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ko/strings.xml b/packages/Tethering/res/values-ko/strings.xml
index 57f24f5..156b247 100644
--- a/packages/Tethering/res/values-ko/strings.xml
+++ b/packages/Tethering/res/values-ko/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"테더링 또는 핫스팟 사용"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"설정하려면 탭하세요."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"테더링이 사용 중지됨"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"자세한 정보는 관리자에게 문의하세요."</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"테더링 또는 핫스팟 사용"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"설정하려면 탭하세요."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"테더링이 사용 중지됨"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"자세한 정보는 관리자에게 문의하세요."</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"핫스팟 및 테더링 상태"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ky/strings.xml b/packages/Tethering/res/values-ky/strings.xml
index 7985485..18ee5fd 100644
--- a/packages/Tethering/res/values-ky/strings.xml
+++ b/packages/Tethering/res/values-ky/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Жалгаштыруу же хотспот жандырылган"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Жөндөө үчүн таптап коюңуз."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Жалгаштыруу функциясы өчүрүлгөн"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Модем режими күйүп турат"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Жөндөө үчүн таптап коюңуз."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Телефонду модем катары колдонууга болбойт"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Байланыш түйүнүнүн жана модем режиминин статусу"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-lo/strings.xml b/packages/Tethering/res/values-lo/strings.xml
index 78f1585..b127670 100644
--- a/packages/Tethering/res/values-lo/strings.xml
+++ b/packages/Tethering/res/values-lo/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ເປີດ​ການ​ປ່ອຍ​ສັນຍານ ຫຼື​ຮັອດສະປອດ​ແລ້ວ"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"ແຕະເພື່ອຕັ້ງຄ່າ."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ເປີດການປ່ອຍສັນຍານ ຫຼື ຮັອດສະປອດແລ້ວ"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"ແຕະເພື່ອຕັ້ງຄ່າ."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ສະຖານະຮັອດສະປອດ ແລະ ການປ່ອຍສັນຍານ"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-lt/strings.xml b/packages/Tethering/res/values-lt/strings.xml
index ebff8ac..8427baf 100644
--- a/packages/Tethering/res/values-lt/strings.xml
+++ b/packages/Tethering/res/values-lt/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Susietas ar aktyvus"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Palieskite, kad nustatytumėte."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Įrenginio kaip modemo naudojimas išjungtas"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Įrenginys naudojamas kaip modemas arba įjungtas viešosios interneto prieigos taškas"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Palieskite, kad nustatytumėte."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Įrenginio kaip modemo naudojimas išjungtas"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Viešosios interneto prieigos taško ir įrenginio kaip modemo naudojimo būsena"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-lv/strings.xml b/packages/Tethering/res/values-lv/strings.xml
index 54d0048..aa2d699 100644
--- a/packages/Tethering/res/values-lv/strings.xml
+++ b/packages/Tethering/res/values-lv/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Piesaiste vai tīklājs ir aktīvs."</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Pieskarieties, lai iestatītu."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Piesaiste ir atspējota"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Piesaiste vai tīklājs ir aktīvs."</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Pieskarieties, lai to iestatītu."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Piesaiste ir atspējota"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Tīklāja un piesaistes statuss"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml
new file mode 100644
index 0000000..052ca09
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-af/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Warmkol het nie internet nie"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Toestelle kan nie aan internet koppel nie"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Skakel warmkol af"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Warmkol is aan"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Bykomende heffings kan geld terwyl jy swerf"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Gaan voort"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml
new file mode 100644
index 0000000..0518c5a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-am/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"መገናኛ ነጥቡ በይነመረብ የለውም"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"መሣሪያዎች ከበይነመረብ ጋር መገናኘት አይችሉም"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"መገናኛ ነጥብ ያጥፉ"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"የመገናኛ ነጥብ በርቷል"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ቀጥል"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml
new file mode 100644
index 0000000..40eb9a74
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ar/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"متابعة"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml
new file mode 100644
index 0000000..4c57f21
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-as/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"হটস্পটৰ কোনো ইণ্টাৰনেট নাই"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"ডিভাইচসমূহ ইণ্টাৰনেটৰ সৈতে সংযোগ কৰিব নোৱাৰি"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"হটস্পট অফ কৰক"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"হটস্পট অন হৈ আছে"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"অব্যাহত ৰাখক"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml
new file mode 100644
index 0000000..2610ab1
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-az/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspotun internetə girişi yoxdur"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Cihazlar internetə qoşula bilmir"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot\'u deaktiv edin"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot aktivdir"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Davam edin"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..7b032ba
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-b+sr+Latn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nema pristup internetu"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Uređaji ne mogu da se povežu na internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Isključi hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot je uključen"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Možda važe dodatni troškovi u romingu"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Nastavi"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml
new file mode 100644
index 0000000..2362a1e
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-be/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хот-спот не падключаны да інтэрнэту"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Прылады не могуць падключацца да інтэрнэту"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Выключыць хот-спот"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Хот-спот уключаны"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Працягнуць"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml
new file mode 100644
index 0000000..6ef1b0b
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-bg/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точката за достъп няма връзка с интернет"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Устройствата не могат да се свържат с интернет"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Изключване на точката за достъп"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точката за достъп е включена"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Напред"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml
new file mode 100644
index 0000000..7f9efba
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-bn/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"চালিয়ে যান"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml
new file mode 100644
index 0000000..7539736
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-bs/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Pristupna tačka nema internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Uređaji se ne mogu povezati na internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Isključi pristupnu tačku"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Pristupna tačka je uključena"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Primjenjuju se dodatne tarife u romingu"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Nastavi"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml
new file mode 100644
index 0000000..e3ad666
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ca/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"El punt d\'accés Wi‑Fi no té accés a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Els dispositius no es poden connectar a Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactiva el punt d\'accés Wi‑Fi"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"El punt d\'accés Wi‑Fi està activat"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"És possible que s\'apliquin costos addicionals en itinerància"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continua"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml
new file mode 100644
index 0000000..f099281
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-cs/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nemá připojení k internetu"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Zařízení se nemohou připojit k internetu"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Vypnout hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot je aktivní"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Při roamingu mohou být účtovány dodatečné poplatky"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Pokračovat"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml
new file mode 100644
index 0000000..1fb2374
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-da/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspottet har intet internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Enheder kan ikke oprette forbindelse til internettet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Deaktiver hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspottet er aktiveret"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Der opkræves muligvis yderligere gebyrer ved roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Fortsæt"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml
new file mode 100644
index 0000000..56d1d1d
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-de/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot ist nicht mit dem Internet verbunden"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Geräte können nicht mit dem Internet verbunden werden"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot deaktivieren"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot aktiviert"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Für das Roaming können zusätzliche Gebühren anfallen"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Weiter"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml
new file mode 100644
index 0000000..674f1f6
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-el/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Το σημείο πρόσβασης Wi-Fi δεν έχει πρόσβαση στο διαδίκτυο."</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Δεν είναι η δυνατή η σύνδεση των συσκευών στο διαδίκτυο."</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Απενεργοποίηση σημείου πρόσβασης Wi-Fi"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Σημείο πρόσβασης Wi-Fi ενεργό"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Συνέχεια"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml
new file mode 100644
index 0000000..3046a37
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-en-rAU/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml
new file mode 100644
index 0000000..3046a37
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-en-rCA/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml
new file mode 100644
index 0000000..3046a37
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-en-rGB/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml
new file mode 100644
index 0000000..3046a37
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-en-rIN/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Devices can’t connect to Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Turn off hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Additional charges may apply while roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continue"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml
new file mode 100644
index 0000000..20c9b94
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-en-rXC/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎Hotspot has no internet‎‏‎‎‏‎"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‏‎Devices can’t connect to internet‎‏‎‎‏‎"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎Turn off hotspot‎‏‎‎‏‎"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎Hotspot is on‎‏‎‎‏‎"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎Additional charges may apply while roaming‎‏‎‎‏‎"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎Continue‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml
new file mode 100644
index 0000000..196303f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-es-rUS/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"El hotspot no tiene Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Los dispositivos no pueden conectarse a Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactiva el hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"El hotspot está activado"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Es posible que apliquen cargos adicionales por roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml
new file mode 100644
index 0000000..cac5b23
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-es/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"El punto de acceso no tiene conexión a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Los dispositivos no se pueden conectar a Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactivar punto de acceso"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Zona Wi-Fi activada"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Puede que se apliquen cargos adicionales en itinerancia"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml
new file mode 100644
index 0000000..ff8dde5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-et/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Kuumkohal puudub Interneti-ühendus"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Seadmed ei saa Internetiga ühendust luua"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Lülita kuumkoht välja"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Kuumkoht on sees"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Rändluse kasutamisega võivad kaasneda lisatasud"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Jätka"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml
new file mode 100644
index 0000000..1758a4f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-eu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Sare publikoak ez du Interneteko konexiorik"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Gailuak ezin dira konektatu Internetera"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desaktibatu sare publikoa"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Sare publikoa aktibatuta dago"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritza moduan"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Egin aurrera"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml
new file mode 100644
index 0000000..79e3ef1
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-fa/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"نقطه اتصال به اینترنت دسترسی ندارد"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"دستگاه‌ها به اینترنت متصل نشدند"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"نقطه اتصال را خاموش کنید"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"نقطه اتصال روشن است"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ادامه"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml
new file mode 100644
index 0000000..64921bc
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-fi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspotilla ei ole internetyhteyttä"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Laitteet eivät voi yhdistää internetiin"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Laita hotspot pois päältä"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot on päällä"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Roaming voi aiheuttaa lisämaksuja"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Jatka"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml
new file mode 100644
index 0000000..eda7b59
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-fr-rCA/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Le point d\'accès n\'est pas connecté à Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Appareils non connectés à Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Désactiver le point d\'accès"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Le point d\'accès est activé"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuer"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml
new file mode 100644
index 0000000..eda7b59
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-fr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Le point d\'accès n\'est pas connecté à Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Appareils non connectés à Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Désactiver le point d\'accès"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Le point d\'accès est activé"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuer"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml
new file mode 100644
index 0000000..c163c61
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-gl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"A zona wifi non ten acceso a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Os dispositivos non se poden conectar a Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desactivar zona wifi"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"A zona wifi está activada"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Pódense aplicar cargos adicionais en itinerancia"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml
new file mode 100644
index 0000000..0f4d26a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-gu/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"આગળ વધો"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml
new file mode 100644
index 0000000..a244200
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-hi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"हॉटस्पॉट से इंटरनेट नहीं चल रहा"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"डिवाइस इंटरनेट से कनेक्ट नहीं हो पा रहे"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"हॉटस्पॉट बंद करें"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"हॉटस्पॉट चालू है"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"जारी रखें"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml
new file mode 100644
index 0000000..41618af
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-hr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Žarišna točka nema pristup internetu"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Uređaji se ne mogu povezati s internetom"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Isključi žarišnu točku"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Žarišna je točka uključena"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"U roamingu su mogući dodatni troškovi"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Nastavi"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml
new file mode 100644
index 0000000..39b7a69
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-hu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"A hotspot nem csatlakozik az internethez"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Az eszközök nem tudnak csatlakozni az internethez"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot kikapcsolása"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"A hotspot be van kapcsolva"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Roaming során további díjak léphetnek fel"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tovább"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml
new file mode 100644
index 0000000..c14ae10a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-hy/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Թեժ կետը միացված չէ ինտերնետին"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Սարքերը չեն կարողանում միանալ ինտերնետին"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Անջատել թեժ կետը"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Թեժ կետը միացված է"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Շարունակել"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml
new file mode 100644
index 0000000..4998474
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-in/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot tidak memiliki internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Perangkat tidak dapat tersambung ke internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Nonaktifkan hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot aktif"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Biaya tambahan mungkin berlaku saat roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Lanjutkan"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml
new file mode 100644
index 0000000..82a7d01
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-is/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Heitur reitur er ekki nettengdur"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Tæki geta ekki tengst við internetið"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Slökkva á heitum reit"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Kveikt er á heitum reit"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Viðbótargjöld kunna að eiga við í reiki"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Halda áfram"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml
new file mode 100644
index 0000000..a10d511
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-it/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"L\'hotspot non ha accesso a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"I dispositivi non possono connettersi a Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Disattiva l\'hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot attivo"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Potrebbero essere addebitati costi aggiuntivi durante il roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continua"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml
new file mode 100644
index 0000000..80807bc
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-iw/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"לנקודה לשיתוף אינטרנט אין חיבור לאינטרנט"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"המכשירים לא יכולים להתחבר לאינטרנט"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"כיבוי הנקודה לשיתוף אינטרנט"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"הנקודה לשיתוף אינטרנט פועלת"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ייתכנו חיובים נוספים בעת נדידה"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"המשך"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml
new file mode 100644
index 0000000..0e21a7f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ja/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"アクセス ポイントがインターネットに接続されていません"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"デバイスをインターネットに接続できません"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"アクセス ポイントを OFF にする"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"アクセス ポイント: ON"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ローミング時に追加料金が発生することがあります"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"続行"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml
new file mode 100644
index 0000000..6d3b548
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ka/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"უსადენო ქსელს არ აქვს ინტერნეტზე წვდომა"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"მოწყობილობები ვერ უკავშირდება ინტერნეტს"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"გამორთეთ უსადენო ქსელი"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"უსადენო ქსელი ჩართულია"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"გაგრძელება"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml
new file mode 100644
index 0000000..985fc3f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-kk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хотспотта интернет жоқ"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Құрылғылар интернетке қосылмайды"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Хотспотты өшіру"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Хотспот қосулы"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Жалғастыру"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml
new file mode 100644
index 0000000..03b5cb6
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-km/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"ហតស្ប៉ត​មិនមាន​អ៊ីនធឺណិត​ទេ"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"ឧបករណ៍​មិនអាច​ភ្ជាប់​អ៊ីនធឺណិត​បានទេ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"បិទ​ហតស្ប៉ត"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ហតស្ប៉ត​ត្រូវបានបើក"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"បន្ត"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml
new file mode 100644
index 0000000..0427a77
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-kn/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ಮುಂದುವರಿಸಿ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml
new file mode 100644
index 0000000..9218e9a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ko/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"핫스팟이 인터넷에 연결되지 않음"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"기기를 인터넷에 연결할 수 없음"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"핫스팟 사용 중지"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"핫스팟 사용 중"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"계속"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml
new file mode 100644
index 0000000..bc3d555
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ky/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хотспоттун Интернети жок"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Түзмөктөр Интернетке туташпай жатат"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Туташуу түйүнүн өчүрүү"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Кошулуу түйүнү күйүк"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Роумингде кошумча акы алынышы мүмкүн"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Улантуу"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml
new file mode 100644
index 0000000..06dcbcb
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-lo/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ສືບຕໍ່"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml
new file mode 100644
index 0000000..db5178b
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-lt/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Nėra viešosios interneto prieigos taško interneto ryšio"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Įrenginiams nepavyksta prisijungti prie interneto"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Išjungti viešosios interneto prieigos tašką"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Viešosios interneto prieigos taškas įjungtas"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tęsti"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml
new file mode 100644
index 0000000..c712173
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-lv/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Tīklājam nav interneta savienojuma"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Ierīces nevar izveidot savienojumu ar internetu"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Izslēgt tīklāju"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Tīklājs ir ieslēgts"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tālāk"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml
new file mode 100644
index 0000000..aa44909
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-mk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точката на пристап нема интернет"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Уредите не може да се поврзат на интернет"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Исклучи ја точката на пристап"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точката на пристап е вклучена"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"При роаминг може да се наплатат дополнителни трошоци"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Продолжи"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml
new file mode 100644
index 0000000..0ef956a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ml/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"തുടരുക"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml
new file mode 100644
index 0000000..417213f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-mn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Сүлжээний цэг дээр интернэт алга байна"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Төхөөрөмжүүд нь интернэтэд холбогдох боломжгүй байна"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Сүлжээний цэгийг унтраах"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Сүлжээний цэг асаалттай байна"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Үргэлжлүүлэх"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml
new file mode 100644
index 0000000..2ed153f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-mr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"हॉटस्पॉटला इंटरनेट नाही"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"डिव्हाइस इंटरनेटला कनेक्ट करू शकत नाहीत"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"हॉटस्पॉट बंद करा"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"हॉटस्पॉट सुरू आहे"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"सुरू ठेवा"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml
new file mode 100644
index 0000000..50817fd
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ms/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Tempat liputan tiada Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Peranti tidak dapat menyambung kepada Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Matikan tempat liputan"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Tempat liputan dihidupkan"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Caj tambahan mungkin digunakan semasa perayauan"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Teruskan"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml
new file mode 100644
index 0000000..c0d70e3
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-my/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"ဟော့စပေါ့တွင် အင်တာနက်မရှိပါ"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"စက်များက အင်တာနက်ချိတ်ဆက်၍ မရပါ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ဟော့စပေါ့ ပိတ်ရန်"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ဟော့စပေါ့ ဖွင့်ထားသည်"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ရှေ့ဆက်ရန်"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml
new file mode 100644
index 0000000..1e7f1c6
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-nb/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Wi-Fi-sonen har ikke internettilgang"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Enheter kan ikke koble til internett"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Slå av Wi-Fi-sonen"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Wi-Fi-sonen er på"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ytterligere kostnader kan påløpe under roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Fortsett"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml
new file mode 100644
index 0000000..fadd357
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ne/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"जारी राख्नुहोस्"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml
new file mode 100644
index 0000000..bf14a0f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-nl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot heeft geen internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Apparaten kunnen geen verbinding maken met internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot uitschakelen"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot is ingeschakeld"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Doorgaan"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml
new file mode 100644
index 0000000..1cdfce0
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-or/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ଜାରି ରଖନ୍ତୁ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml
new file mode 100644
index 0000000..93402c3
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-pa/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ਜਾਰੀ ਰੱਖੋ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml
new file mode 100644
index 0000000..8becd07
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-pl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nie ma internetu"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Urządzenia nie mogą połączyć się z internetem"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Wyłącz hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot jest włączony"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Dalej"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml
new file mode 100644
index 0000000..8e01736
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-pt-rBR/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"O ponto de acesso não tem conexão com a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Não foi possível conectar os dispositivos à Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desativar ponto de acesso"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"O ponto de acesso está ativado"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Pode haver cobranças extras durante o roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml
new file mode 100644
index 0000000..2356379
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-pt-rPT/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"A zona Wi-Fi não tem Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Não é possível ligar os dispositivos à Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desativar zona Wi-Fi"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"A zona Wi-Fi está ativada"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Podem aplicar-se custos adicionais em roaming."</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml
new file mode 100644
index 0000000..8e01736
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-pt/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"O ponto de acesso não tem conexão com a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Não foi possível conectar os dispositivos à Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Desativar ponto de acesso"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"O ponto de acesso está ativado"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Pode haver cobranças extras durante o roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuar"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml
new file mode 100644
index 0000000..2e62bd6
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ro/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspotul nu are internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Dispozitivele nu se pot conecta la internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Dezactivați hotspotul"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspotul este activ"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Se pot aplica taxe suplimentare pentru roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Continuați"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml
new file mode 100644
index 0000000..69f8c59
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ru/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точка доступа не подключена к Интернету"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Не удается подключить устройства к Интернету"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Отключить точку доступа"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точка доступа включена"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Продолжить"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml
new file mode 100644
index 0000000..632748a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-si/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"හොට්ස්පොට් හට අන්තර්ජාලය නැත"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"උපාංගවලට අන්තර්ජාලයට සම්බන්ධ විය නොහැකිය"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"හොට්ස්පොට් ක්‍රියාවිරහිත කරන්න"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"හොට්ස්පොට් ක්‍රියාත්මකයි"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ඉදිරියට යන්න"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml
new file mode 100644
index 0000000..247fc1b
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-sk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot nemá internetové pripojenie"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Zariadenia sa nedajú pripojiť k internetu"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Vypnúť hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot je zapnutý"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Pokračovať"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml
new file mode 100644
index 0000000..ed22372
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-sl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Dostopna točka nima internetne povezave"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Naprave ne morejo vzpostaviti internetne povezave"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Izklopi dostopno točko"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Dostopna točka je vklopljena"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Med gostovanjem lahko nastanejo dodatni stroški"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Naprej"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml
new file mode 100644
index 0000000..4bfab6e
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-sq/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Zona e qasjes për internet nuk ka internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Pajisjet nuk mund të lidhen me internetin"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Çaktivizo zonën e qasjes për internet"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Zona e qasjes për internet është aktive"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Mund të zbatohen tarifime shtesë kur je në roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Vazhdo"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml
new file mode 100644
index 0000000..478d53a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-sr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Хотспот нема приступ интернету"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Уређаји не могу да се повежу на интернет"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Искључи хотспот"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Хотспот је укључен"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Можда важе додатни трошкови у ромингу"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Настави"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml
new file mode 100644
index 0000000..a793ed6
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-sv/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Surfzonen har ingen internetanslutning"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Enheterna har ingen internetanslutning"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Inaktivera surfzon"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Surfzonen är aktiverad"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Ytterligare avgifter kan tillkomma vid roaming"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Fortsätt"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml
new file mode 100644
index 0000000..3fe09fc
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-sw/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Mtandao pepe hauna intaneti"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Vifaa vimeshindwa kuunganisha kwenye intaneti"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Zima mtandao pepe"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Mtandaopepe umewashwa"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Endelea"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml
new file mode 100644
index 0000000..63c28c6
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ta/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"தொடர்க"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml
new file mode 100644
index 0000000..2cf579c
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-te/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"కొనసాగించు"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml
new file mode 100644
index 0000000..3837002
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-th/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"ฮอตสปอตไม่ได้เชื่อมต่ออินเทอร์เน็ต"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"อุปกรณ์เชื่อมต่ออินเทอร์เน็ตไม่ได้"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ปิดฮอตสปอต"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ฮอตสปอตเปิดอยู่"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"ต่อไป"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml
new file mode 100644
index 0000000..208f893
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-tl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Walang internet ang hotspot"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Hindi makakonekta sa internet ang mga device"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"I-off ang hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Naka-on ang hotspot"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Ituloy"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml
new file mode 100644
index 0000000..3482faf
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-tr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Hotspot\'un internet bağlantısı yok"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Cihazlar internete bağlanamıyor"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Hotspot\'u kapat"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Hotspot açık"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Dolaşım sırasında ek ücretler uygulanabilir"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Devam"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml
new file mode 100644
index 0000000..dea31144
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-uk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Точка доступу не підключена до Інтернету"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Не вдається підключити пристрої до Інтернету"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Вимкнути точку доступу"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Точку доступу ввімкнено"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"У роумінгу може стягуватися додаткова плата"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Продовжити"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml
new file mode 100644
index 0000000..09bc0c9
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-ur/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"ہاٹ اسپاٹ میں انٹرنیٹ نہیں ہے"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"آلات انٹرنیٹ سے منسلک نہیں ہو سکتے"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"ہاٹ اسپاٹ آف کریں"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"ہاٹ اسپاٹ آن ہے"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"جاری رکھیں"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml
new file mode 100644
index 0000000..5231c5f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-uz/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for no_upstream_notification_title (6246167638178412020) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_message (5010177541603431003) -->
+    <skip />
+    <!-- no translation found for no_upstream_notification_disable_button (2613861474440640595) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_title (3633925855626231152) -->
+    <skip />
+    <!-- no translation found for upstream_roaming_notification_message (1396837704184358258) -->
+    <skip />
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Davom etish"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml
new file mode 100644
index 0000000..bf4ee10
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-vi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"Điểm phát sóng không có kết nối Internet"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Các thiết bị không thể kết nối Internet"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Tắt điểm phát sóng"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"Điểm phát sóng đang bật"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Tiếp tục"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml
new file mode 100644
index 0000000..38c2563
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-zh-rCN/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"热点无法访问互联网"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"设备无法连接到互联网"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"关闭热点"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"热点已开启"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"漫游时可能会产生额外的费用"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"继续"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml
new file mode 100644
index 0000000..3bb52e4
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-zh-rHK/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"熱點沒有互聯網連線"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"裝置無法連線至互聯網"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"關閉熱點"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"已開啟熱點"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"漫遊時可能需要支付額外費用"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"繼續"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml
new file mode 100644
index 0000000..298c3ea
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-zh-rTW/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"無線基地台沒有網際網路連線"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"裝置無法連上網際網路"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"關閉無線基地台"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"無線基地台已開啟"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"使用漫遊服務可能須支付額外費用"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"繼續"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml b/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml
new file mode 100644
index 0000000..3dc0078
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04-zu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="6246167638178412020">"I-Hotspot ayina-inthanethi"</string>
+    <string name="no_upstream_notification_message" msgid="5010177541603431003">"Amadivayisi awakwazi ukuxhuma ku-inthanethi"</string>
+    <string name="no_upstream_notification_disable_button" msgid="2613861474440640595">"Vala i-hotspot"</string>
+    <string name="upstream_roaming_notification_title" msgid="3633925855626231152">"I-Hotspot ivuliwe"</string>
+    <string name="upstream_roaming_notification_message" msgid="1396837704184358258">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string>
+    <string name="upstream_roaming_notification_continue_button" msgid="5324117849715705638">"Qhubeka"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml
new file mode 100644
index 0000000..19d659c
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Verbinding het nie internet nie"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Toestelle kan nie koppel nie"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Skakel verbinding af"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Warmkol of verbinding is aan"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Bykomende heffings kan geld terwyl jy swerf"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml
new file mode 100644
index 0000000..8995430
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ማስተሳሰር ምንም በይነመረብ የለውም"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"መሣሪያዎችን ማገናኘት አይቻልም"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ማስተሳሰርን አጥፋ"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml
new file mode 100644
index 0000000..54f3b53
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ما مِن اتصال بالإنترنت خلال التوصيل"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"تعذّر اتصال الأجهزة"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"إيقاف التوصيل"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"نقطة الاتصال أو التوصيل مفعّلان"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml
new file mode 100644
index 0000000..e215141c
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"টে\'ডাৰিং অফ কৰক"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"হটস্পট অথবা টে\'ডাৰিং অন আছে"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml
new file mode 100644
index 0000000..1fd8e4c
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modemin internetə girişi yoxdur"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Cihazları qoşmaq mümkün deyil"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Modemi deaktiv edin"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot və ya modem aktivdir"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..1abe4f3
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Privezivanje nema pristup internetu"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Povezivanje uređaja nije uspelo"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključi privezivanje"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Uključen je hotspot ili privezivanje"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Možda važe dodatni troškovi u romingu"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml
new file mode 100644
index 0000000..38dbd1e
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Не ўдалося падключыць прылады"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Выключыць рэжым мадэма"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Хот-спот або рэжым мадэма ўключаны"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml
new file mode 100644
index 0000000..04b44db
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Тетърингът няма връзка с интернет"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Устройствата не могат да установят връзка"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Изключване на тетъринга"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Точката за достъп или тетърингът са включени"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml
new file mode 100644
index 0000000..579d1be
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"ডিভাইস কানেক্ট করতে পারছে না"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"টিথারিং বন্ধ করুন"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"হটস্পট বা টিথারিং চালু আছে"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml
new file mode 100644
index 0000000..9ce3efe
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Povezivanje putem mobitela nema internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Uređaji se ne mogu povezati"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključi povezivanje putem mobitela"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Pristupna tačka ili povezivanje putem mobitela je uključeno"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Mogu nastati dodatni troškovi u romingu"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml
new file mode 100644
index 0000000..46d4c35
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"La compartició de xarxa no té accés a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"No es poden connectar els dispositius"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactiva la compartició de xarxa"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"És possible que s\'apliquin costos addicionals en itinerància"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml
new file mode 100644
index 0000000..cc13860
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nemá připojení k internetu"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Zařízení se nemůžou připojit"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vypnout tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Je zapnutý hotspot nebo tethering"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Při roamingu mohou být účtovány dodatečné poplatky"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml
new file mode 100644
index 0000000..92c3ae1
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Netdeling har ingen internetforbindelse"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enheder kan ikke oprette forbindelse"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Deaktiver netdeling"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot eller netdeling er aktiveret"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Der opkræves muligvis yderligere gebyrer ved roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml
new file mode 100644
index 0000000..967eb4d
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering hat keinen Internetzugriff"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Geräte können sich nicht verbinden"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering deaktivieren"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot oder Tethering ist aktiviert"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Für das Roaming können zusätzliche Gebühren anfallen"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml
new file mode 100644
index 0000000..5fb4974
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Δεν είναι δυνατή η σύνδεση των συσκευών"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Απενεργοποιήστε τη σύνδεση"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml
new file mode 100644
index 0000000..45647f9
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml
new file mode 100644
index 0000000..45647f9
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml
new file mode 100644
index 0000000..45647f9
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml
new file mode 100644
index 0000000..45647f9
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml
new file mode 100644
index 0000000..7877074
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‎Tethering has no internet‎‏‎‎‏‎"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎Devices can’t connect‎‏‎‎‏‎"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎Turn off tethering‎‏‎‎‏‎"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎Hotspot or tethering is on‎‏‎‎‏‎"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎Additional charges may apply while roaming‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml
new file mode 100644
index 0000000..08edd81
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"La conexión mediante dispositivo móvil no tiene Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"No se pueden conectar los dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión mediante dispositivo móvil"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Se activó el hotspot o la conexión mediante dispositivo móvil"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Es posible que se apliquen cargos adicionales por roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml
new file mode 100644
index 0000000..79f51d0
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"La conexión no se puede compartir, porque no hay acceso a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Los dispositivos no se pueden conectar"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión compartida"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Punto de acceso o conexión compartida activados"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Puede que se apliquen cargos adicionales en itinerancia"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml
new file mode 100644
index 0000000..2da5f8a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Jagamisel puudub internetiühendus"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Seadmed ei saa ühendust luua"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Lülita jagamine välja"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Kuumkoht või jagamine on sisse lülitatud"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rändluse kasutamisega võivad kaasneda lisatasud"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml
new file mode 100644
index 0000000..2073f28
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Konexioa partekatzeko aukerak ez du Interneteko konexiorik"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Ezin dira konektatu gailuak"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desaktibatu konexioa partekatzeko aukera"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml
new file mode 100644
index 0000000..e21b2a0
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"«اشتراک‌گذاری اینترنت» به اینترنت دسترسی ندارد"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"دستگاه‌ها متصل نمی‌شوند"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"خاموش کردن «اشتراک‌گذاری اینترنت»"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"«نقطه اتصال» یا «اشتراک‌گذاری اینترنت» روشن است"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml
new file mode 100644
index 0000000..88b0b13
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ei jaettavaa internetyhteyttä"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Laitteet eivät voi muodostaa yhteyttä"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Laita yhteyden jakaminen pois päältä"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot tai yhteyden jakaminen on päällä"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Roaming voi aiheuttaa lisämaksuja"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml
new file mode 100644
index 0000000..3b781bc
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Le partage de connexion n\'est pas connecté à Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossible de connecter les appareils"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Désactiver le partage de connexion"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Le point d\'accès ou le partage de connexion est activé"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml
new file mode 100644
index 0000000..51d7203
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Aucune connexion à Internet n\'est disponible pour le partage de connexion"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossible de connecter les appareils"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Désactiver le partage de connexion"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Le point d\'accès ou le partage de connexion est activé"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml
new file mode 100644
index 0000000..008ccb4
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"A conexión compartida non ten Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Non se puideron conectar os dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión compartida"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Está activada a zona wifi ou a conexión compartida"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pódense aplicar cargos adicionais en itinerancia"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml
new file mode 100644
index 0000000..f2e3b4d
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"ડિવાઇસ કનેક્ટ કરી શકાતા નથી"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml
new file mode 100644
index 0000000..b11839d
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिंग से इंटरनेट नहीं चल रहा"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"डिवाइस कनेक्ट नहीं हो पा रहे"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिंग बंद करें"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हॉटस्पॉट या टेदरिंग चालू है"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml
new file mode 100644
index 0000000..0a5aca2
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modemsko povezivanje nema internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Uređaji se ne mogu povezati"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključivanje modemskog povezivanja"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Uključena je žarišna točka ili modemsko povezivanje"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"U roamingu su mogući dodatni troškovi"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml
new file mode 100644
index 0000000..21c689a4
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nincs internetkapcsolat az internet megosztásához"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Az eszközök nem tudnak csatlakozni"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Internetmegosztás kikapcsolása"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"A hotspot vagy az internetmegosztás be van kapcsolva"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Roaming során további díjak léphetnek fel"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml
new file mode 100644
index 0000000..689d9287
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Մոդեմի ռեժիմի կապը բացակայում է"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Չհաջողվեց միացնել սարքը"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Անջատել մոդեմի ռեժիմը"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Թեժ կետը կամ մոդեմի ռեժիմը միացված է"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml
new file mode 100644
index 0000000..a5f4d19
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tidak ada koneksi internet di tethering"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Perangkat tidak dapat terhubung"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Nonaktifkan tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot atau tethering aktif"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Biaya tambahan mungkin berlaku saat roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml
new file mode 100644
index 0000000..fc7e8aa
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tjóðrun er ekki með internettengingu"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Tæki geta ekki tengst"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Slökkva á tjóðrun"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Kveikt er á heitum reit eða tjóðrun"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Viðbótargjöld kunna að eiga við í reiki"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml
new file mode 100644
index 0000000..6456dd1
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nessuna connessione a Internet per il tethering"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossibile connettere i dispositivi"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Disattiva il tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot o tethering attivi"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml
new file mode 100644
index 0000000..46b24bd
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"למכשירים אין אפשרות להתחבר"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"השבתה של שיתוף האינטרנט בין מכשירים"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ייתכנו חיובים נוספים בעת נדידה"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml
new file mode 100644
index 0000000..e6eb277
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"テザリングがインターネットに接続されていません"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"デバイスを接続できません"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"テザリングを OFF にする"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"アクセス ポイントまたはテザリングが ON です"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ローミング時に追加料金が発生することがあります"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml
new file mode 100644
index 0000000..aeddd71
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ტეტერინგს არ აქვს ინტერნეტზე წვდომა"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"მოწყობილობები ვერ ახერხებენ დაკავშირებას"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ტეტერინგის გამორთვა"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ჩართულია უსადენო ქსელი ან ტეტერინგი"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml
new file mode 100644
index 0000000..255f0a2
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Тетеринг режимі интернет байланысынсыз пайдаланылуда"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Құрылғыларды байланыстыру мүмкін емес"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Тетерингіні өшіру"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Хотспот немесе тетеринг қосулы"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml
new file mode 100644
index 0000000..2bceb1c
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ការភ្ជាប់​មិនមានអ៊ីនធឺណិត​ទេ"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"មិនអាច​ភ្ជាប់ឧបករណ៍​បានទេ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"បិទការភ្ជាប់"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ហតស្ប៉ត ឬការភ្ជាប់​ត្រូវបានបើក"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml
new file mode 100644
index 0000000..ed76930
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ಟೆಥರಿಂಗ್‌ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ಟೆಥರಿಂಗ್‌ ಆಫ್ ಮಾಡಿ"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ಹಾಟ್‌ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್‌ ಆನ್ ಆಗಿದೆ"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ರೋಮಿಂಗ್‌ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml
new file mode 100644
index 0000000..6e50494
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"테더링으로 인터넷을 사용할 수 없음"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"기기에서 연결할 수 없음"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"테더링 사용 중지"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"핫스팟 또는 테더링 켜짐"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml
new file mode 100644
index 0000000..d68128b
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Модем режими Интернети жок колдонулууда"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Түзмөктөр туташпай жатат"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Модем режимин өчүрүү"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Байланыш түйүнү же модем режими күйүк"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роумингде кошумча акы алынышы мүмкүн"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml
new file mode 100644
index 0000000..03e134a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ປິດການປ່ອຍສັນຍານ"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml
new file mode 100644
index 0000000..652cedc
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nėra įrenginio kaip modemo naudojimo interneto ryšio"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Nepavyko susieti įrenginių"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Išjungti įrenginio kaip modemo naudojimą"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml
new file mode 100644
index 0000000..2219722
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Piesaistei nav interneta savienojuma"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Nevar savienot ierīces"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Izslēgt piesaisti"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ir ieslēgts tīklājs vai piesaiste"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml
new file mode 100644
index 0000000..227f9e3
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Нема интернет преку мобилен"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Уредите не може да се поврзат"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Исклучи интернет преку мобилен"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Точката на пристап или интернетот преку мобилен е вклучен"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"При роаминг може да се наплатат дополнителни трошоци"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml
new file mode 100644
index 0000000..ec43885
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ടെതറിംഗ് ഓഫാക്കുക"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ഹോട്ട്‌സ്‌പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml
new file mode 100644
index 0000000..e263573
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Модемд интернэт алга байна"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Төхөөрөмжүүд холбогдох боломжгүй байна"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Модем болгохыг унтраах"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Сүлжээний цэг эсвэл модем болгох асаалттай байна"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml
new file mode 100644
index 0000000..adf845d
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिंगला इंटरनेट नाही"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"डिव्हाइस कनेक्ट होऊ शकत नाहीत"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिंग बंद करा"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हॉटस्पॉट किंवा टेदरिंग सुरू आहे"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml
new file mode 100644
index 0000000..f65c451
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Penambatan tiada Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Peranti tidak dapat disambungkan"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Matikan penambatan"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Tempat liputan atau penambatan dihidupkan"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Caj tambahan mungkin digunakan semasa perayauan"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml
new file mode 100644
index 0000000..4118e77
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"စက်များ ချိတ်ဆက်၍ မရပါ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml
new file mode 100644
index 0000000..3685358
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Internettdeling har ikke internettilgang"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enhetene kan ikke koble til"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Slå av internettdeling"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Wi-Fi-sone eller internettdeling er på"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ytterligere kostnader kan påløpe under roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
new file mode 100644
index 0000000..d074f15
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"यन्त्रहरू कनेक्ट गर्न सकिएन"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिङ निष्क्रिय पार्नुहोस्"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हटस्पट वा टेदरिङ सक्रिय छ"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml
new file mode 100644
index 0000000..1d88894
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering heeft geen internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Apparaten kunnen niet worden verbonden"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering uitschakelen"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot of tethering is ingeschakeld"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml
new file mode 100644
index 0000000..8038815
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml
new file mode 100644
index 0000000..819833e
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml
new file mode 100644
index 0000000..65e4380
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nie ma internetu"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Urządzenia nie mogą się połączyć"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Wyłącz tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot lub tethering jest włączony"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml
new file mode 100644
index 0000000..d886617
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"O tethering não tem Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível conectar os dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar o tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ponto de acesso ou tethering ativado"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pode haver cobranças extras durante o roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml
new file mode 100644
index 0000000..bfd45ca
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"A ligação (à Internet) via telemóvel não tem Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível ligar os dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar ligação (à Internet) via telemóvel"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Podem aplicar-se custos adicionais em roaming."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml
new file mode 100644
index 0000000..d886617
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"O tethering não tem Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível conectar os dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar o tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ponto de acesso ou tethering ativado"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pode haver cobranças extras durante o roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml
new file mode 100644
index 0000000..8d87a9e5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Procesul de tethering nu are internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Dispozitivele nu se pot conecta"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Dezactivați procesul de tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"S-a activat hotspotul sau tethering"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Se pot aplica taxe suplimentare pentru roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml
new file mode 100644
index 0000000..dbdb9eb
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Режим модема используется без доступа к Интернету"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Невозможно подключить устройства."</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Отключить режим модема"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Включены точка доступа или режим модема"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml
new file mode 100644
index 0000000..d8301e4
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ටෙදරින් හට අන්තර්ජාලය නැත"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"උපාංගවලට සම්බන්ධ විය නොහැකිය"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ටෙදරින් ක්‍රියාවිරහිත කරන්න"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"හොට්ස්පොට් හෝ ටෙදරින් ක්‍රියාත්මකයි"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml
new file mode 100644
index 0000000..bef7136
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nemá internetové pripojenie"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Zariadenia sa nemôžu pripojiť"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vypnúť tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Je zapnutý hotspot alebo tethering"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml
new file mode 100644
index 0000000..3202c62
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Internetna povezava prek mobilnega telefona ni vzpostavljena"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Napravi se ne moreta povezati"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Izklopi internetno povezavo prek mobilnega telefona"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Med gostovanjem lahko nastanejo dodatni stroški"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml
new file mode 100644
index 0000000..37f6ad2
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ndarja e internetit nuk ka internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Pajisjet nuk mund të lidhen"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Çaktivizo ndarjen e internetit"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Zona e qasjes për internet ose ndarja e internetit është aktive"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Mund të zbatohen tarifime shtesë kur je në roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml
new file mode 100644
index 0000000..5566d03
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Привезивање нема приступ интернету"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Повезивање уређаја није успело"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Искључи привезивање"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Укључен је хотспот или привезивање"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Можда важе додатни трошкови у ромингу"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml
new file mode 100644
index 0000000..9765acd
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Det finns ingen internetanslutning för internetdelningen"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enheterna kan inte anslutas"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Inaktivera internetdelning"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Surfzon eller internetdelning har aktiverats"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ytterligare avgifter kan tillkomma vid roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml
new file mode 100644
index 0000000..cf850c9
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Kipengele cha kusambaza mtandao hakina intaneti"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Imeshindwa kuunganisha vifaa"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Zima kipengele cha kusambaza mtandao"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Umewasha kipengele cha kusambaza mtandao au mtandao pepe"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml
new file mode 100644
index 0000000..f4b15aa
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"சாதனங்களால் இணைய முடியவில்லை"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"இணைப்பு முறையை ஆஃப் செய்"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml
new file mode 100644
index 0000000..937d34d
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"పరికరాలు కనెక్ట్ అవ్వడం లేదు"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"టెథరింగ్‌ను ఆఫ్ చేయండి"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"హాట్‌స్పాట్ లేదా టెథరింగ్ ఆన్‌లో ఉంది"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"రోమింగ్‌లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml
new file mode 100644
index 0000000..f781fae
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"อุปกรณ์เชื่อมต่อไม่ได้"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml
new file mode 100644
index 0000000..8d5d4653
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Walang internet ang pag-tether"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Hindi makakonekta ang mga device"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"I-off ang pag-tether"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Naka-on ang Hotspot o pag-tether"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml
new file mode 100644
index 0000000..80cab33
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering\'in internet bağlantısı yok"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Cihazlar bağlanamıyor"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering\'i kapat"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot veya tethering açık"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Dolaşım sırasında ek ücretler uygulanabilir"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml
new file mode 100644
index 0000000..c05932a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Телефон, який використовується як модем, не підключений до Інтернету"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Не вдається підключити пристрої"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Вимкнути використання телефона як модема"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Увімкнено точку доступу або використання телефона як модема"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"У роумінгу може стягуватися додаткова плата"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml
new file mode 100644
index 0000000..d820eee
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"ٹیدرنگ میں انٹرنیٹ نہیں ہے"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"آلات منسلک نہیں ہو سکتے"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ٹیدرنگ آف کریں"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ہاٹ اسپاٹ یا ٹیدرنگ آن ہے"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml
new file mode 100644
index 0000000..726148a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modem internetga ulanmagan"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Qurilmalar ulanmadi"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Modem rejimini faolsizlantirish"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot yoki modem rejimi yoniq"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml
new file mode 100644
index 0000000..b7cb045
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Không có Internet để chia sẻ kết Internet"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Các thiết bị không thể kết nối"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tắt tính năng chia sẻ Internet"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml
new file mode 100644
index 0000000..af91aff
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"共享网络未连接到互联网"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"设备无法连接"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"关闭网络共享"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"热点或网络共享已开启"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"漫游时可能会产生额外的费用"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml
new file mode 100644
index 0000000..28e6b80
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過網絡共享連線至互聯網"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"裝置無法連接"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉網絡共享"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"熱點或網絡共享已開啟"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"漫遊時可能需要支付額外費用"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml
new file mode 100644
index 0000000..528a1e5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過網路共用連上網際網路"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"裝置無法連線"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉網路共用"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"無線基地台或網路共用已開啟"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"使用漫遊服務可能須支付額外費用"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml
new file mode 100644
index 0000000..11eb666
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi"</string>
+    <string name="no_upstream_notification_message" msgid="3843613362272973447">"Amadivayisi awakwazi ukuxhumeka"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vala ukusebenzisa ifoni njengemodemu"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe"</string>
+    <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004/config.xml b/packages/Tethering/res/values-mcc310-mnc004/config.xml
new file mode 100644
index 0000000..5c5be04
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+    <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+         "0" for disable this feature. -->
+    <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+
+    <!-- Config for showing upstream roaming notification. -->
+    <bool name="config_upstream_roaming_notification">true</bool>
+</resources>
\ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc310-mnc004/strings.xml b/packages/Tethering/res/values-mcc310-mnc004/strings.xml
new file mode 100644
index 0000000..ce9ff60
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_title">Tethering has no internet</string>
+    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_message">Devices can\u2019t connect</string>
+    <!-- String for no upstream notification disable button [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_disable_button">Turn off tethering</string>
+
+    <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+    <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string>
+    <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+    <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml
new file mode 100644
index 0000000..9bfa531
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Verbinding het nie internet nie"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Toestelle kan nie koppel nie"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Skakel verbinding af"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Warmkol of verbinding is aan"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Bykomende heffings kan geld terwyl jy swerf"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml
new file mode 100644
index 0000000..5949dfa
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ማስተሳሰር ምንም በይነመረብ የለውም"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"መሣሪያዎችን ማገናኘት አይቻልም"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ማስተሳሰርን አጥፋ"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml
new file mode 100644
index 0000000..8467f9b
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ما مِن اتصال بالإنترنت خلال التوصيل"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"تعذّر اتصال الأجهزة"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"إيقاف التوصيل"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"نقطة الاتصال أو التوصيل مفعّلان"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml
new file mode 100644
index 0000000..9776bd8
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"টে\'ডাৰিং অফ কৰক"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"হটস্পট অথবা টে\'ডাৰিং অন আছে"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml
new file mode 100644
index 0000000..e6d3eaf
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Modemin internetə girişi yoxdur"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Cihazları qoşmaq mümkün deyil"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Modemi deaktiv edin"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot və ya modem aktivdir"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..4c8a1df
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Privezivanje nema pristup internetu"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Povezivanje uređaja nije uspelo"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključi privezivanje"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Uključen je hotspot ili privezivanje"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Možda važe dodatni troškovi u romingu"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml
new file mode 100644
index 0000000..edfa41e
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Не ўдалося падключыць прылады"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Выключыць рэжым мадэма"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Хот-спот або рэжым мадэма ўключаны"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml
new file mode 100644
index 0000000..f563981
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Тетърингът няма връзка с интернет"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Устройствата не могат да установят връзка"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Изключване на тетъринга"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Точката за достъп или тетърингът са включени"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml
new file mode 100644
index 0000000..d8ecd2e
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"ডিভাইস কানেক্ট করতে পারছে না"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"টিথারিং বন্ধ করুন"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"হটস্পট বা টিথারিং চালু আছে"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml
new file mode 100644
index 0000000..b85fd5e
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Povezivanje putem mobitela nema internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Uređaji se ne mogu povezati"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključi povezivanje putem mobitela"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Pristupna tačka ili povezivanje putem mobitela je uključeno"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Mogu nastati dodatni troškovi u romingu"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml
new file mode 100644
index 0000000..a357215
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"La compartició de xarxa no té accés a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"No es poden connectar els dispositius"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactiva la compartició de xarxa"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"És possible que s\'apliquin costos addicionals en itinerància"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml
new file mode 100644
index 0000000..91196be
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nemá připojení k internetu"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Zařízení se nemůžou připojit"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vypnout tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Je zapnutý hotspot nebo tethering"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Při roamingu mohou být účtovány dodatečné poplatky"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml
new file mode 100644
index 0000000..1968900
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Netdeling har ingen internetforbindelse"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enheder kan ikke oprette forbindelse"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Deaktiver netdeling"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot eller netdeling er aktiveret"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Der opkræves muligvis yderligere gebyrer ved roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml
new file mode 100644
index 0000000..eb3f8c5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering hat keinen Internetzugriff"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Geräte können sich nicht verbinden"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering deaktivieren"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot oder Tethering ist aktiviert"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Für das Roaming können zusätzliche Gebühren anfallen"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml
new file mode 100644
index 0000000..56c3d81
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Δεν είναι δυνατή η σύνδεση των συσκευών"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Απενεργοποιήστε τη σύνδεση"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml
new file mode 100644
index 0000000..dd1a197
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml
new file mode 100644
index 0000000..dd1a197
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml
new file mode 100644
index 0000000..dd1a197
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml
new file mode 100644
index 0000000..dd1a197
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml
new file mode 100644
index 0000000..d3347aa
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‎‎‎Tethering has no internet‎‏‎‎‏‎"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‏‏‏‏‎Devices can’t connect‎‏‎‎‏‎"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎Turn off tethering‎‏‎‎‏‎"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎Hotspot or tethering is on‎‏‎‎‏‎"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‎Additional charges may apply while roaming‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml
new file mode 100644
index 0000000..2f0504f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"La conexión mediante dispositivo móvil no tiene Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"No se pueden conectar los dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión mediante dispositivo móvil"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Se activó el hotspot o la conexión mediante dispositivo móvil"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Es posible que se apliquen cargos adicionales por roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml
new file mode 100644
index 0000000..2d8f882
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"La conexión no se puede compartir, porque no hay acceso a Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Los dispositivos no se pueden conectar"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión compartida"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Punto de acceso o conexión compartida activados"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Puede que se apliquen cargos adicionales en itinerancia"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml
new file mode 100644
index 0000000..8493c470
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Jagamisel puudub internetiühendus"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Seadmed ei saa ühendust luua"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Lülita jagamine välja"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Kuumkoht või jagamine on sisse lülitatud"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rändluse kasutamisega võivad kaasneda lisatasud"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml
new file mode 100644
index 0000000..33bccab
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Konexioa partekatzeko aukerak ez du Interneteko konexiorik"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Ezin dira konektatu gailuak"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desaktibatu konexioa partekatzeko aukera"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml
new file mode 100644
index 0000000..cf8a0cc
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"«اشتراک‌گذاری اینترنت» به اینترنت دسترسی ندارد"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"دستگاه‌ها متصل نمی‌شوند"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"خاموش کردن «اشتراک‌گذاری اینترنت»"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"«نقطه اتصال» یا «اشتراک‌گذاری اینترنت» روشن است"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml
new file mode 100644
index 0000000..6a3ab80
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Ei jaettavaa internetyhteyttä"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Laitteet eivät voi muodostaa yhteyttä"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Laita yhteyden jakaminen pois päältä"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot tai yhteyden jakaminen on päällä"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Roaming voi aiheuttaa lisämaksuja"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml
new file mode 100644
index 0000000..ffb9bf60
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Le partage de connexion n\'est pas connecté à Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossible de connecter les appareils"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Désactiver le partage de connexion"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Le point d\'accès ou le partage de connexion est activé"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml
new file mode 100644
index 0000000..768bce3
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Aucune connexion à Internet n\'est disponible pour le partage de connexion"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossible de connecter les appareils"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Désactiver le partage de connexion"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Le point d\'accès ou le partage de connexion est activé"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml
new file mode 100644
index 0000000..0c4195a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"A conexión compartida non ten Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Non se puideron conectar os dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión compartida"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Está activada a zona wifi ou a conexión compartida"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pódense aplicar cargos adicionais en itinerancia"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml
new file mode 100644
index 0000000..e9d33a7
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"ડિવાઇસ કનેક્ટ કરી શકાતા નથી"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml
new file mode 100644
index 0000000..aa418ac
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिंग से इंटरनेट नहीं चल रहा"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"डिवाइस कनेक्ट नहीं हो पा रहे"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिंग बंद करें"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हॉटस्पॉट या टेदरिंग चालू है"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml
new file mode 100644
index 0000000..51c524a
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Modemsko povezivanje nema internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Uređaji se ne mogu povezati"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključivanje modemskog povezivanja"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Uključena je žarišna točka ili modemsko povezivanje"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"U roamingu su mogući dodatni troškovi"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml
new file mode 100644
index 0000000..164e45e
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Nincs internetkapcsolat az internet megosztásához"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Az eszközök nem tudnak csatlakozni"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Internetmegosztás kikapcsolása"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"A hotspot vagy az internetmegosztás be van kapcsolva"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Roaming során további díjak léphetnek fel"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml
new file mode 100644
index 0000000..e76c0a4
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Մոդեմի ռեժիմի կապը բացակայում է"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Չհաջողվեց միացնել սարքը"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Անջատել մոդեմի ռեժիմը"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Թեժ կետը կամ մոդեմի ռեժիմը միացված է"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml
new file mode 100644
index 0000000..2b817f8
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tidak ada koneksi internet di tethering"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Perangkat tidak dapat terhubung"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Nonaktifkan tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot atau tethering aktif"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Biaya tambahan mungkin berlaku saat roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml
new file mode 100644
index 0000000..a338d9c
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tjóðrun er ekki með internettengingu"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Tæki geta ekki tengst"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Slökkva á tjóðrun"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Kveikt er á heitum reit eða tjóðrun"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Viðbótargjöld kunna að eiga við í reiki"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml
new file mode 100644
index 0000000..77769c2
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Nessuna connessione a Internet per il tethering"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossibile connettere i dispositivi"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Disattiva il tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot o tethering attivi"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml
new file mode 100644
index 0000000..5267b51
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"למכשירים אין אפשרות להתחבר"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"השבתה של שיתוף האינטרנט בין מכשירים"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ייתכנו חיובים נוספים בעת נדידה"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml
new file mode 100644
index 0000000..66a9a6d
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"テザリングがインターネットに接続されていません"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"デバイスを接続できません"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"テザリングを OFF にする"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"アクセス ポイントまたはテザリングが ON です"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ローミング時に追加料金が発生することがあります"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml
new file mode 100644
index 0000000..d8ad880
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ტეტერინგს არ აქვს ინტერნეტზე წვდომა"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"მოწყობილობები ვერ ახერხებენ დაკავშირებას"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ტეტერინგის გამორთვა"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ჩართულია უსადენო ქსელი ან ტეტერინგი"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml
new file mode 100644
index 0000000..1ddd6b4
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Тетеринг режимі интернет байланысынсыз пайдаланылуда"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Құрылғыларды байланыстыру мүмкін емес"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Тетерингіні өшіру"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Хотспот немесе тетеринг қосулы"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml
new file mode 100644
index 0000000..cf5a137
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ការភ្ជាប់​មិនមានអ៊ីនធឺណិត​ទេ"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"មិនអាច​ភ្ជាប់ឧបករណ៍​បានទេ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"បិទការភ្ជាប់"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ហតស្ប៉ត ឬការភ្ជាប់​ត្រូវបានបើក"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"អាចមាន​ការគិតថ្លៃ​បន្ថែម នៅពេល​រ៉ូមីង"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml
new file mode 100644
index 0000000..68ae68b
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ಟೆಥರಿಂಗ್‌ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ಟೆಥರಿಂಗ್‌ ಆಫ್ ಮಾಡಿ"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ಹಾಟ್‌ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್‌ ಆನ್ ಆಗಿದೆ"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ರೋಮಿಂಗ್‌ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml
new file mode 100644
index 0000000..17185ba
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"테더링으로 인터넷을 사용할 수 없음"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"기기에서 연결할 수 없음"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"테더링 사용 중지"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"핫스팟 또는 테더링 켜짐"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml
new file mode 100644
index 0000000..6a9fb98
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Модем режими Интернети жок колдонулууда"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Түзмөктөр туташпай жатат"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Модем режимин өчүрүү"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Байланыш түйүнү же модем режими күйүк"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роумингде кошумча акы алынышы мүмкүн"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml
new file mode 100644
index 0000000..bcc4b57
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ປິດການປ່ອຍສັນຍານ"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml
new file mode 100644
index 0000000..011c2c1
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Nėra įrenginio kaip modemo naudojimo interneto ryšio"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Nepavyko susieti įrenginių"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Išjungti įrenginio kaip modemo naudojimą"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml
new file mode 100644
index 0000000..5cb2f3b
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Piesaistei nav interneta savienojuma"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Nevar savienot ierīces"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Izslēgt piesaisti"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ir ieslēgts tīklājs vai piesaiste"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml
new file mode 100644
index 0000000..4cbfd88
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Нема интернет преку мобилен"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Уредите не може да се поврзат"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Исклучи интернет преку мобилен"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Точката на пристап или интернетот преку мобилен е вклучен"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"При роаминг може да се наплатат дополнителни трошоци"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml
new file mode 100644
index 0000000..9cf4eaf
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ടെതറിംഗ് ഓഫാക്കുക"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ഹോട്ട്‌സ്‌പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml
new file mode 100644
index 0000000..47c82c1
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Модемд интернэт алга байна"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Төхөөрөмжүүд холбогдох боломжгүй байна"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Модем болгохыг унтраах"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Сүлжээний цэг эсвэл модем болгох асаалттай байна"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml
new file mode 100644
index 0000000..ad9e809
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिंगला इंटरनेट नाही"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"डिव्हाइस कनेक्ट होऊ शकत नाहीत"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिंग बंद करा"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हॉटस्पॉट किंवा टेदरिंग सुरू आहे"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml
new file mode 100644
index 0000000..e708cb8
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Penambatan tiada Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Peranti tidak dapat disambungkan"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Matikan penambatan"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Tempat liputan atau penambatan dihidupkan"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Caj tambahan mungkin digunakan semasa perayauan"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml
new file mode 100644
index 0000000..ba54622
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"စက်များ ချိတ်ဆက်၍ မရပါ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml
new file mode 100644
index 0000000..57db484a2
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Internettdeling har ikke internettilgang"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enhetene kan ikke koble til"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Slå av internettdeling"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Wi-Fi-sone eller internettdeling er på"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ytterligere kostnader kan påløpe under roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
new file mode 100644
index 0000000..1503244
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"यन्त्रहरू कनेक्ट गर्न सकिएन"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिङ निष्क्रिय पार्नुहोस्"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हटस्पट वा टेदरिङ सक्रिय छ"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml
new file mode 100644
index 0000000..b08133f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering heeft geen internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Apparaten kunnen niet worden verbonden"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering uitschakelen"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot of tethering is ingeschakeld"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml
new file mode 100644
index 0000000..1ad4ca3
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml
new file mode 100644
index 0000000..88def56
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml
new file mode 100644
index 0000000..f9890ab
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nie ma internetu"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Urządzenia nie mogą się połączyć"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Wyłącz tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot lub tethering jest włączony"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml
new file mode 100644
index 0000000..ce3b884
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"O tethering não tem Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível conectar os dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar o tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ponto de acesso ou tethering ativado"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pode haver cobranças extras durante o roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml
new file mode 100644
index 0000000..7e883ea
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"A ligação (à Internet) via telemóvel não tem Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível ligar os dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar ligação (à Internet) via telemóvel"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Podem aplicar-se custos adicionais em roaming."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml
new file mode 100644
index 0000000..ce3b884
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"O tethering não tem Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível conectar os dispositivos"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar o tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ponto de acesso ou tethering ativado"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pode haver cobranças extras durante o roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml
new file mode 100644
index 0000000..1009417
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Procesul de tethering nu are internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Dispozitivele nu se pot conecta"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Dezactivați procesul de tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"S-a activat hotspotul sau tethering"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Se pot aplica taxe suplimentare pentru roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml
new file mode 100644
index 0000000..88683be
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Режим модема используется без доступа к Интернету"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Невозможно подключить устройства."</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Отключить режим модема"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Включены точка доступа или режим модема"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml
new file mode 100644
index 0000000..176bcdb
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ටෙදරින් හට අන්තර්ජාලය නැත"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"උපාංගවලට සම්බන්ධ විය නොහැකිය"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ටෙදරින් ක්‍රියාවිරහිත කරන්න"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"හොට්ස්පොට් හෝ ටෙදරින් ක්‍රියාත්මකයි"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml
new file mode 100644
index 0000000..b9e2127
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nemá internetové pripojenie"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Zariadenia sa nemôžu pripojiť"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vypnúť tethering"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Je zapnutý hotspot alebo tethering"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml
new file mode 100644
index 0000000..e8140e6
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Internetna povezava prek mobilnega telefona ni vzpostavljena"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Napravi se ne moreta povezati"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Izklopi internetno povezavo prek mobilnega telefona"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Med gostovanjem lahko nastanejo dodatni stroški"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml
new file mode 100644
index 0000000..61e698d
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Ndarja e internetit nuk ka internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Pajisjet nuk mund të lidhen"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Çaktivizo ndarjen e internetit"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Zona e qasjes për internet ose ndarja e internetit është aktive"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Mund të zbatohen tarifime shtesë kur je në roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml
new file mode 100644
index 0000000..b4c411c
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Привезивање нема приступ интернету"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Повезивање уређаја није успело"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Искључи привезивање"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Укључен је хотспот или привезивање"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Можда важе додатни трошкови у ромингу"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml
new file mode 100644
index 0000000..4f543e4
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Det finns ingen internetanslutning för internetdelningen"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enheterna kan inte anslutas"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Inaktivera internetdelning"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Surfzon eller internetdelning har aktiverats"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ytterligare avgifter kan tillkomma vid roaming"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml
new file mode 100644
index 0000000..ac347ab
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Kipengele cha kusambaza mtandao hakina intaneti"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Imeshindwa kuunganisha vifaa"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Zima kipengele cha kusambaza mtandao"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Umewasha kipengele cha kusambaza mtandao au mtandao pepe"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml
new file mode 100644
index 0000000..2ea2467
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"இணைப்பு முறைக்கு இணைய இணைப்பு இல்லை"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"சாதனங்களால் இணைய முடியவில்லை"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"இணைப்பு முறையை ஆஃப் செய்"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ஹாட்ஸ்பாட் அல்லது இணைப்பு முறை ஆன் செய்யப்பட்டுள்ளது"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml
new file mode 100644
index 0000000..9360297
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"పరికరాలు కనెక్ట్ అవ్వడం లేదు"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"టెథరింగ్‌ను ఆఫ్ చేయండి"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"హాట్‌స్పాట్ లేదా టెథరింగ్ ఆన్‌లో ఉంది"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"రోమింగ్‌లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml
new file mode 100644
index 0000000..9c4d7e0
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"อุปกรณ์เชื่อมต่อไม่ได้"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml
new file mode 100644
index 0000000..a7c78a5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Walang internet ang pag-tether"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Hindi makakonekta ang mga device"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"I-off ang pag-tether"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Naka-on ang Hotspot o pag-tether"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml
new file mode 100644
index 0000000..93da2c3
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering\'in internet bağlantısı yok"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Cihazlar bağlanamıyor"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering\'i kapat"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot veya tethering açık"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Dolaşım sırasında ek ücretler uygulanabilir"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml
new file mode 100644
index 0000000..ee0dcd2
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Телефон, який використовується як модем, не підключений до Інтернету"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Не вдається підключити пристрої"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Вимкнути використання телефона як модема"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Увімкнено точку доступу або використання телефона як модема"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"У роумінгу може стягуватися додаткова плата"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml
new file mode 100644
index 0000000..41cd28e
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"ٹیدرنگ میں انٹرنیٹ نہیں ہے"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"آلات منسلک نہیں ہو سکتے"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ٹیدرنگ آف کریں"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ہاٹ اسپاٹ یا ٹیدرنگ آن ہے"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml
new file mode 100644
index 0000000..c847bc9
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Modem internetga ulanmagan"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Qurilmalar ulanmadi"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Modem rejimini faolsizlantirish"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot yoki modem rejimi yoniq"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml
new file mode 100644
index 0000000..a74326f
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Không có Internet để chia sẻ kết Internet"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Các thiết bị không thể kết nối"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tắt tính năng chia sẻ Internet"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml
new file mode 100644
index 0000000..d737003
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"共享网络未连接到互联网"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"设备无法连接"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"关闭网络共享"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"热点或网络共享已开启"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"漫游时可能会产生额外的费用"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml
new file mode 100644
index 0000000..f378a9d
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過網絡共享連線至互聯網"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"裝置無法連接"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉網絡共享"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"熱點或網絡共享已開啟"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"漫遊時可能需要支付額外費用"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml
new file mode 100644
index 0000000..cd653df
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過網路共用連上網際網路"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"裝置無法連線"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉網路共用"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"無線基地台或網路共用已開啟"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"使用漫遊服務可能須支付額外費用"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml
new file mode 100644
index 0000000..32f6df5
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi"</string>
+    <string name="no_upstream_notification_message" msgid="6508394877641864863">"Amadivayisi awakwazi ukuxhumeka"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vala ukusebenzisa ifoni njengemodemu"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe"</string>
+    <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480/config.xml b/packages/Tethering/res/values-mcc311-mnc480/config.xml
new file mode 100644
index 0000000..5c5be04
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+    <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+         "0" for disable this feature. -->
+    <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+
+    <!-- Config for showing upstream roaming notification. -->
+    <bool name="config_upstream_roaming_notification">true</bool>
+</resources>
\ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc311-mnc480/strings.xml b/packages/Tethering/res/values-mcc311-mnc480/strings.xml
new file mode 100644
index 0000000..ce9ff60
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources>
+    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_title">Tethering has no internet</string>
+    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_message">Devices can\u2019t connect</string>
+    <!-- String for no upstream notification disable button [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_disable_button">Turn off tethering</string>
+
+    <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+    <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string>
+    <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+    <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
+</resources>
diff --git a/packages/Tethering/res/values-mk/strings.xml b/packages/Tethering/res/values-mk/strings.xml
index 0fab8aa..9ad9b9a 100644
--- a/packages/Tethering/res/values-mk/strings.xml
+++ b/packages/Tethering/res/values-mk/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Поврзувањето или точката на пристап се активни"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Допрете за поставување."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Врзувањето е оневозможено"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Контактирајте со администраторот за детали"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Активно е врзување или точка на пристап"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Допрете за поставување."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Врзувањето е оневозможено"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Контактирајте со администраторот за детали"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус на точката на пристап и врзувањето"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ml/strings.xml b/packages/Tethering/res/values-ml/strings.xml
index fd7e556..9db79ce 100644
--- a/packages/Tethering/res/values-ml/strings.xml
+++ b/packages/Tethering/res/values-ml/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"വിശദവിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്‌പോട്ട് സജീവമാണ്"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"സജ്ജീകരിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"വിശദാംശങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ഹോട്ട്‌സ്പോട്ടിന്റെയും ടെതറിംഗിന്റെയും നില"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-mn/strings.xml b/packages/Tethering/res/values-mn/strings.xml
index 4596577..42d1edb 100644
--- a/packages/Tethering/res/values-mn/strings.xml
+++ b/packages/Tethering/res/values-mn/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Модем болгох эсвэл идэвхтэй цэг болгох"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Тохируулахын тулд товшино уу."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Модем болгох боломжгүй байна"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Модем болгох эсвэл сүлжээний цэг идэвхтэй байна"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Тохируулахын тулд товшино уу."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Модем болгохыг идэвхгүй болгосон"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Сүлжээний цэг болон модем болгох төлөв"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-mr/strings.xml b/packages/Tethering/res/values-mr/strings.xml
index 85c9ade..13995b6 100644
--- a/packages/Tethering/res/values-mr/strings.xml
+++ b/packages/Tethering/res/values-mr/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग किंवा हॉटस्पॉट सक्रिय"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"सेट करण्यासाठी टॅप करा."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग बंद आहे"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"तपशीलांसाठी तुमच्या प्रशासकाशी संपर्क साधा"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिंग किंवा हॉटस्पॉट अ‍ॅक्टिव्ह आहे"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"सेट करण्यासाठी टॅप करा."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिंग बंद केले आहे"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"तपशीलांसाठी तुमच्या ॲडमिनशी संपर्क साधा"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हॉटस्पॉट आणि टेदरिंगची स्थिती"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ms/strings.xml b/packages/Tethering/res/values-ms/strings.xml
index ec6bdbd..d6a67f3 100644
--- a/packages/Tethering/res/values-ms/strings.xml
+++ b/packages/Tethering/res/values-ms/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Penambatan atau titik panas aktif"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Ketik untuk membuat persediaan."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Penambatan dilumpuhkan"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hubungi pentadbir anda untuk maklumat lanjut"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Penambatan atau tempat liputan aktif"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Ketik untuk membuat persediaan."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Penambatan dilumpuhkan"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hubungi pentadbir anda untuk mendapatkan maklumat lanjut"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status tempat liputan &amp; penambatan"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-my/strings.xml b/packages/Tethering/res/values-my/strings.xml
index 83978b6..49f6b88 100644
--- a/packages/Tethering/res/values-my/strings.xml
+++ b/packages/Tethering/res/values-my/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"တဆင့်ပြန်လည်လွှင့်ခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"မိုဘိုင်းဖုန်းကို မိုဒမ်အဖြစ်သုံးခြင်းအား ပိတ်ထားသည်"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"အသေးစိတ်အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းကို ပိတ်ထားသည်"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"အသေးစိတ်အတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ဟော့စပေါ့နှင့် မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း အခြေအနေ"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-nb/strings.xml b/packages/Tethering/res/values-nb/strings.xml
index 9abf32d..9594e0a 100644
--- a/packages/Tethering/res/values-nb/strings.xml
+++ b/packages/Tethering/res/values-nb/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Internettdeling eller trådløs sone er aktiv"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Trykk for å konfigurere."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Internettdeling er slått av"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ta kontakt med administratoren din for å få mer informasjon"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Internettdeling eller Wi-Fi-sone er aktiv"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Trykk for å konfigurere."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Internettdeling er slått av"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Ta kontakt med administratoren din for å få mer informasjon"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status for Wi-Fi-sone og internettdeling"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ne/strings.xml b/packages/Tethering/res/values-ne/strings.xml
index c886929..72ae3a8 100644
--- a/packages/Tethering/res/values-ne/strings.xml
+++ b/packages/Tethering/res/values-ne/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"टेथर गर्ने वा हटस्पट सक्रिय"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"सेटअप गर्न ट्याप गर्नुहोस्।"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिङलाई असक्षम पारिएको छ"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिङ वा हटस्पट सक्रिय छ"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"सेटअप गर्न ट्याप गर्नुहोस्।"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिङ सुविधा असक्षम पारिएको छ"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हटस्पट तथा टेदरिङको स्थिति"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-nl/strings.xml b/packages/Tethering/res/values-nl/strings.xml
index 0ec4bff6..18b2bbf 100644
--- a/packages/Tethering/res/values-nl/strings.xml
+++ b/packages/Tethering/res/values-nl/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering of hotspot actief"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tik om in te stellen."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is uitgeschakeld"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Neem contact op met je beheerder voor meer informatie"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering of hotspot actief"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Tik om in te stellen."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is uitgeschakeld"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Neem contact op met je beheerder voor meer informatie"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status van hotspot en tethering"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-or/strings.xml b/packages/Tethering/res/values-or/strings.xml
index 4576857..a15a6db 100644
--- a/packages/Tethering/res/values-or/strings.xml
+++ b/packages/Tethering/res/values-or/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍‌ ସକ୍ରିୟ ଅଛି"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"ସେଟଅପ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ଟିଥରିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ବିବରଣୀ ପାଇଁ ନିଜ ଆଡମିନ୍‌ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ଟିଥେରିଂ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"ସେଟ୍ ଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ଟିଥେରିଂ ଅକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"ବିବରଣୀଗୁଡ଼ିକ ପାଇଁ ଆପଣଙ୍କ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ହଟସ୍ପଟ୍ ଓ ଟିଥେରିଂ ସ୍ଥିତି"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-pa/strings.xml b/packages/Tethering/res/values-pa/strings.xml
index deddf2e..a8235e4 100644
--- a/packages/Tethering/res/values-pa/strings.xml
+++ b/packages/Tethering/res/values-pa/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ਟੈਦਰਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ਟੈਦਰਿੰਗ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ਹੌਟਸਪੌਟ ਅਤੇ ਟੈਦਰਿੰਗ ਦੀ ਸਥਿਤੀ"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-pl/strings.xml b/packages/Tethering/res/values-pl/strings.xml
index 48d8468..ccb017d 100644
--- a/packages/Tethering/res/values-pl/strings.xml
+++ b/packages/Tethering/res/values-pl/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Aktywny tethering lub punkt dostępu"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Kliknij, by skonfigurować."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering został wyłączony"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Aktywny tethering lub punkt dostępu"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Kliknij, by skonfigurować"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering został wyłączony"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot i tethering – stan"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-pt-rBR/strings.xml b/packages/Tethering/res/values-pt-rBR/strings.xml
index 32c22b8..a0a4745 100644
--- a/packages/Tethering/res/values-pt-rBR/strings.xml
+++ b/packages/Tethering/res/values-pt-rBR/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Ponto de acesso ou tethering ativo"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering desativado"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Fale com seu administrador para saber detalhes"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status de ponto de acesso e tethering"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-pt-rPT/strings.xml b/packages/Tethering/res/values-pt-rPT/strings.xml
index 641e22f..e3f03fc 100644
--- a/packages/Tethering/res/values-pt-rPT/strings.xml
+++ b/packages/Tethering/res/values-pt-rPT/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Ligação ponto a ponto ou hotspot activos"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"A ligação (à Internet) via telemóvel está desativada."</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacte o gestor para obter detalhes."</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Ligação (à Internet) via telemóvel ou zona Wi-Fi ativas"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"A ligação (à Internet) via telemóvel está desativada."</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacte o administrador para obter detalhes."</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado da zona Wi-Fi e da ligação (à Internet) via telemóvel"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-pt/strings.xml b/packages/Tethering/res/values-pt/strings.xml
index 32c22b8..a0a4745 100644
--- a/packages/Tethering/res/values-pt/strings.xml
+++ b/packages/Tethering/res/values-pt/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Ponto de acesso ou tethering ativo"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering desativado"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Fale com seu administrador para saber detalhes"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status de ponto de acesso e tethering"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ro/strings.xml b/packages/Tethering/res/values-ro/strings.xml
index f861f73..5706a4a 100644
--- a/packages/Tethering/res/values-ro/strings.xml
+++ b/packages/Tethering/res/values-ro/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering sau hotspot activ"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Atingeți ca să configurați."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tetheringul este dezactivat"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contactați administratorul pentru detalii"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering sau hotspot activ"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Atingeți ca să configurați."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tetheringul este dezactivat"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contactați administratorul pentru detalii"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Starea hotspotului și a tetheringului"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ru/strings.xml b/packages/Tethering/res/values-ru/strings.xml
index 027cb41..7cb6f7d 100644
--- a/packages/Tethering/res/values-ru/strings.xml
+++ b/packages/Tethering/res/values-ru/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Включен режим модема"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Нажмите, чтобы настроить."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Включить режим модема нельзя"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Обратитесь к администратору, чтобы узнать подробности."</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Включен режим модема или точка доступа"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Нажмите, чтобы настроить."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Использование телефона в качестве модема запрещено"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Чтобы узнать подробности, обратитесь к администратору."</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус хот-спота и режима модема"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-si/strings.xml b/packages/Tethering/res/values-si/strings.xml
index 7d8599f..ec34c22 100644
--- a/packages/Tethering/res/values-si/strings.xml
+++ b/packages/Tethering/res/values-si/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"පිහිටුවීමට තට්ටු කරන්න."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ටෙදරින් අබල කර ඇත"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ටෙදරින් හෝ හොට්ස්පොට් සක්‍රීයයි"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"පිහිටුවීමට තට්ටු කරන්න."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ටෙදරින් අබල කර ඇත"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"හොට්ස්පොට් &amp; ටෙදරින් තත්ත්වය"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-sk/strings.xml b/packages/Tethering/res/values-sk/strings.xml
index a8fe297..43e787c 100644
--- a/packages/Tethering/res/values-sk/strings.xml
+++ b/packages/Tethering/res/values-sk/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering alebo prístupový bod je aktívny"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Klepnutím prejdete na nastavenie."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering je deaktivovaný"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"O podrobnosti požiadajte svojho správcu"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering alebo prístupový bod je aktívny"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Klepnutím prejdete na nastavenie."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering je deaktivovaný"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"O podrobnosti požiadajte svojho správcu"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stav hotspotu a tetheringu"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-sl/strings.xml b/packages/Tethering/res/values-sl/strings.xml
index b5e5e38..5943362 100644
--- a/packages/Tethering/res/values-sl/strings.xml
+++ b/packages/Tethering/res/values-sl/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Aktivna povezava z internetom ali dostopna točka sta aktivni"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Dotaknite se, če želite nastaviti."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Povezava z internetom prek mobilnega telefona je onemogočena"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Za podrobnosti se obrnite na skrbnika"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Povezava z internetom prek mobilnega telefona ali dostopna točka je aktivna"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Dotaknite se, če želite nastaviti."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Povezava z internetom prek mobilnega telefona je onemogočena"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Za podrobnosti se obrnite na skrbnika"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stanje dostopne točke in povezave z internetom prek mobilnega telefona"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-sq/strings.xml b/packages/Tethering/res/values-sq/strings.xml
index fdd4906..21e1155 100644
--- a/packages/Tethering/res/values-sq/strings.xml
+++ b/packages/Tethering/res/values-sq/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Lidhja e çiftimit ose ajo e qasjes në zona publike interneti është aktive"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Trokit për ta konfiguruar."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Lidhja e çiftimit është çaktivizuar"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakto me administratorin për detaje"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Ndarja e internetit ose zona e qasjes së internetit është aktive"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Trokit për ta konfiguruar."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Ndarja e internetit është çaktivizuar"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakto me administratorin për detaje"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Statusi i zonës së qasjes dhe ndarjes së internetit"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-sr/strings.xml b/packages/Tethering/res/values-sr/strings.xml
index 9fab34589..e2e4dc6 100644
--- a/packages/Tethering/res/values-sr/strings.xml
+++ b/packages/Tethering/res/values-sr/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Активно повезивање са интернетом преко мобилног уређаја или хотспот"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Додирните да бисте подесили."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Привезивање је онемогућено"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Потражите детаље од администратора"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Привезивање или хотспот је активан"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Додирните да бисте подесили."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Привезивање је онемогућено"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Потражите детаље од администратора"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус хотспота и привезивања"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-sv/strings.xml b/packages/Tethering/res/values-sv/strings.xml
index 10eeb0f..72702c2 100644
--- a/packages/Tethering/res/values-sv/strings.xml
+++ b/packages/Tethering/res/values-sv/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Internetdelning eller surfzon aktiverad"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Tryck om du vill konfigurera."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Internetdelning har inaktiverats"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakta administratören om du vill veta mer"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Internetdelning eller surfzon har aktiverats"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Tryck om du vill konfigurera."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Internetdelning har inaktiverats"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakta administratören om du vill veta mer"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Trådlös surfzon och internetdelning har inaktiverats"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-sw/strings.xml b/packages/Tethering/res/values-sw/strings.xml
index 3353963..65e4aa8ce 100644
--- a/packages/Tethering/res/values-sw/strings.xml
+++ b/packages/Tethering/res/values-sw/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Kushiriki au kusambaza intaneti kumewashwa"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Gusa ili uweke mipangilio."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Umezima kipengele cha kusambaza mtandao"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Kusambaza mtandao au mtandaopepe umewashwa"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Gusa ili uweke mipangilio."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Umezima kipengele cha kusambaza mtandao"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Mtandaopepe na hali ya kusambaza mtandao"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ta/strings.xml b/packages/Tethering/res/values-ta/strings.xml
index b1e5cc2..4aba62d 100644
--- a/packages/Tethering/res/values-ta/strings.xml
+++ b/packages/Tethering/res/values-ta/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"டெதெரிங்/ஹாட்ஸ்பாட் இயங்குகிறது"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"அமைக்க, தட்டவும்."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"இணைப்பு முறை முடக்கப்பட்டுள்ளது"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"விவரங்களுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"டெதெரிங் அல்லது ஹாட்ஸ்பாட் இயங்குகிறது"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"அமைக்க, தட்டவும்."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"டெதெரிங் முடக்கப்பட்டுள்ளது"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"விவரங்களுக்கு உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ஹாட்ஸ்பாட் &amp; டெதெரிங் நிலை"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-te/strings.xml b/packages/Tethering/res/values-te/strings.xml
index aae40de..1f91791 100644
--- a/packages/Tethering/res/values-te/strings.xml
+++ b/packages/Tethering/res/values-te/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"టీథర్ చేయబడినది లేదా హాట్‌స్పాట్ సక్రియంగా ఉండేది"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"సెటప్ చేయడానికి నొక్కండి."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"టెథెరింగ్ నిలిపివేయబడింది"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"వివరాల కోసం మీ నిర్వాహకులను సంప్రదించండి"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"టెథరింగ్ లేదా హాట్‌స్పాట్ యాక్టివ్‌గా ఉంది"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"సెటప్ చేయడానికి ట్యాప్ చేయండి."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"టెథరింగ్ డిజేబుల్ చేయబడింది"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"వివరాల కోసం మీ అడ్మిన్‌ని సంప్రదించండి"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"హాట్‌స్పాట్ &amp; టెథరింగ్ స్థితి"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-th/strings.xml b/packages/Tethering/res/values-th/strings.xml
index 1b80056..44171c0 100644
--- a/packages/Tethering/res/values-th/strings.xml
+++ b/packages/Tethering/res/values-th/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"การปล่อยสัญญาณหรือฮอตสปอตทำงานอยู่"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"แตะเพื่อตั้งค่า"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือหรือฮอตสปอตทำงานอยู่"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"แตะเพื่อตั้งค่า"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"สถานะฮอตสปอตและการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-tl/strings.xml b/packages/Tethering/res/values-tl/strings.xml
index 12863f9..7347dd3 100644
--- a/packages/Tethering/res/values-tl/strings.xml
+++ b/packages/Tethering/res/values-tl/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Pagsasama o aktibong hotspot"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"I-tap upang i-set up."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Naka-disable ang pag-tether"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Aktibo ang pag-tether o hotspot"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"I-tap para i-set up."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Naka-disable ang pag-tether"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status ng hotspot at pag-tether"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-tr/strings.xml b/packages/Tethering/res/values-tr/strings.xml
index bfcf1ac..32030f1 100644
--- a/packages/Tethering/res/values-tr/strings.xml
+++ b/packages/Tethering/res/values-tr/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering veya hotspot etkin"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Ayarlamak için dokunun."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering devre dışı bırakıldı"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ayrıntılı bilgi için yöneticinize başvurun"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering veya hotspot etkin"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Ayarlamak için dokunun."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering devre dışı bırakıldı"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Ayrıntılı bilgi için yöneticinize başvurun"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot ve tethering durumu"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-uk/strings.xml b/packages/Tethering/res/values-uk/strings.xml
index 8e159c07..1ca89b3 100644
--- a/packages/Tethering/res/values-uk/strings.xml
+++ b/packages/Tethering/res/values-uk/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Прив\'язка чи точка дост. активна"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Торкніться, щоб налаштувати."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Використання телефона в режимі модема вимкнено"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Щоб дізнатися більше, зв’яжіться з адміністратором"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Модем чи точка доступу активні"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Натисніть, щоб налаштувати."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Використання телефона як модема вимкнено"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Щоб дізнатися більше, зв\'яжіться з адміністратором"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус точки доступу та модема"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-ur/strings.xml b/packages/Tethering/res/values-ur/strings.xml
index 89195d4..d72c7d4 100644
--- a/packages/Tethering/res/values-ur/strings.xml
+++ b/packages/Tethering/res/values-ur/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"ٹیدرنگ غیر فعال ہے"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"ٹیدرنگ غیر فعال ہے"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ہاٹ اسپاٹ اور ٹیتھرنگ کا اسٹیٹس"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-uz/strings.xml b/packages/Tethering/res/values-uz/strings.xml
index 0ac4d4a..af3b2eb 100644
--- a/packages/Tethering/res/values-uz/strings.xml
+++ b/packages/Tethering/res/values-uz/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Modem rejimi yoniq"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Sozlash uchun bosing."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Modem rejimi faolsizlantirildi"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Tafsilotlari uchun administratoringizga murojaat qiling"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Modem rejimi yoki hotspot yoniq"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Sozlash uchun bosing."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Modem rejimi faolsizlantirildi"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Tafsilotlari uchun administratoringizga murojaat qiling"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot va modem rejimi holati"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-vi/strings.xml b/packages/Tethering/res/values-vi/strings.xml
index 85a4db8..21a0735 100644
--- a/packages/Tethering/res/values-vi/strings.xml
+++ b/packages/Tethering/res/values-vi/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Chức năng điểm truy cập Internet hoặc điểm phát sóng đang hoạt động"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Nhấn để thiết lập."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Đã tắt tính năng chia sẻ kết nối"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Tính năng chia sẻ Internet hoặc điểm phát sóng đang hoạt động"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Hãy nhấn để thiết lập."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Đã tắt tính năng chia sẻ Internet"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Trạng thái điểm phát sóng và chia sẻ Internet"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-zh-rCN/strings.xml b/packages/Tethering/res/values-zh-rCN/strings.xml
index ff1fe03..98e3b4b 100644
--- a/packages/Tethering/res/values-zh-rCN/strings.xml
+++ b/packages/Tethering/res/values-zh-rCN/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"网络共享或热点已启用"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"点按即可进行设置。"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"网络共享已停用"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"请与您的管理员联系以了解详情"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"网络共享或热点已启用"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"点按即可设置。"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"网络共享已停用"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"如需了解详情,请与您的管理员联系"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"热点和网络共享状态"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-zh-rHK/strings.xml b/packages/Tethering/res/values-zh-rHK/strings.xml
index 0de39fa..9cafd42 100644
--- a/packages/Tethering/res/values-zh-rHK/strings.xml
+++ b/packages/Tethering/res/values-zh-rHK/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"已啟用網絡共享或熱點"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"輕按即可設定。"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"網絡共享已停用"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"請聯絡您的管理員以瞭解詳情"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"網絡共享或熱點已啟用"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"輕按即可設定。"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"網絡共享已停用"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"請聯絡您的管理員以瞭解詳情"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"熱點和網絡共享狀態"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-zh-rTW/strings.xml b/packages/Tethering/res/values-zh-rTW/strings.xml
index 9a117bb..50a50bf 100644
--- a/packages/Tethering/res/values-zh-rTW/strings.xml
+++ b/packages/Tethering/res/values-zh-rTW/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"網路共用或無線基地台已啟用"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"輕觸即可進行設定。"</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"數據連線已停用"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"詳情請洽你的管理員"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"網路共用或無線基地台已啟用"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"輕觸即可進行設定。"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"網路共用已停用"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"詳情請洽你的管理員"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"無線基地台與網路共用狀態"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values-zu/strings.xml b/packages/Tethering/res/values-zu/strings.xml
index 8fe10d8..f210f87 100644
--- a/packages/Tethering/res/values-zu/strings.xml
+++ b/packages/Tethering/res/values-zu/strings.xml
@@ -1,8 +1,29 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--  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.
+ -->
+
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string>
-    <string name="tethered_notification_message" msgid="2113628520792055377">"Thepha ukuze usethe."</string>
-    <string name="disable_tether_notification_title" msgid="7526977944111313195">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string>
-    <string name="disable_tether_notification_message" msgid="2913366428516852495">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string>
+    <string name="tethered_notification_message" msgid="64800879503420696">"Thepha ukuze usethe."</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string>
+    <string name="disable_tether_notification_message" msgid="6717523799293901476">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"I-Hotspot nesimo sokusebenzisa ifoni njengemodemu"</string>
+    <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
+    <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
+    <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
+    <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string>
+    <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string>
 </resources>
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index f825d6b..3f5bc90 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -55,6 +55,12 @@
         <item>"bt-pan"</item>
     </string-array>
 
+    <!-- Use the BPF offload for tethering when the kernel has support. True by default.
+         If the device doesn't want to support tether BPF offload, this should be false.
+         Note that this setting could be overridden by device config.
+    -->
+    <bool translatable="false" name="config_tether_enable_bpf_offload">true</bool>
+
     <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. -->
     <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool>
 
@@ -62,6 +68,13 @@
     <string-array translatable="false" name="config_tether_dhcp_range">
     </string-array>
 
+    <!-- Used to config periodic polls tether offload stats from tethering offload HAL to make the
+     data warnings work. 5000(ms) by default. If the device doesn't want to poll tether
+     offload stats, this should be -1. Note that this setting could be override by
+     runtime resource overlays.
+    -->
+    <integer translatable="false" name="config_tether_offload_poll_interval">5000</integer>
+
     <!-- Array of ConnectivityManager.TYPE_{BLUETOOTH, ETHERNET, MOBILE, MOBILE_DUN, MOBILE_HIPRI,
          WIFI} values allowable for tethering.
 
@@ -87,7 +100,7 @@
                         TYPE_MOBILE_HIPRI is appended.
 
          For other changes applied to this list, now and in the future, see
-         com.android.server.connectivity.tethering.TetheringConfiguration.
+         com.android.networkstack.tethering.TetheringConfiguration.
 
          Note also: the order of this is important. The first upstream type
          for which a satisfying network exists is used.
@@ -156,48 +169,14 @@
     <!-- ComponentName of the service used to run no ui tether provisioning. -->
     <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
 
-    <!-- Enable tethering notification -->
-    <!-- Icons for showing tether enable notification.
-         Each item should have two elements and be separated with ";".
+    <!-- No upstream notification is shown when there is a downstream but no upstream that is able
+         to do the tethering. -->
+    <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+         "-1" for disable this feature. -->
+    <integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer>
 
-         The first element is downstream types which is one of tethering. This element has to be
-         made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream
-         types and use "," to separate each combinations. Such as
-
-             USB|BT,WIFI|USB|BT
-
-         The second element is icon for the item. This element has to be composed by
-         <package name>:drawable/<resource name>. Such as
-
-             1. com.android.networkstack.tethering:drawable/stat_sys_tether_general
-             2. android:drawable/xxx
-
-         So the entire string of each item would be
-
-             USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general
-
-         NOTE: One config can be separated into two or more for readability. Such as
-
-               WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx
-
-               can be separated into
-
-               WIFI|USB;android:drawable/xxx
-               WIFI|BT;android:drawable/xxx
-               USB|BT;android:drawable/xxx
-               WIFI|USB|BT;android:drawable/xxx
-
-         Notification will not show if the downstream type isn't listed in array.
-         Empty array means disable notifications. -->
-    <!-- In AOSP, hotspot is configured to no notification by default. Because status bar has showed
-         an icon on the right side already -->
-    <string-array translatable="false" name="tethering_notification_icons">
-        <item>USB;com.android.networkstack.tethering:drawable/stat_sys_tether_usb</item>
-        <item>BT;com.android.networkstack.tethering:drawable/stat_sys_tether_bluetooth</item>
-        <item>WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general</item>
-    </string-array>
-    <!-- String for tether enable notification title. -->
-    <string name="tethering_notification_title">@string/tethered_notification_title</string>
-    <!-- String for tether enable notification message. -->
-    <string name="tethering_notification_message">@string/tethered_notification_message</string>
+    <!-- Cellular roaming notification is shown when upstream is cellular network and in roaming
+         state. -->
+    <!-- Config for showing upstream roaming notification. -->
+    <bool name="config_upstream_roaming_notification">false</bool>
 </resources>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
index bbba3f3..4e2bb1e 100644
--- a/packages/Tethering/res/values/overlayable.xml
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -23,7 +23,13 @@
             <item type="array" name="config_tether_wifi_p2p_regexs"/>
             <item type="array" name="config_tether_bluetooth_regexs"/>
             <item type="array" name="config_tether_dhcp_range"/>
+            <!-- Use the BPF offload for tethering when the kernel has support. True by default.
+                 If the device doesn't want to support tether BPF offload, this should be false.
+                 Note that this setting could be overridden by device config.
+            -->
+            <item type="bool" name="config_tether_enable_bpf_offload"/>
             <item type="bool" name="config_tether_enable_legacy_dhcp_server"/>
+            <item type="integer" name="config_tether_offload_poll_interval"/>
             <item type="array" name="config_tether_upstream_types"/>
             <item type="bool" name="config_tether_upstream_automatic"/>
             <!-- Configuration values for tethering entitlement check -->
@@ -32,44 +38,6 @@
             <item type="string" name="config_mobile_hotspot_provision_response"/>
             <item type="integer" name="config_mobile_hotspot_provision_check_period"/>
             <item type="string" name="config_wifi_tether_enable"/>
-            <!-- Configuration values for TetheringNotificationUpdater -->
-            <!-- Icons for showing tether enable notification.
-            Each item should have two elements and be separated with ";".
-
-            The first element is downstream types which is one of tethering. This element has to be
-            made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream
-            types and use "," to separate each combinations. Such as
-
-            USB|BT,WIFI|USB|BT
-
-            The second element is icon for the item. This element has to be composed by
-            <package name>:drawable/<resource name>. Such as
-
-            1. com.android.networkstack.tethering:drawable/stat_sys_tether_general
-            2. android:drawable/xxx
-
-            So the entire string of each item would be
-
-            USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general
-
-            NOTE: One config can be separated into two or more for readability. Such as
-
-                  WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx
-
-                  can be separated into
-
-                  WIFI|USB;android:drawable/xxx
-                  WIFI|BT;android:drawable/xxx
-                  USB|BT;android:drawable/xxx
-                  WIFI|USB|BT;android:drawable/xxx
-
-            Notification will not show if the downstream type isn't listed in array.
-            Empty array means disable notifications. -->
-            <item type="array" name="tethering_notification_icons"/>
-            <!-- String for tether enable notification title. -->
-            <item type="string" name="tethering_notification_title"/>
-            <!-- String for tether enable notification message. -->
-            <item type="string" name="tethering_notification_message"/>
             <!-- Params from config.xml that can be overlaid -->
         </policy>
     </overlayable>
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
index ba98a66..d63c7c5 100644
--- a/packages/Tethering/res/values/strings.xml
+++ b/packages/Tethering/res/values/strings.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Shown when the device is tethered -->
     <!-- String for tethered notification title [CHAR LIMIT=200] -->
     <string name="tethered_notification_title">Tethering or hotspot active</string>
@@ -32,4 +32,16 @@
     Internet" settings page. That is currently the tether_settings_title_all string. -->
     <!-- String for tether notification channel name [CHAR LIMIT=200] -->
     <string name="notification_channel_tethering_status">Hotspot &amp; tethering status</string>
-</resources>
\ No newline at end of file
+
+    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_title"></string>
+    <!-- String for no upstream notification message [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_message"></string>
+    <!-- String for no upstream notification disable button [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_disable_button"></string>
+
+    <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+    <string name="upstream_roaming_notification_title"></string>
+    <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+    <string name="upstream_roaming_notification_message"></string>
+</resources>
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
index d6bc063..4710a30 100644
--- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -18,10 +18,12 @@
 
 import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
 
-import android.annotation.NonNull;
 import android.net.LinkAddress;
 import android.util.ArraySet;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import java.net.Inet4Address;
 import java.util.Collection;
 import java.util.Collections;
@@ -160,6 +162,29 @@
         return this;
     }
 
+    /**
+     * Set the client address to tell DHCP server only offer this address.
+     * The client's prefix length is the same as server's.
+     *
+     * <p>If not set, the default value is null.
+     */
+    public DhcpServingParamsParcelExt setSingleClientAddr(@Nullable Inet4Address clientAddr) {
+        this.singleClientAddr = clientAddr == null ? 0 : inet4AddressToIntHTH(clientAddr);
+        return this;
+    }
+
+    /**
+     * Set whether the DHCP server should request a new prefix from IpServer when receiving
+     * DHCPDECLINE message in certain particular link (e.g. there is only one downstream USB
+     * tethering client). If it's false, process DHCPDECLINE message as RFC2131#4.3.3 suggests.
+     *
+     * <p>If not set, the default value is false.
+     */
+    public DhcpServingParamsParcelExt setChangePrefixOnDecline(boolean changePrefixOnDecline) {
+        this.changePrefixOnDecline = changePrefixOnDecline;
+        return this;
+    }
+
     private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
         int[] res = new int[addrs.size()];
         int i = 0;
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 38f8609..659d344 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -18,12 +18,14 @@
 
 import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
 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;
 import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
 import android.net.INetd;
 import android.net.INetworkStackStatusCallback;
@@ -32,13 +34,15 @@
 import android.net.LinkProperties;
 import android.net.MacAddress;
 import android.net.RouteInfo;
+import android.net.TetherOffloadRuleParcel;
 import android.net.TetheredClient;
 import android.net.TetheringManager;
+import android.net.TetheringRequestParcel;
 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.IDhcpEventCallbacks;
 import android.net.dhcp.IDhcpServer;
 import android.net.ip.IpNeighborMonitor.NeighborEvent;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
@@ -46,6 +50,7 @@
 import android.net.shared.RouteUtils;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
+import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.os.Looper;
@@ -56,6 +61,7 @@
 import android.util.SparseArray;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.State;
@@ -111,6 +117,15 @@
     private static final String ETHERNET_IFACE_ADDR = "192.168.50.1";
     private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24;
 
+    // TODO: remove this constant after introducing PrivateAddressCoordinator.
+    private static final List<IpPrefix> NCM_PREFIXES = Collections.unmodifiableList(
+            Arrays.asList(
+                    new IpPrefix("192.168.42.0/24"),
+                    new IpPrefix("192.168.51.0/24"),
+                    new IpPrefix("192.168.52.0/24"),
+                    new IpPrefix("192.168.53.0/24")
+    ));
+
     // TODO: have PanService use some visible version of this constant
     private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1";
     private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
@@ -118,6 +133,8 @@
     // TODO: have this configurable
     private static final int DHCP_LEASE_TIME_SECS = 3600;
 
+    private static final MacAddress NULL_MAC_ADDRESS = MacAddress.fromString("00:00:00:00:00:00");
+
     private static final String TAG = "IpServer";
     private static final boolean DBG = false;
     private static final boolean VDBG = false;
@@ -206,6 +223,8 @@
     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;
+    // request from DHCP server that it wants to have a new prefix
+    public static final int CMD_NEW_PREFIX_REQUEST          = BASE_IPSERVER + 12;
 
     private final State mInitialState;
     private final State mLocalHotspotState;
@@ -221,6 +240,7 @@
     private final int mInterfaceType;
     private final LinkProperties mLinkProperties;
     private final boolean mUsingLegacyDhcp;
+    private final boolean mUsingBpfOffload;
 
     private final Dependencies mDeps;
 
@@ -242,6 +262,10 @@
     private IDhcpServer mDhcpServer;
     private RaParams mLastRaParams;
     private LinkAddress mIpv4Address;
+
+    private LinkAddress mStaticIpv4ServerAddr;
+    private LinkAddress mStaticIpv4ClientAddr;
+
     @NonNull
     private List<TetheredClient> mDhcpLeases = Collections.emptyList();
 
@@ -273,15 +297,31 @@
             return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
                     dstMac);
         }
+
+        // Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
+        // would be error-prone due to generated stable AIDL classes not having a copy constructor.
+        public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
+            final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
+            parcel.inputInterfaceIndex = upstreamIfindex;
+            parcel.outputInterfaceIndex = downstreamIfindex;
+            parcel.destination = address.getAddress();
+            parcel.prefixLength = 128;
+            parcel.srcL2Address = srcMac.toByteArray();
+            parcel.dstL2Address = dstMac.toByteArray();
+            return parcel;
+        }
     }
     private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules =
             new LinkedHashMap<>();
 
     private final IpNeighborMonitor mIpNeighborMonitor;
 
+    // TODO: Add a dependency object to pass the data members or variables from the tethering
+    // object. It helps to reduce the arguments of the constructor.
     public IpServer(
             String ifaceName, Looper looper, int interfaceType, SharedLog log,
-            INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
+            INetd netd, Callback callback, boolean usingLegacyDhcp, boolean usingBpfOffload,
+            Dependencies deps) {
         super(ifaceName, looper);
         mLog = log.forSubComponent(ifaceName);
         mNetd = netd;
@@ -291,6 +331,7 @@
         mInterfaceType = interfaceType;
         mLinkProperties = new LinkProperties();
         mUsingLegacyDhcp = usingLegacyDhcp;
+        mUsingBpfOffload = usingBpfOffload;
         mDeps = deps;
         resetLinkProperties();
         mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -298,7 +339,12 @@
 
         mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
                 new MyNeighborEventConsumer());
-        if (!mIpNeighborMonitor.start()) {
+
+        // IP neighbor monitor monitors the neighbor events for adding/removing offload
+        // forwarding rules per client. If BPF offload is not supported, don't start listening
+        // for neighbor events. See updateIpv6ForwardingRules, addIpv6ForwardingRule,
+        // removeIpv6ForwardingRule.
+        if (mUsingBpfOffload && !mIpNeighborMonitor.start()) {
             mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
         }
 
@@ -429,7 +475,7 @@
                                 handleError();
                             }
                         }
-                    }, new DhcpLeaseCallback());
+                    }, new DhcpEventCallback());
                 } catch (RemoteException e) {
                     throw new IllegalStateException(e);
                 }
@@ -442,13 +488,15 @@
         }
     }
 
-    private class DhcpLeaseCallback extends IDhcpLeaseCallbacks.Stub {
+    private class DhcpEventCallback extends IDhcpEventCallbacks.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);
+                        intToInet4AddressHTH(lease.netAddr), lease.prefixLength,
+                        0 /* flags */, RT_SCOPE_UNIVERSE /* as per RFC6724#3.2 */,
+                        lease.expTime /* deprecationTime */, lease.expTime /* expirationTime */);
 
                 final MacAddress macAddress;
                 try {
@@ -460,7 +508,7 @@
                 }
 
                 final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo(
-                        address, lease.hostname, lease.expTime);
+                        address, lease.hostname);
                 leases.add(new TetheredClient(
                         macAddress,
                         Collections.singletonList(addressInfo),
@@ -474,6 +522,12 @@
         }
 
         @Override
+        public void onNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
+            Objects.requireNonNull(currentPrefix);
+            sendMessage(CMD_NEW_PREFIX_REQUEST, currentPrefix);
+        }
+
+        @Override
         public int getInterfaceVersion() {
             return this.VERSION;
         }
@@ -484,19 +538,38 @@
         }
     }
 
-    private boolean startDhcp(Inet4Address addr, int prefixLen) {
+    private RouteInfo getDirectConnectedRoute(@NonNull final LinkAddress ipv4Address) {
+        Objects.requireNonNull(ipv4Address);
+        return new RouteInfo(PrefixUtils.asIpPrefix(ipv4Address), null, mIfaceName, RTN_UNICAST);
+    }
+
+    private DhcpServingParamsParcel makeServingParams(@NonNull final Inet4Address defaultRouter,
+            @NonNull final Inet4Address dnsServer, @NonNull LinkAddress serverAddr,
+            @Nullable Inet4Address clientAddr) {
+        final boolean changePrefixOnDecline =
+                (mInterfaceType == TetheringManager.TETHERING_NCM && clientAddr == null);
+        return new DhcpServingParamsParcelExt()
+            .setDefaultRouters(defaultRouter)
+            .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
+            .setDnsServers(dnsServer)
+            .setServerAddr(serverAddr)
+            .setMetered(true)
+            .setSingleClientAddr(clientAddr)
+            .setChangePrefixOnDecline(changePrefixOnDecline);
+            // TODO: also advertise link MTU
+    }
+
+    private boolean startDhcp(final LinkAddress serverLinkAddr, final LinkAddress clientLinkAddr) {
         if (mUsingLegacyDhcp) {
             return true;
         }
-        final DhcpServingParamsParcel params;
-        params = new DhcpServingParamsParcelExt()
-                .setDefaultRouters(addr)
-                .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
-                .setDnsServers(addr)
-                .setServerAddr(new LinkAddress(addr, prefixLen))
-                .setMetered(true);
-        // TODO: also advertise link MTU
 
+        final Inet4Address addr = (Inet4Address) serverLinkAddr.getAddress();
+        final Inet4Address clientAddr = clientLinkAddr == null ? null :
+                (Inet4Address) clientLinkAddr.getAddress();
+
+        final DhcpServingParamsParcel params = makeServingParams(addr /* defaultRouter */,
+                addr /* dnsServer */, serverLinkAddr, clientAddr);
         mDhcpServerStartIndex++;
         mDeps.makeDhcpServer(
                 mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
@@ -523,15 +596,16 @@
                 });
                 mDhcpServer = null;
             } catch (RemoteException e) {
-                mLog.e("Error stopping DHCP", e);
+                mLog.e("Error stopping DHCP server", e);
                 // Not much more we can do here
             }
         }
     }
 
-    private boolean configureDhcp(boolean enable, Inet4Address addr, int prefixLen) {
+    private boolean configureDhcp(boolean enable, final LinkAddress serverAddr,
+            final LinkAddress clientAddr) {
         if (enable) {
-            return startDhcp(addr, prefixLen);
+            return startDhcp(serverAddr, clientAddr);
         } else {
             stopDhcp();
             return true;
@@ -544,6 +618,8 @@
         // into calls to InterfaceController, shared with startIPv4().
         mInterfaceCtrl.clearIPv4Address();
         mIpv4Address = null;
+        mStaticIpv4ServerAddr = null;
+        mStaticIpv4ClientAddr = null;
     }
 
     private boolean configureIPv4(boolean enabled) {
@@ -554,7 +630,10 @@
         final Inet4Address srvAddr;
         int prefixLen = 0;
         try {
-            if (mInterfaceType == TetheringManager.TETHERING_USB
+            if (mStaticIpv4ServerAddr != null) {
+                srvAddr = (Inet4Address) mStaticIpv4ServerAddr.getAddress();
+                prefixLen = mStaticIpv4ServerAddr.getPrefixLength();
+            } else if (mInterfaceType == TetheringManager.TETHERING_USB
                     || mInterfaceType == TetheringManager.TETHERING_NCM) {
                 srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR);
                 prefixLen = USB_PREFIX_LENGTH;
@@ -574,7 +653,7 @@
                 // code that calls into NetworkManagementService directly.
                 srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
                 mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
-                return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
+                return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
             }
             mIpv4Address = new LinkAddress(srvAddr, prefixLen);
         } catch (IllegalArgumentException e) {
@@ -599,32 +678,31 @@
             return false;
         }
 
-        if (!configureDhcp(enabled, srvAddr, prefixLen)) {
-            return false;
-        }
-
-        // Directly-connected route.
-        final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
-                mIpv4Address.getPrefixLength());
-        final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST);
         if (enabled) {
             mLinkProperties.addLinkAddress(mIpv4Address);
-            mLinkProperties.addRoute(route);
+            mLinkProperties.addRoute(getDirectConnectedRoute(mIpv4Address));
         } else {
             mLinkProperties.removeLinkAddress(mIpv4Address);
-            mLinkProperties.removeRoute(route);
+            mLinkProperties.removeRoute(getDirectConnectedRoute(mIpv4Address));
         }
-        return true;
+        return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
+    }
+
+    private Inet4Address getRandomIPv4Address(@NonNull final byte[] rawAddr) {
+        final byte[] ipv4Addr = rawAddr;
+        ipv4Addr[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
+        try {
+            return (Inet4Address) InetAddress.getByAddress(ipv4Addr);
+        } catch (UnknownHostException e) {
+            mLog.e("Failed to construct Inet4Address from raw IPv4 addr");
+            return null;
+        }
     }
 
     private String getRandomWifiIPv4Address() {
-        try {
-            byte[] bytes = parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress();
-            bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
-            return InetAddress.getByAddress(bytes).getHostAddress();
-        } catch (Exception e) {
-            return WIFI_HOST_IFACE_ADDR;
-        }
+        final Inet4Address ipv4Addr =
+                getRandomIPv4Address(parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress());
+        return ipv4Addr != null ? ipv4Addr.getHostAddress() : WIFI_HOST_IFACE_ADDR;
     }
 
     private boolean startIPv6() {
@@ -660,7 +738,7 @@
     //
     // TODO: Evaluate using a data structure than is more directly suited to
     // communicating only the relevant information.
-    private void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
+    private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) {
         if (mRaDaemon == null) return;
 
         // Avoid unnecessary work on spurious updates.
@@ -675,15 +753,15 @@
             final String upstreamIface = v6only.getInterfaceName();
 
             params = new RaParams();
-            // 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.
+            // When BPF offload is enabled, 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.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu();
             params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
 
-            if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
+            if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment);
 
             for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
                 if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
@@ -711,21 +789,43 @@
         mLastIPv6UpstreamIfindex = upstreamIfindex;
     }
 
+    private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) {
+        final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
+                mNetd, toBeRemoved);
+        if (removalFailures > 0) {
+            mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
+                    removalFailures));
+        }
+
+        for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
+    }
+
+    private void addRoutesToLocalNetwork(@NonNull final List<RouteInfo> toBeAdded) {
+        try {
+            // It's safe to call networkAddInterface() even if
+            // the interface is already in the local_network.
+            mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
+            try {
+                // Add routes from local network. Note that adding routes that
+                // already exist does not cause an error (EEXIST is silently ignored).
+                RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
+            } catch (IllegalStateException e) {
+                mLog.e("Failed to add IPv4/v6 routes to local table: " + e);
+                return;
+            }
+        } catch (ServiceSpecificException | RemoteException e) {
+            mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
+            return;
+        }
+
+        for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
+    }
+
     private void configureLocalIPv6Routes(
             HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
         // [1] Remove the routes that are deprecated.
         if (!deprecatedPrefixes.isEmpty()) {
-            final ArrayList<RouteInfo> toBeRemoved =
-                    getLocalRoutesFor(mIfaceName, deprecatedPrefixes);
-            // Remove routes from local network.
-            final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
-                    mNetd, toBeRemoved);
-            if (removalFailures > 0) {
-                mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
-                        removalFailures));
-            }
-
-            for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
+            removeRoutesFromLocalNetwork(getLocalRoutesFor(mIfaceName, deprecatedPrefixes));
         }
 
         // [2] Add only the routes that have not previously been added.
@@ -736,24 +836,7 @@
             }
 
             if (!addedPrefixes.isEmpty()) {
-                final ArrayList<RouteInfo> toBeAdded =
-                        getLocalRoutesFor(mIfaceName, addedPrefixes);
-                try {
-                    // It's safe to call networkAddInterface() even if
-                    // the interface is already in the local_network.
-                    mNetd.networkAddInterface(INetd.LOCAL_NET_ID, mIfaceName);
-                    try {
-                        // Add routes from local network. Note that adding routes that
-                        // already exist does not cause an error (EEXIST is silently ignored).
-                        RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
-                    } catch (IllegalStateException e) {
-                        mLog.e("Failed to add IPv6 routes to local table: " + e);
-                    }
-                } catch (ServiceSpecificException | RemoteException e) {
-                    mLog.e("Failed to add " + mIfaceName + " to local table: ", e);
-                }
-
-                for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
+                addRoutesToLocalNetwork(getLocalRoutesFor(mIfaceName, addedPrefixes));
             }
         }
     }
@@ -804,10 +887,13 @@
     }
 
     private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
+        // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF
+        // offload is disabled. Add this check just in case.
+        // TODO: Perhaps remove this protection check.
+        if (!mUsingBpfOffload) return;
+
         try {
-            mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex,
-                    rule.address.getAddress(),  mInterfaceParams.macAddr.toByteArray(),
-                    rule.dstMac.toByteArray());
+            mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
             mIpv6ForwardingRules.put(rule.address, rule);
         } catch (RemoteException | ServiceSpecificException e) {
             mLog.e("Could not add IPv6 downstream rule: ", e);
@@ -815,8 +901,13 @@
     }
 
     private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
+        // Theoretically, we don't need this check because IP neighbor monitor doesn't start if BPF
+        // offload is disabled. Add this check just in case.
+        // TODO: Perhaps remove this protection check.
+        if (!mUsingBpfOffload) return;
+
         try {
-            mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress());
+            mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
             if (removeFromMap) {
                 mIpv6ForwardingRules.remove(rule.address);
             }
@@ -866,9 +957,12 @@
             return;
         }
 
+        // When deleting rules, we still need to pass a non-null MAC, even though it's ignored.
+        // Do this here instead of in the Ipv6ForwardingRule constructor to ensure that we never
+        // add rules with a null MAC, only delete them.
+        MacAddress dstMac = e.isValid() ? e.macAddr : NULL_MAC_ADDRESS;
         Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex,
-                mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr,
-                e.macAddr);
+                mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, dstMac);
         if (e.isValid()) {
             addIpv6ForwardingRule(rule);
         } else {
@@ -884,12 +978,85 @@
         }
     }
 
-    private byte getHopLimit(String upstreamIface) {
+    // TODO: call PrivateAddressCoordinator.requestDownstreamAddress instead of this temporary
+    // logic.
+    private Inet4Address requestDownstreamAddress(@NonNull final IpPrefix currentPrefix) {
+        final int oldIndex = NCM_PREFIXES.indexOf(currentPrefix);
+        if (oldIndex == -1) {
+            mLog.e("current prefix isn't supported for NCM link: " + currentPrefix);
+            return null;
+        }
+
+        final IpPrefix newPrefix = NCM_PREFIXES.get((oldIndex + 1) % NCM_PREFIXES.size());
+        return getRandomIPv4Address(newPrefix.getRawAddress());
+    }
+
+    private void handleNewPrefixRequest(@NonNull final IpPrefix currentPrefix) {
+        if (!currentPrefix.contains(mIpv4Address.getAddress())
+                || currentPrefix.getPrefixLength() != mIpv4Address.getPrefixLength()) {
+            Log.e(TAG, "Invalid prefix: " + currentPrefix);
+            return;
+        }
+
+        final LinkAddress deprecatedLinkAddress = mIpv4Address;
+        final Inet4Address srvAddr = requestDownstreamAddress(currentPrefix);
+        if (srvAddr == null) {
+            mLog.e("Fail to request a new downstream prefix");
+            return;
+        }
+        mIpv4Address = new LinkAddress(srvAddr, currentPrefix.getPrefixLength());
+
+        // Add new IPv4 address on the interface.
+        if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
+            mLog.e("Failed to add new IP " + srvAddr);
+            return;
+        }
+
+        // Remove deprecated routes from local network.
+        removeRoutesFromLocalNetwork(
+                Collections.singletonList(getDirectConnectedRoute(deprecatedLinkAddress)));
+        mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
+
+        // Add new routes to local network.
+        addRoutesToLocalNetwork(
+                Collections.singletonList(getDirectConnectedRoute(mIpv4Address)));
+        mLinkProperties.addLinkAddress(mIpv4Address);
+
+        // Update local DNS caching server with new IPv4 address, otherwise, dnsmasq doesn't
+        // listen on the interface configured with new IPv4 address, that results DNS validation
+        // failure of downstream client even if appropriate routes have been configured.
+        try {
+            mNetd.tetherApplyDnsInterfaces();
+        } catch (ServiceSpecificException | RemoteException e) {
+            mLog.e("Failed to update local DNS caching server");
+            return;
+        }
+        sendLinkProperties();
+
+        // Notify DHCP server that new prefix/route has been applied on IpServer.
+        final Inet4Address clientAddr = mStaticIpv4ClientAddr == null ? null :
+                (Inet4Address) mStaticIpv4ClientAddr.getAddress();
+        final DhcpServingParamsParcel params = makeServingParams(srvAddr /* defaultRouter */,
+                srvAddr /* dnsServer */, mIpv4Address /* serverLinkAddress */, clientAddr);
+        try {
+            mDhcpServer.updateParams(params, new OnHandlerStatusCallback() {
+                    @Override
+                    public void callback(int statusCode) {
+                        if (statusCode != STATUS_SUCCESS) {
+                            mLog.e("Error updating DHCP serving params: " + statusCode);
+                        }
+                    }
+            });
+        } catch (RemoteException e) {
+            mLog.e("Error updating DHCP serving params", e);
+        }
+    }
+
+    private byte getHopLimit(String upstreamIface, int adjustTTL) {
         try {
             int upstreamHopLimit = Integer.parseUnsignedInt(
                     mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit"));
-            // Add one hop to account for this forwarding device
-            upstreamHopLimit++;
+            upstreamHopLimit = upstreamHopLimit + adjustTTL;
             // Cap the hop limit to 255.
             return (byte) Integer.min(upstreamHopLimit, 255);
         } catch (Exception e) {
@@ -934,6 +1101,20 @@
         mLinkProperties.setInterfaceName(mIfaceName);
     }
 
+    private void maybeConfigureStaticIp(final TetheringRequestParcel request) {
+        // Ignore static address configuration if they are invalid or null. In theory, static
+        // addresses should not be invalid here because TetheringManager do not allow caller to
+        // specify invalid static address configuration.
+        if (request == null || request.localIPv4Address == null
+                || request.staticClientAddress == null || !checkStaticAddressConfiguration(
+                request.localIPv4Address, request.staticClientAddress)) {
+            return;
+        }
+
+        mStaticIpv4ServerAddr = request.localIPv4Address;
+        mStaticIpv4ClientAddr = request.staticClientAddress;
+    }
+
     class InitialState extends State {
         @Override
         public void enter() {
@@ -948,9 +1129,11 @@
                     mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
                     switch (message.arg1) {
                         case STATE_LOCAL_ONLY:
+                            maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
                             transitionTo(mLocalHotspotState);
                             break;
                         case STATE_TETHERED:
+                            maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
                             transitionTo(mTetheredState);
                             break;
                         default:
@@ -961,7 +1144,7 @@
                     transitionTo(mUnavailableState);
                     break;
                 case CMD_IPV6_TETHER_UPDATE:
-                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
                     break;
                 default:
                     return NOT_HANDLED;
@@ -979,11 +1162,9 @@
             }
 
             try {
-                final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
-                        mIpv4Address.getPrefixLength());
-                NetdUtils.tetherInterface(mNetd, mIfaceName, ipv4Prefix);
+                NetdUtils.tetherInterface(mNetd, mIfaceName, PrefixUtils.asIpPrefix(mIpv4Address));
             } catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
-                mLog.e("Error Tethering: " + e);
+                mLog.e("Error Tethering", e);
                 mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
                 return;
             }
@@ -1027,7 +1208,7 @@
                     if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
                     break;
                 case CMD_IPV6_TETHER_UPDATE:
-                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+                    updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
                     sendLinkProperties();
                     break;
                 case CMD_IP_FORWARDING_ENABLE_ERROR:
@@ -1035,9 +1216,12 @@
                 case CMD_START_TETHERING_ERROR:
                 case CMD_STOP_TETHERING_ERROR:
                 case CMD_SET_DNS_FORWARDERS_ERROR:
-                    mLastError = TetheringManager.TETHER_ERROR_MASTER_ERROR;
+                    mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
                     transitionTo(mInitialState);
                     break;
+                case CMD_NEW_PREFIX_REQUEST:
+                    handleNewPrefixRequest((IpPrefix) message.obj);
+                    break;
                 default:
                     return false;
             }
@@ -1166,7 +1350,7 @@
                         } catch (RemoteException | ServiceSpecificException e) {
                             mLog.e("Exception enabling NAT: " + e.toString());
                             cleanupUpstream();
-                            mLastError = TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+                            mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
                             transitionTo(mInitialState);
                             return true;
                         }
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
index 5a6d5c1..dd67ddd 100644
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -15,8 +15,11 @@
  */
 package android.net.util;
 
+import android.net.TetheringRequestParcel;
+
 import java.io.FileDescriptor;
 import java.net.SocketException;
+import java.util.Objects;
 
 /**
  * Native methods for tethering utilization.
@@ -38,4 +41,17 @@
     public static int uint16(short s) {
         return s & 0xffff;
     }
+
+    /** Check whether two TetheringRequestParcels are the same. */
+    public static boolean isTetheringRequestEquals(final TetheringRequestParcel request,
+            final TetheringRequestParcel otherRequest) {
+        if (request == otherRequest) return true;
+
+        return request != null && otherRequest != null
+                && request.tetheringType == otherRequest.tetheringType
+                && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address)
+                && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress)
+                && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck
+                && request.showProvisioningUi == otherRequest.showProvisioningUi;
+    }
 }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java b/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
similarity index 98%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java
rename to packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
index cdd1a5d..8a96988 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/ConnectedClientsTracker.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.net.TetheringManager.TETHERING_WIFI;
 
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
similarity index 73%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
rename to packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index e81d6ac..3c6e8d8 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -14,18 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
 import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
 import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_INVALID;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -36,9 +37,8 @@
 import android.content.IntentFilter;
 import android.net.util.SharedLog;
 import android.os.Bundle;
+import android.os.ConditionVariable;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
 import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.os.ResultReceiver;
@@ -46,14 +46,12 @@
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
-import android.util.ArraySet;
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.StateMachine;
-import com.android.networkstack.tethering.R;
 
 import java.io.PrintWriter;
+import java.util.BitSet;
 
 /**
  * Re-check tethering provisioning for enabled downstream tether types.
@@ -70,52 +68,44 @@
     @VisibleForTesting
     protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
     private static final String ACTION_PROVISIONING_ALARM =
-            "com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM";
+            "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM";
     private static final String EXTRA_SUBID = "subId";
 
     private final ComponentName mSilentProvisioningService;
     private static final int MS_PER_HOUR = 60 * 60 * 1000;
-    private static final int EVENT_START_PROVISIONING = 0;
-    private static final int EVENT_STOP_PROVISIONING = 1;
-    private static final int EVENT_UPSTREAM_CHANGED = 2;
-    private static final int EVENT_MAYBE_RUN_PROVISIONING = 3;
-    private static final int EVENT_GET_ENTITLEMENT_VALUE = 4;
+    private static final int DUMP_TIMEOUT = 10_000;
 
-    // The ArraySet contains enabled downstream types, ex:
+    // The BitSet is the bit map of each enabled downstream types, ex:
     // {@link TetheringManager.TETHERING_WIFI}
     // {@link TetheringManager.TETHERING_USB}
     // {@link TetheringManager.TETHERING_BLUETOOTH}
-    private final ArraySet<Integer> mCurrentTethers;
+    private final BitSet mCurrentDownstreams;
+    private final BitSet mExemptedDownstreams;
     private final Context mContext;
-    private final int mPermissionChangeMessageCode;
     private final SharedLog mLog;
     private final SparseIntArray mEntitlementCacheValue;
-    private final EntitlementHandler mHandler;
-    private final StateMachine mTetherMasterSM;
+    private final Handler mHandler;
     // Key: TetheringManager.TETHERING_*(downstream).
     // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
-    private final SparseIntArray mCellularPermitted;
+    private final SparseIntArray mCurrentEntitlementResults;
+    private final Runnable mPermissionChangeCallback;
     private PendingIntent mProvisioningRecheckAlarm;
-    private boolean mCellularUpstreamPermitted = true;
+    private boolean mLastCellularUpstreamPermitted = true;
     private boolean mUsingCellularAsUpstream = false;
     private boolean mNeedReRunProvisioningUi = false;
     private OnUiEntitlementFailedListener mListener;
     private TetheringConfigurationFetcher mFetcher;
 
-    public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
-            int permissionChangeMessageCode) {
-
+    public EntitlementManager(Context ctx, Handler h, SharedLog log,
+            Runnable callback) {
         mContext = ctx;
         mLog = log.forSubComponent(TAG);
-        mCurrentTethers = new ArraySet<Integer>();
-        mCellularPermitted = new SparseIntArray();
+        mCurrentDownstreams = new BitSet();
+        mExemptedDownstreams = new BitSet();
+        mCurrentEntitlementResults = new SparseIntArray();
         mEntitlementCacheValue = new SparseIntArray();
-        mTetherMasterSM = tetherMasterSM;
-        mPermissionChangeMessageCode = permissionChangeMessageCode;
-        final Handler masterHandler = tetherMasterSM.getHandler();
-        // Create entitlement's own handler which is associated with TetherMaster thread
-        // let all entitlement processes run in the same thread.
-        mHandler = new EntitlementHandler(masterHandler.getLooper());
+        mPermissionChangeCallback = callback;
+        mHandler = h;
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
                 null, mHandler);
         mSilentProvisioningService = ComponentName.unflattenFromString(
@@ -154,13 +144,35 @@
      * Check if cellular upstream is permitted.
      */
     public boolean isCellularUpstreamPermitted() {
-        // If provisioning is required and EntitlementManager don't know any downstream,
-        // cellular upstream should not be allowed.
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-        if (mCurrentTethers.size() == 0 && isTetherProvisioningRequired(config)) {
-            return false;
-        }
-        return mCellularUpstreamPermitted;
+
+        return isCellularUpstreamPermitted(config);
+    }
+
+    private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) {
+        if (!isTetherProvisioningRequired(config)) return true;
+
+        // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular
+        // upstream should not be enabled. Enable cellular upstream for exempted downstreams only
+        // when there is no non-exempted downstream.
+        if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty();
+
+        return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1;
+    }
+
+    /**
+     * Set exempted downstream type. If there is only exempted downstream type active,
+     * corresponding entitlement check will not be run and cellular upstream will be permitted
+     * by default. If a privileged app enables tethering without a provisioning check, and then
+     * another app enables tethering of the same type but does not disable the provisioning check,
+     * then the downstream immediately loses exempt status and a provisioning check is run.
+     * If any non-exempted downstream type is active, the cellular upstream will be gated by the
+     * result of entitlement check from non-exempted downstreams. If entitlement check is still
+     * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any
+     * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled.
+     */
+    public void setExemptedDownstreamType(final int type) {
+        mExemptedDownstreams.set(type, true);
     }
 
     /**
@@ -172,36 +184,26 @@
      *        provisioning app UI if there is one.
      */
     public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_START_PROVISIONING,
-                downstreamType, encodeBool(showProvisioningUi)));
-    }
+        if (!isValidDownstreamType(downstreamType)) return;
 
-    private void handleStartProvisioningIfNeeded(int type, boolean showProvisioningUi) {
-        if (!isValidDownstreamType(type)) return;
+        mCurrentDownstreams.set(downstreamType, true);
 
-        if (!mCurrentTethers.contains(type)) mCurrentTethers.add(type);
+        mExemptedDownstreams.set(downstreamType, false);
 
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-        if (isTetherProvisioningRequired(config)) {
-            // If provisioning is required and the result is not available yet,
-            // cellular upstream should not be allowed.
-            if (mCellularPermitted.size() == 0) {
-                mCellularUpstreamPermitted = false;
-            }
-            // If upstream is not cellular, provisioning app would not be launched
-            // till upstream change to cellular.
-            if (mUsingCellularAsUpstream) {
-                if (showProvisioningUi) {
-                    runUiTetherProvisioning(type, config.activeDataSubId);
-                } else {
-                    runSilentTetherProvisioning(type, config.activeDataSubId);
-                }
-                mNeedReRunProvisioningUi = false;
+        if (!isTetherProvisioningRequired(config)) return;
+
+        // If upstream is not cellular, provisioning app would not be launched
+        // till upstream change to cellular.
+        if (mUsingCellularAsUpstream) {
+            if (showProvisioningUi) {
+                runUiTetherProvisioning(downstreamType, config.activeDataSubId);
             } else {
-                mNeedReRunProvisioningUi |= showProvisioningUi;
+                runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
             }
+            mNeedReRunProvisioningUi = false;
         } else {
-            mCellularUpstreamPermitted = true;
+            mNeedReRunProvisioningUi |= showProvisioningUi;
         }
     }
 
@@ -210,18 +212,15 @@
      *
      * @param type tethering type from TetheringManager.TETHERING_{@code *}
      */
-    public void stopProvisioningIfNeeded(int type) {
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0));
-    }
+    public void stopProvisioningIfNeeded(int downstreamType) {
+        if (!isValidDownstreamType(downstreamType)) return;
 
-    private void handleStopProvisioningIfNeeded(int type) {
-        if (!isValidDownstreamType(type)) return;
-
-        mCurrentTethers.remove(type);
+        mCurrentDownstreams.set(downstreamType, false);
         // There are lurking bugs where the notion of "provisioning required" or
         // "tethering supported" may change without without tethering being notified properly.
         // Remove the mapping all the time no matter provisioning is required or not.
-        removeDownstreamMapping(type);
+        removeDownstreamMapping(downstreamType);
+        mExemptedDownstreams.set(downstreamType, false);
     }
 
     /**
@@ -230,31 +229,27 @@
      * @param isCellular whether tethering upstream is cellular.
      */
     public void notifyUpstream(boolean isCellular) {
-        mHandler.sendMessage(mHandler.obtainMessage(
-                EVENT_UPSTREAM_CHANGED, encodeBool(isCellular), 0));
-    }
-
-    private void handleNotifyUpstream(boolean isCellular) {
         if (DBG) {
             mLog.i("notifyUpstream: " + isCellular
-                    + ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted
+                    + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted
                     + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi);
         }
         mUsingCellularAsUpstream = isCellular;
 
         if (mUsingCellularAsUpstream) {
             final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-            handleMaybeRunProvisioning(config);
+            maybeRunProvisioning(config);
         }
     }
 
     /** Run provisioning if needed */
     public void maybeRunProvisioning() {
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_MAYBE_RUN_PROVISIONING));
+        final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
+        maybeRunProvisioning(config);
     }
 
-    private void handleMaybeRunProvisioning(final TetheringConfiguration config) {
-        if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) {
+    private void maybeRunProvisioning(final TetheringConfiguration config) {
+        if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) {
             return;
         }
 
@@ -262,8 +257,9 @@
         // are allowed. Therefore even if the silent check here ends in a failure and the UI later
         // yields success, then the downstream that got a failure will re-evaluate as a result of
         // the change and get the new correct value.
-        for (Integer downstream : mCurrentTethers) {
-            if (mCellularPermitted.indexOfKey(downstream) < 0) {
+        for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0;
+                downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) {
+            if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) {
                 if (mNeedReRunProvisioningUi) {
                     mNeedReRunProvisioningUi = false;
                     runUiTetherProvisioning(downstream, config.activeDataSubId);
@@ -309,7 +305,7 @@
             mLog.log("reevaluateSimCardProvisioning() don't run in TetherMaster thread");
         }
         mEntitlementCacheValue.clear();
-        mCellularPermitted.clear();
+        mCurrentEntitlementResults.clear();
 
         // TODO: refine provisioning check to isTetherProvisioningRequired() ??
         if (!config.hasMobileHotspotProvisionApp()
@@ -319,7 +315,7 @@
         }
 
         if (mUsingCellularAsUpstream) {
-            handleMaybeRunProvisioning(config);
+            maybeRunProvisioning(config);
         }
     }
 
@@ -433,26 +429,25 @@
     }
 
     private void evaluateCellularPermission(final TetheringConfiguration config) {
-        final boolean oldPermitted = mCellularUpstreamPermitted;
-        mCellularUpstreamPermitted = (!isTetherProvisioningRequired(config)
-                || mCellularPermitted.indexOfValue(TETHER_ERROR_NO_ERROR) > -1);
+        final boolean permitted = isCellularUpstreamPermitted(config);
 
         if (DBG) {
-            mLog.i("Cellular permission change from " + oldPermitted
-                    + " to " + mCellularUpstreamPermitted);
+            mLog.i("Cellular permission change from " + mLastCellularUpstreamPermitted
+                    + " to " + permitted);
         }
 
-        if (mCellularUpstreamPermitted != oldPermitted) {
-            mLog.log("Cellular permission change: " + mCellularUpstreamPermitted);
-            mTetherMasterSM.sendMessage(mPermissionChangeMessageCode);
+        if (mLastCellularUpstreamPermitted != permitted) {
+            mLog.log("Cellular permission change: " + permitted);
+            mPermissionChangeCallback.run();
         }
         // Only schedule periodic re-check when tether is provisioned
         // and the result is ok.
-        if (mCellularUpstreamPermitted && mCellularPermitted.size() > 0) {
+        if (permitted && mCurrentEntitlementResults.size() > 0) {
             scheduleProvisioningRechecks(config);
         } else {
             cancelTetherProvisioningRechecks();
         }
+        mLastCellularUpstreamPermitted = permitted;
     }
 
     /**
@@ -464,10 +459,10 @@
      */
     protected void addDownstreamMapping(int type, int resultCode) {
         mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode
-                + " ,TetherTypeRequested: " + mCurrentTethers.contains(type));
-        if (!mCurrentTethers.contains(type)) return;
+                + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type));
+        if (!mCurrentDownstreams.get(type)) return;
 
-        mCellularPermitted.put(type, resultCode);
+        mCurrentEntitlementResults.put(type, resultCode);
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
         evaluateCellularPermission(config);
     }
@@ -478,7 +473,7 @@
      */
     protected void removeDownstreamMapping(int type) {
         mLog.i("removeDownstreamMapping: " + type);
-        mCellularPermitted.delete(type);
+        mCurrentEntitlementResults.delete(type);
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
         evaluateCellularPermission(config);
     }
@@ -494,49 +489,10 @@
         }
     };
 
-    private class EntitlementHandler extends Handler {
-        EntitlementHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case EVENT_START_PROVISIONING:
-                    handleStartProvisioningIfNeeded(msg.arg1, toBool(msg.arg2));
-                    break;
-                case EVENT_STOP_PROVISIONING:
-                    handleStopProvisioningIfNeeded(msg.arg1);
-                    break;
-                case EVENT_UPSTREAM_CHANGED:
-                    handleNotifyUpstream(toBool(msg.arg1));
-                    break;
-                case EVENT_MAYBE_RUN_PROVISIONING:
-                    final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-                    handleMaybeRunProvisioning(config);
-                    break;
-                case EVENT_GET_ENTITLEMENT_VALUE:
-                    handleRequestLatestTetheringEntitlementValue(msg.arg1,
-                            (ResultReceiver) msg.obj, toBool(msg.arg2));
-                    break;
-                default:
-                    mLog.log("Unknown event: " + msg.what);
-                    break;
-            }
-        }
-    }
-
-    private static boolean toBool(int encodedBoolean) {
-        return encodedBoolean != 0;
-    }
-
-    private static int encodeBool(boolean b) {
-        return b ? 1 : 0;
-    }
-
     private static boolean isValidDownstreamType(int type) {
         switch (type) {
             case TETHERING_BLUETOOTH:
+            case TETHERING_ETHERNET:
             case TETHERING_USB:
             case TETHERING_WIFI:
                 return true;
@@ -550,18 +506,33 @@
      * @param pw {@link PrintWriter} is used to print formatted
      */
     public void dump(PrintWriter pw) {
-        pw.print("mCellularUpstreamPermitted: ");
-        pw.println(mCellularUpstreamPermitted);
-        for (Integer type : mCurrentTethers) {
-            pw.print("Type: ");
-            pw.print(typeString(type));
-            if (mCellularPermitted.indexOfKey(type) > -1) {
-                pw.print(", Value: ");
-                pw.println(errorString(mCellularPermitted.get(type)));
-            } else {
-                pw.println(", Value: empty");
+        final ConditionVariable mWaiting = new ConditionVariable();
+        mHandler.post(() -> {
+            pw.print("isCellularUpstreamPermitted: ");
+            pw.println(isCellularUpstreamPermitted());
+            for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0;
+                    type = mCurrentDownstreams.nextSetBit(type + 1)) {
+                pw.print("Type: ");
+                pw.print(typeString(type));
+                if (mCurrentEntitlementResults.indexOfKey(type) > -1) {
+                    pw.print(", Value: ");
+                    pw.println(errorString(mCurrentEntitlementResults.get(type)));
+                } else {
+                    pw.println(", Value: empty");
+                }
             }
+            mWaiting.open();
+        });
+        if (!mWaiting.block(DUMP_TIMEOUT)) {
+            pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms");
         }
+        pw.print("Exempted: [");
+        for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0;
+                type = mExemptedDownstreams.nextSetBit(type + 1)) {
+            pw.print(typeString(type));
+            pw.print(", ");
+        }
+        pw.println("]");
     }
 
     private static String typeString(int type) {
@@ -579,7 +550,7 @@
         switch (value) {
             case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN";
             case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR";
-            case TETHER_ERROR_PROVISION_FAILED: return "TETHER_ERROR_PROVISION_FAILED";
+            case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED";
             default:
                 return String.format("UNKNOWN ERROR (%d)", value);
         }
@@ -592,7 +563,7 @@
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
                 addDownstreamMapping(type, updatedCacheValue);
-                if (updatedCacheValue == TETHER_ERROR_PROVISION_FAILED && notifyFail) {
+                if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) {
                     mListener.onUiEntitlementFailed(type);
                 }
                 if (receiver != null) receiver.send(updatedCacheValue, null);
@@ -635,21 +606,19 @@
             mEntitlementCacheValue.put(type, resultCode);
             return resultCode;
         } else {
-            mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
-            return TETHER_ERROR_PROVISION_FAILED;
+            mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED);
+            return TETHER_ERROR_PROVISIONING_FAILED;
         }
     }
 
     /** Get the last value of the tethering entitlement check. */
     public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
             boolean showEntitlementUi) {
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE,
-                downstream, encodeBool(showEntitlementUi), receiver));
+        if (!isValidDownstreamType(downstream)) {
+            receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null);
+            return;
+        }
 
-    }
-
-    private void handleRequestLatestTetheringEntitlementValue(int downstream,
-            ResultReceiver receiver, boolean showEntitlementUi) {
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
         if (!isTetherProvisioningRequired(config)) {
             receiver.send(TETHER_ERROR_NO_ERROR, null);
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
similarity index 93%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
rename to packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
index 66b9ade8..f3dcaa2 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -161,11 +161,28 @@
     private void updateIPv6TetheringInterfaces() {
         for (IpServer ipServer : mNotifyList) {
             final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
-            ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
+            ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp);
             break;
         }
     }
 
+    private int getTtlAdjustment() {
+        if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) {
+            return 0;
+        }
+
+        // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1
+        // for carrier requirement.
+        if (mUpstreamNetworkState.networkCapabilities.hasTransport(
+                NetworkCapabilities.TRANSPORT_CELLULAR)) {
+            return -1;
+        }
+
+        // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary
+        // distinction between tethered and untethered traffic.
+        return 1;
+    }
+
     private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
         final Downstream ds = findDownstream(ipServer);
         if (ds == null) return null;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
similarity index 84%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
rename to packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
index a402ffa..88c77b0 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/OffloadController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.METERED_NO;
@@ -23,8 +23,11 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkStats.UID_TETHERING;
+import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
 
+import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.usage.NetworkStatsManager;
@@ -39,8 +42,7 @@
 import android.net.netlink.ConntrackMessage;
 import android.net.netlink.NetlinkConstants;
 import android.net.netlink.NetlinkSocket;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.provider.Settings;
@@ -51,7 +53,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
+import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
 
 import java.net.Inet4Address;
 import java.net.Inet6Address;
@@ -89,8 +91,8 @@
     private final Handler mHandler;
     private final OffloadHardwareInterface mHwInterface;
     private final ContentResolver mContentResolver;
-    private final @NonNull OffloadTetheringStatsProvider mStatsProvider;
-    private final @Nullable NetworkStatsProviderCallback mStatsProviderCb;
+    @Nullable
+    private final OffloadTetheringStatsProvider mStatsProvider;
     private final SharedLog mLog;
     private final HashMap<String, LinkProperties> mDownstreams;
     private boolean mConfigInitialized;
@@ -116,27 +118,47 @@
     // includes upstream interfaces that have a quota set.
     private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
 
+    // Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
+    // quota is interface independent and global for tether offload. Note that this is only
+    // accessed on the handler thread and in the constructor.
+    private long mRemainingAlertQuota = QUOTA_UNLIMITED;
+    // Runnable that used to schedule the next stats poll.
+    private final Runnable mScheduledPollingTask = () -> {
+        updateStatsForCurrentUpstream();
+        maybeSchedulePollingStats();
+    };
+
     private int mNatUpdateCallbacksReceived;
     private int mNatUpdateNetlinkErrors;
 
+    @NonNull
+    private final Dependencies mDeps;
+
+    // TODO: Put more parameters in constructor into dependency object.
+    interface Dependencies {
+        @NonNull
+        TetheringConfiguration getTetherConfig();
+    }
+
     public OffloadController(Handler h, OffloadHardwareInterface hwi,
-            ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log) {
+            ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log,
+            @NonNull Dependencies deps) {
         mHandler = h;
         mHwInterface = hwi;
         mContentResolver = contentResolver;
-        mStatsProvider = new OffloadTetheringStatsProvider();
         mLog = log.forSubComponent(TAG);
         mDownstreams = new HashMap<>();
         mExemptPrefixes = new HashSet<>();
         mLastLocalPrefixStrs = new HashSet<>();
-        NetworkStatsProviderCallback providerCallback = null;
+        OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider();
         try {
-            providerCallback = nsm.registerNetworkStatsProvider(
-                    getClass().getSimpleName(), mStatsProvider);
+            nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider);
         } catch (RuntimeException e) {
             Log.wtf(TAG, "Cannot register offload stats provider: " + e);
+            provider = null;
         }
-        mStatsProviderCb = providerCallback;
+        mStatsProvider = provider;
+        mDeps = deps;
     }
 
     /** Start hardware offload. */
@@ -185,7 +207,7 @@
                         // and we need to synchronize stats and limits between
                         // software and hardware forwarding.
                         updateStatsForAllUpstreams();
-                        mStatsProvider.pushTetherStats();
+                        if (mStatsProvider != null) mStatsProvider.pushTetherStats();
                     }
 
                     @Override
@@ -198,7 +220,7 @@
                         // limits set take into account any software tethering
                         // traffic that has been happening in the meantime.
                         updateStatsForAllUpstreams();
-                        mStatsProvider.pushTetherStats();
+                        if (mStatsProvider != null) mStatsProvider.pushTetherStats();
                         // [2] (Re)Push all state.
                         computeAndPushLocalPrefixes(UpdateType.FORCE);
                         pushAllDownstreamState();
@@ -217,10 +239,12 @@
                         // TODO: rev the HAL so that it provides an interface name.
 
                         updateStatsForCurrentUpstream();
-                        mStatsProvider.pushTetherStats();
-                        // Push stats to service does not cause the service react to it immediately.
-                        // Inform the service about limit reached.
-                        if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached();
+                        if (mStatsProvider != null) {
+                            mStatsProvider.pushTetherStats();
+                            // Push stats to service does not cause the service react to it
+                            // immediately. Inform the service about limit reached.
+                            mStatsProvider.notifyLimitReached();
+                        }
                     }
 
                     @Override
@@ -240,6 +264,7 @@
             mLog.log("tethering offload started");
             mNatUpdateCallbacksReceived = 0;
             mNatUpdateNetlinkErrors = 0;
+            maybeSchedulePollingStats();
         }
         return isStarted;
     }
@@ -255,6 +280,9 @@
         mHwInterface.stopOffloadControl();
         mControlInitialized = false;
         mConfigInitialized = false;
+        if (mHandler.hasCallbacks(mScheduledPollingTask)) {
+            mHandler.removeCallbacks(mScheduledPollingTask);
+        }
         if (wasStarted) mLog.log("tethering offload stopped");
     }
 
@@ -263,13 +291,17 @@
     }
 
     @VisibleForTesting
-    class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider {
+    class OffloadTetheringStatsProvider extends NetworkStatsProvider {
         // These stats must only ever be touched on the handler thread.
         @NonNull
         private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
         @NonNull
         private NetworkStats mUidStats = new NetworkStats(0L, 0);
 
+        /**
+         * A helper function that collect tether stats from local hashmap. Note that this does not
+         * invoke binder call.
+         */
         @VisibleForTesting
         @NonNull
         NetworkStats getTetherStats(@NonNull StatsType how) {
@@ -280,14 +312,14 @@
                 final ForwardedStats value = kv.getValue();
                 final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
                         ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
-                stats = stats.addValues(entry);
+                stats = stats.addEntry(entry);
             }
 
             return stats;
         }
 
         @Override
-        public void setLimit(String iface, long quotaBytes) {
+        public void onSetLimit(String iface, long quotaBytes) {
             // Listen for all iface is necessary since upstream might be changed after limit
             // is set.
             mHandler.post(() -> {
@@ -315,13 +347,12 @@
          */
         public void pushTetherStats() {
             // TODO: remove the accumulated stats and report the diff from HAL directly.
-            if (null == mStatsProviderCb) return;
             final NetworkStats ifaceDiff =
                     getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
             final NetworkStats uidDiff =
                     getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
             try {
-                mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
+                notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
                 mIfaceStats = mIfaceStats.add(ifaceDiff);
                 mUidStats = mUidStats.add(uidDiff);
             } catch (RuntimeException e) {
@@ -330,7 +361,7 @@
         }
 
         @Override
-        public void requestStatsUpdate(int token) {
+        public void onRequestStatsUpdate(int 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(() -> {
@@ -340,8 +371,13 @@
         }
 
         @Override
-        public void setAlert(long quotaBytes) {
+        public void onSetAlert(long quotaBytes) {
             // TODO: Ask offload HAL to notify alert without stopping traffic.
+            // Post it to handler thread since it access remaining quota bytes.
+            mHandler.post(() -> {
+                updateAlertQuota(quotaBytes);
+                maybeSchedulePollingStats();
+            });
         }
     }
 
@@ -363,15 +399,70 @@
         // the stats for each interface, and does not observe partial writes where rxBytes is
         // updated and txBytes is not.
         ForwardedStats diff = mHwInterface.getForwardedStats(iface);
+        final long usedAlertQuota = diff.rxBytes + diff.txBytes;
         ForwardedStats base = mForwardedStats.get(iface);
         if (base != null) {
             diff.add(base);
         }
+
+        // Update remaining alert quota if it is still positive.
+        if (mRemainingAlertQuota > 0 && usedAlertQuota > 0) {
+            // Trim to zero if overshoot.
+            final long newQuota = Math.max(mRemainingAlertQuota - usedAlertQuota, 0);
+            updateAlertQuota(newQuota);
+        }
+
         mForwardedStats.put(iface, diff);
         // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from
         // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately.
     }
 
+    /**
+     * Update remaining alert quota, fire the {@link NetworkStatsProvider#notifyAlertReached()}
+     * callback when it reaches zero. This can be invoked either from service setting the alert, or
+     * {@code maybeUpdateStats} when updating stats. Note that this can be only called on
+     * handler thread.
+     *
+     * @param newQuota non-negative value to indicate the new quota, or
+     *                 {@link NetworkStatsProvider#QUOTA_UNLIMITED} to indicate there is no
+     *                 quota.
+     */
+    private void updateAlertQuota(long newQuota) {
+        if (newQuota < QUOTA_UNLIMITED) {
+            throw new IllegalArgumentException("invalid quota value " + newQuota);
+        }
+        if (mRemainingAlertQuota == newQuota) return;
+
+        mRemainingAlertQuota = newQuota;
+        if (mRemainingAlertQuota == 0) {
+            mLog.i("notifyAlertReached");
+            if (mStatsProvider != null) mStatsProvider.notifyAlertReached();
+        }
+    }
+
+    /**
+     * Schedule polling if needed, this will be stopped if offload has been
+     * stopped or remaining quota reaches zero or upstream is empty.
+     * Note that this can be only called on handler thread.
+     */
+    private void maybeSchedulePollingStats() {
+        if (!isPollingStatsNeeded()) return;
+
+        if (mHandler.hasCallbacks(mScheduledPollingTask)) {
+            mHandler.removeCallbacks(mScheduledPollingTask);
+        }
+        mHandler.postDelayed(mScheduledPollingTask,
+                mDeps.getTetherConfig().getOffloadPollInterval());
+    }
+
+    private boolean isPollingStatsNeeded() {
+        return started() && mRemainingAlertQuota > 0
+                && !TextUtils.isEmpty(currentUpstreamInterface())
+                && mDeps.getTetherConfig() != null
+                && mDeps.getTetherConfig().getOffloadPollInterval()
+                >= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
+    }
+
     private boolean maybeUpdateDataLimit(String iface) {
         // setDataLimit may only be called while offload is occurring on this upstream.
         if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) {
@@ -411,6 +502,8 @@
         final String iface = currentUpstreamInterface();
         if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS);
 
+        maybeSchedulePollingStats();
+
         // TODO: examine return code and decide what to do if programming
         // upstream parameters fails (probably just wait for a subsequent
         // onOffloadEvent() callback to tell us offload is available again and
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
similarity index 86%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
rename to packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index b545717..fe92204 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.net.util.TetheringUtils.uint16;
 
@@ -41,6 +41,7 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.util.ArrayList;
+import java.util.NoSuchElementException;
 
 
 /**
@@ -65,6 +66,7 @@
 
     private final Handler mHandler;
     private final SharedLog mLog;
+    private final Dependencies mDeps;
     private IOffloadControl mOffloadControl;
     private TetheringOffloadCallback mTetheringOffloadCallback;
     private ControlCallback mControlCallback;
@@ -125,8 +127,76 @@
     }
 
     public OffloadHardwareInterface(Handler h, SharedLog log) {
+        this(h, log, new Dependencies(log));
+    }
+
+    OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) {
         mHandler = h;
         mLog = log.forSubComponent(TAG);
+        mDeps = deps;
+    }
+
+    /** Capture OffloadHardwareInterface dependencies, for injection. */
+    static class Dependencies {
+        private final SharedLog mLog;
+
+        Dependencies(SharedLog log) {
+            mLog = log;
+        }
+
+        public IOffloadConfig getOffloadConfig() {
+            try {
+                return IOffloadConfig.getService(true /*retry*/);
+            } catch (RemoteException | NoSuchElementException e) {
+                mLog.e("getIOffloadConfig error " + e);
+                return null;
+            }
+        }
+
+        public IOffloadControl getOffloadControl() {
+            try {
+                return IOffloadControl.getService(true /*retry*/);
+            } catch (RemoteException | NoSuchElementException e) {
+                mLog.e("tethering offload control not supported: " + e);
+                return null;
+            }
+        }
+
+        public NativeHandle createConntrackSocket(final int groups) {
+            final FileDescriptor fd;
+            try {
+                fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
+            } catch (ErrnoException e) {
+                mLog.e("Unable to create conntrack socket " + e);
+                return null;
+            }
+
+            final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
+            try {
+                Os.bind(fd, sockAddr);
+            } catch (ErrnoException | SocketException e) {
+                mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
+                try {
+                    SocketUtils.closeSocket(fd);
+                } catch (IOException ie) {
+                    // Nothing we can do here
+                }
+                return null;
+            }
+            try {
+                Os.connect(fd, sockAddr);
+            } catch (ErrnoException | SocketException e) {
+                mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
+                try {
+                    SocketUtils.closeSocket(fd);
+                } catch (IOException ie) {
+                    // Nothing we can do here
+                }
+                return null;
+            }
+
+            return new NativeHandle(fd, true);
+        }
     }
 
     /** Get default value indicating whether offload is supported. */
@@ -140,13 +210,7 @@
      * share them with offload management process.
      */
     public boolean initOffloadConfig() {
-        IOffloadConfig offloadConfig;
-        try {
-            offloadConfig = IOffloadConfig.getService();
-        } catch (RemoteException e) {
-            mLog.e("getIOffloadConfig error " + e);
-            return false;
-        }
+        final IOffloadConfig offloadConfig = mDeps.getOffloadConfig();
         if (offloadConfig == null) {
             mLog.e("Could not find IOffloadConfig service");
             return false;
@@ -158,11 +222,11 @@
         //
         // h2    provides a file descriptor bound to the following netlink groups
         //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
-        final NativeHandle h1 = createConntrackSocket(
+        final NativeHandle h1 = mDeps.createConntrackSocket(
                 NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
         if (h1 == null) return false;
 
-        final NativeHandle h2 = createConntrackSocket(
+        final NativeHandle h2 = mDeps.createConntrackSocket(
                 NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
         if (h2 == null) {
             closeFdInNativeHandle(h1);
@@ -197,53 +261,12 @@
         }
     }
 
-    private NativeHandle createConntrackSocket(final int groups) {
-        FileDescriptor fd;
-        try {
-            fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
-        } catch (ErrnoException e) {
-            mLog.e("Unable to create conntrack socket " + e);
-            return null;
-        }
-
-        final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
-        try {
-            Os.bind(fd, sockAddr);
-        } catch (ErrnoException | SocketException e) {
-            mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
-            try {
-                SocketUtils.closeSocket(fd);
-            } catch (IOException ie) {
-                // Nothing we can do here
-            }
-            return null;
-        }
-        try {
-            Os.connect(fd, sockAddr);
-        } catch (ErrnoException | SocketException e) {
-            mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
-            try {
-                SocketUtils.closeSocket(fd);
-            } catch (IOException ie) {
-                // Nothing we can do here
-            }
-            return null;
-        }
-
-        return new NativeHandle(fd, true);
-    }
-
     /** Initialize the tethering offload HAL. */
     public boolean initOffloadControl(ControlCallback controlCb) {
         mControlCallback = controlCb;
 
         if (mOffloadControl == null) {
-            try {
-                mOffloadControl = IOffloadControl.getService();
-            } catch (RemoteException e) {
-                mLog.e("tethering offload control not supported: " + e);
-                return false;
-            }
+            mOffloadControl = mDeps.getOffloadControl();
             if (mOffloadControl == null) {
                 mLog.e("tethering IOffloadControl.getService() returned null");
                 return false;
@@ -307,7 +330,6 @@
             return stats;
         }
 
-        mLog.log(logmsg + YIELDS + stats);
         return stats;
     }
 
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
similarity index 90%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
rename to packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 5539017..3ce3b45 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.Manifest.permission.NETWORK_STACK;
@@ -39,11 +39,12 @@
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.TetheringManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
 import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
 import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
@@ -59,9 +60,8 @@
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
 
-import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothProfile;
@@ -80,6 +80,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.TetherStatesParcel;
 import android.net.TetheredClient;
@@ -92,6 +93,7 @@
 import android.net.util.InterfaceSet;
 import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
+import android.net.util.TetheringUtils;
 import android.net.util.VersionedBroadcastListener;
 import android.net.wifi.WifiClient;
 import android.net.wifi.WifiManager;
@@ -107,8 +109,10 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceSpecificException;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -196,6 +200,11 @@
     private final SharedLog mLog = new SharedLog(TAG);
     private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
             new RemoteCallbackList<>();
+    // Currently active tethering requests per tethering type. Only one of each type can be
+    // requested at a time. After a tethering type is requested, the map keeps tethering parameters
+    // to be used after the interface comes up asynchronously.
+    private final SparseArray<TetheringRequestParcel> mActiveTetheringRequests =
+            new SparseArray<>();
 
     // used to synchronize public access to members
     private final Object mPublicSync;
@@ -221,6 +230,7 @@
     private final ConnectedClientsTracker mConnectedClientsTracker;
     private final TetheringThreadExecutor mExecutor;
     private final TetheringNotificationUpdater mNotificationUpdater;
+    private final UserManager mUserManager;
     private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
     // All the usage of mTetheringEventCallback should run in the same thread.
     private ITetheringEventCallback mTetheringEventCallback = null;
@@ -250,7 +260,7 @@
         mContext = mDeps.getContext();
         mNetd = mDeps.getINetd(mContext);
         mLooper = mDeps.getTetheringLooper();
-        mNotificationUpdater = mDeps.getNotificationUpdater(mContext);
+        mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
 
         mPublicSync = new Object();
 
@@ -260,12 +270,15 @@
         mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
         mTetherMasterSM.start();
 
-        final NetworkStatsManager statsManager =
-                (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
         mHandler = mTetherMasterSM.getHandler();
-        mOffloadController = new OffloadController(mHandler,
-                mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(),
-                statsManager, mLog);
+        mOffloadController = mDeps.getOffloadController(mHandler, mLog,
+                new OffloadController.Dependencies() {
+
+                    @Override
+                    public TetheringConfiguration getTetherConfig() {
+                        return mConfig;
+                    }
+                });
         mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
                 TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
         mForwardedDownstreams = new LinkedHashSet<>();
@@ -274,8 +287,9 @@
         filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
         // EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream
         // permission is changed according to entitlement check result.
-        mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM, mLog,
-                TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED);
+        mEntitlementMgr = mDeps.getEntitlementManager(mContext, mHandler, mLog,
+                () -> mTetherMasterSM.sendMessage(
+                TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED));
         mEntitlementMgr.setOnUiEntitlementFailedListener((int downstream) -> {
             mLog.log("OBSERVED UiEnitlementFailed");
             stopTethering(downstream);
@@ -294,33 +308,29 @@
 
         mStateReceiver = new StateReceiver();
 
-        final UserManager userManager = (UserManager) mContext.getSystemService(
-                Context.USER_SERVICE);
-        mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mTetheringRestriction = new UserRestrictionActionListener(
+                mUserManager, this, mNotificationUpdater);
         mExecutor = new TetheringThreadExecutor(mHandler);
         mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
+        mNetdCallback = new NetdCallback();
 
         // Load tethering configuration.
         updateConfiguration();
-        // NetdCallback should be registered after updateConfiguration() to ensure
-        // TetheringConfiguration is created.
-        mNetdCallback = new NetdCallback();
+
+        startStateMachineUpdaters();
+    }
+
+    /**
+     * Start to register callbacks.
+     * Call this function when tethering is ready to handle callback events.
+     */
+    private void startStateMachineUpdaters() {
         try {
             mNetd.registerUnsolicitedEventListener(mNetdCallback);
         } catch (RemoteException e) {
             mLog.e("Unable to register netd UnsolicitedEventListener");
         }
-
-        startStateMachineUpdaters(mHandler);
-        startTrackDefaultNetwork();
-
-        final WifiManager wifiManager = getWifiManager();
-        if (wifiManager != null) {
-            wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
-        }
-    }
-
-    private void startStateMachineUpdaters(Handler handler) {
         mCarrierConfigChange.startListening();
         mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener,
                 PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
@@ -333,7 +343,19 @@
         filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
         filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
         filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
-        mContext.registerReceiver(mStateReceiver, filter, null, handler);
+        mContext.registerReceiver(mStateReceiver, filter, null, mHandler);
+
+        final IntentFilter noUpstreamFilter = new IntentFilter();
+        noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING);
+        mContext.registerReceiver(
+                mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler);
+
+        final WifiManager wifiManager = getWifiManager();
+        if (wifiManager != null) {
+            wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
+        }
+
+        startTrackDefaultNetwork();
     }
 
     private class TetheringThreadExecutor implements Executor {
@@ -362,9 +384,10 @@
 
             mActiveDataSubId = subId;
             updateConfiguration();
+            mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId);
             // To avoid launching unexpected provisioning checks, ignore re-provisioning
             // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning()
-            // ill be triggered again when CarrierConfig is loaded.
+            // will be triggered again when CarrierConfig is loaded.
             if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
                 mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
             } else {
@@ -424,9 +447,7 @@
         // 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());
-            }
+            updateConnectedClients(clients);
         }
     }
 
@@ -487,14 +508,35 @@
     }
 
     void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
-        mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
-                request.showProvisioningUi);
-        enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
+        mHandler.post(() -> {
+            final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get(
+                    request.tetheringType);
+            // If tethering is already enabled with a different request,
+            // disable before re-enabling.
+            if (unfinishedRequest != null
+                    && !TetheringUtils.isTetheringRequestEquals(unfinishedRequest, request)) {
+                enableTetheringInternal(request.tetheringType, false /* disabled */, null);
+                mEntitlementMgr.stopProvisioningIfNeeded(request.tetheringType);
+            }
+            mActiveTetheringRequests.put(request.tetheringType, request);
+
+            if (request.exemptFromEntitlementCheck) {
+                mEntitlementMgr.setExemptedDownstreamType(request.tetheringType);
+            } else {
+                mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
+                        request.showProvisioningUi);
+            }
+            enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
+        });
     }
 
     void stopTethering(int type) {
-        enableTetheringInternal(type, false /* disabled */, null);
-        mEntitlementMgr.stopProvisioningIfNeeded(type);
+        mHandler.post(() -> {
+            mActiveTetheringRequests.remove(type);
+
+            enableTetheringInternal(type, false /* disabled */, null);
+            mEntitlementMgr.stopProvisioningIfNeeded(type);
+        });
     }
 
     /**
@@ -503,39 +545,45 @@
      */
     private void enableTetheringInternal(int type, boolean enable,
             final IIntResultListener listener) {
-        int result;
+        int result = TETHER_ERROR_NO_ERROR;
         switch (type) {
             case TETHERING_WIFI:
                 result = setWifiTethering(enable);
-                sendTetherResult(listener, result);
                 break;
             case TETHERING_USB:
                 result = setUsbTethering(enable);
-                sendTetherResult(listener, result);
                 break;
             case TETHERING_BLUETOOTH:
                 setBluetoothTethering(enable, listener);
                 break;
             case TETHERING_NCM:
                 result = setNcmTethering(enable);
-                sendTetherResult(listener, result);
                 break;
             case TETHERING_ETHERNET:
                 result = setEthernetTethering(enable);
-                sendTetherResult(listener, result);
                 break;
             default:
                 Log.w(TAG, "Invalid tether type.");
-                sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE);
+                result = TETHER_ERROR_UNKNOWN_TYPE;
+        }
+
+        // The result of Bluetooth tethering will be sent by #setBluetoothTethering.
+        if (type != TETHERING_BLUETOOTH) {
+            sendTetherResult(listener, result, type);
         }
     }
 
-    private void sendTetherResult(final IIntResultListener listener, int result) {
+    private void sendTetherResult(final IIntResultListener listener, final int result,
+            final int type) {
         if (listener != null) {
             try {
                 listener.onResult(result);
             } catch (RemoteException e) { }
         }
+
+        // If changing tethering fail, remove corresponding request
+        // no matter who trigger the start/stop.
+        if (result != TETHER_ERROR_NO_ERROR) mActiveTetheringRequests.remove(type);
     }
 
     private int setWifiTethering(final boolean enable) {
@@ -557,7 +605,7 @@
             Binder.restoreCallingIdentity(ident);
         }
 
-        return TETHER_ERROR_MASTER_ERROR;
+        return TETHER_ERROR_INTERNAL_ERROR;
     }
 
     private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) {
@@ -565,7 +613,7 @@
         if (adapter == null || !adapter.isEnabled()) {
             Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
                     + (adapter == null));
-            sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL);
+            sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL, TETHERING_BLUETOOTH);
             return;
         }
 
@@ -593,8 +641,8 @@
                 // We should figure out a way to bubble up that failure instead of sending success.
                 final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
                         ? TETHER_ERROR_NO_ERROR
-                        : TETHER_ERROR_MASTER_ERROR;
-                sendTetherResult(listener, result);
+                        : TETHER_ERROR_INTERNAL_ERROR;
+                sendTetherResult(listener, result, TETHERING_BLUETOOTH);
                 adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
             }
         }, BluetoothProfile.PAN);
@@ -605,27 +653,30 @@
                 Context.ETHERNET_SERVICE);
         synchronized (mPublicSync) {
             if (enable) {
-                if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR;
+                if (mEthernetCallback != null) {
+                    Log.d(TAG, "Ethernet tethering already started");
+                    return TETHER_ERROR_NO_ERROR;
+                }
 
                 mEthernetCallback = new EthernetCallback();
                 mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
             } else {
                 stopEthernetTetheringLocked();
-                if (mEthernetCallback != null) {
-                    mEthernetIfaceRequest.release();
-                    mEthernetCallback = null;
-                    mEthernetIfaceRequest = null;
-                }
             }
         }
         return TETHER_ERROR_NO_ERROR;
     }
 
     private void stopEthernetTetheringLocked() {
-        if (mConfiguredEthernetIface == null) return;
-        changeInterfaceState(mConfiguredEthernetIface, IpServer.STATE_AVAILABLE);
-        stopTrackingInterfaceLocked(mConfiguredEthernetIface);
-        mConfiguredEthernetIface = null;
+        if (mConfiguredEthernetIface != null) {
+            stopTrackingInterfaceLocked(mConfiguredEthernetIface);
+            mConfiguredEthernetIface = null;
+        }
+        if (mEthernetCallback != null) {
+            mEthernetIfaceRequest.release();
+            mEthernetCallback = null;
+            mEthernetIfaceRequest = null;
+        }
     }
 
     private class EthernetCallback implements EthernetManager.TetheredInterfaceCallback {
@@ -672,12 +723,18 @@
                 Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
                 return TETHER_ERROR_UNAVAIL_IFACE;
             }
-            // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's
-            // queue but not yet processed, this will be a no-op and it will not
-            // return an error.
+            // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet
+            // processed, this will be a no-op and it will not return an error.
             //
-            // TODO: reexamine the threading and messaging model.
-            tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState);
+            // This code cannot race with untether() because they both synchronize on mPublicSync.
+            // TODO: reexamine the threading and messaging model to totally remove mPublicSync.
+            final int type = tetherState.ipServer.interfaceType();
+            final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null);
+            if (request != null) {
+                mActiveTetheringRequests.delete(type);
+            }
+            tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0,
+                    request);
             return TETHER_ERROR_NO_ERROR;
         }
     }
@@ -726,7 +783,7 @@
 
     // TODO: Figure out how to update for local hotspot mode interfaces.
     private void sendTetherStateChangedBroadcast() {
-        if (!mDeps.isTetheringSupported()) return;
+        if (!isTetheringSupported()) return;
 
         final ArrayList<String> availableList = new ArrayList<>();
         final ArrayList<String> tetherList = new ArrayList<>();
@@ -815,6 +872,8 @@
             } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {
                 mLog.log("OBSERVED data saver changed");
                 handleDataSaverChanged();
+            } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) {
+                untetherAll();
             }
         }
 
@@ -882,8 +941,10 @@
                     case WifiManager.WIFI_AP_STATE_ENABLED:
                         enableWifiIpServingLocked(ifname, ipmode);
                         break;
-                    case WifiManager.WIFI_AP_STATE_DISABLED:
                     case WifiManager.WIFI_AP_STATE_DISABLING:
+                        // We can see this state on the way to disabled.
+                        break;
+                    case WifiManager.WIFI_AP_STATE_DISABLED:
                     case WifiManager.WIFI_AP_STATE_FAILED:
                     default:
                         disableWifiIpServingLocked(ifname, curState);
@@ -957,14 +1018,22 @@
     }
 
     @VisibleForTesting
+    boolean isTetheringActive() {
+        return mActiveTetheringRequests.size() > 0;
+    }
+
+    @VisibleForTesting
     protected static class UserRestrictionActionListener {
-        private final UserManager mUserManager;
+        private final UserManager mUserMgr;
         private final Tethering mWrapper;
+        private final TetheringNotificationUpdater mNotificationUpdater;
         public boolean mDisallowTethering;
 
-        public UserRestrictionActionListener(UserManager um, Tethering wrapper) {
-            mUserManager = um;
+        public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper,
+                @NonNull TetheringNotificationUpdater updater) {
+            mUserMgr = um;
             mWrapper = wrapper;
+            mNotificationUpdater = updater;
             mDisallowTethering = false;
         }
 
@@ -972,7 +1041,7 @@
             // getUserRestrictions gets restriction for this process' user, which is the primary
             // user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary
             // user. See UserManager.DISALLOW_CONFIG_TETHERING.
-            final Bundle restrictions = mUserManager.getUserRestrictions();
+            final Bundle restrictions = mUserMgr.getUserRestrictions();
             final boolean newlyDisallowed =
                     restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
             final boolean prevDisallowed = mDisallowTethering;
@@ -983,13 +1052,22 @@
                 return;
             }
 
-            // TODO: Add user restrictions notification.
-            final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0);
-
-            if (newlyDisallowed && isTetheringActiveOnDevice) {
-                mWrapper.untetherAll();
-                // TODO(b/148139325): send tetheringSupported on restriction change
+            if (!newlyDisallowed) {
+                // Clear the restricted notification when user is allowed to have tethering
+                // function.
+                mNotificationUpdater.tetheringRestrictionLifted();
+                return;
             }
+
+            if (mWrapper.isTetheringActive()) {
+                // Restricted notification is shown when tethering function is disallowed on
+                // user's device.
+                mNotificationUpdater.notifyTetheringDisabledByRestriction();
+
+                // Untether from all downstreams since tethering is disallowed.
+                mWrapper.untetherAll();
+            }
+            // TODO(b/148139325): send tetheringSupported on restriction change
         }
     }
 
@@ -1416,7 +1494,7 @@
             if (mTetherUpstream != newUpstream) {
                 mTetherUpstream = newUpstream;
                 mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream);
-                reportUpstreamChanged(mTetherUpstream);
+                reportUpstreamChanged(ns);
             }
         }
 
@@ -1458,7 +1536,7 @@
             } else {
                 dnsServers = mConfig.defaultIPv4DNS;
             }
-            final int netId = (network != null) ? network.netId : NETID_UNSET;
+            final int netId = (network != null) ? network.getNetId() : NETID_UNSET;
             try {
                 mNetd.tetherDnsSet(netId, dnsServers);
                 mLog.log(String.format(
@@ -1523,6 +1601,7 @@
             mIPv6TetheringCoordinator.removeActiveDownstream(who);
             mOffload.excludeDownstreamInterface(who.interfaceName());
             mForwardedDownstreams.remove(who);
+            updateConnectedClients(null /* wifiClients */);
 
             // If this is a Wi-Fi interface, tell WifiManager of any errors
             // or the inactive serving state.
@@ -1537,7 +1616,8 @@
             }
         }
 
-        private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
+        @VisibleForTesting
+        void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
             if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
                 mOffload.sendOffloadExemptPrefixes((Set<IpPrefix>) o);
                 return;
@@ -1563,6 +1643,9 @@
 
             switch (arg1) {
                 case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
+                    if (ns.network.equals(mTetherUpstream)) {
+                        mNotificationUpdater.onUpstreamCapabilitiesChanged(ns.networkCapabilities);
+                    }
                     handleNewUpstreamNetworkState(ns);
                     break;
                 case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
@@ -1892,10 +1975,12 @@
     /** Get the latest value of the tethering entitlement check. */
     void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
             boolean showEntitlementUi) {
-        if (receiver != null) {
+        if (receiver == null) return;
+
+        mHandler.post(() -> {
             mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver,
                     showEntitlementUi);
-        }
+        });
     }
 
     /** Register tethering event callback */
@@ -1907,7 +1992,7 @@
         mHandler.post(() -> {
             mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
             final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
-            parcel.tetheringSupported = mDeps.isTetheringSupported();
+            parcel.tetheringSupported = isTetheringSupported();
             parcel.upstreamNetwork = mTetherUpstream;
             parcel.config = mConfig.toStableParcelable();
             parcel.states =
@@ -1946,8 +2031,10 @@
         });
     }
 
-    private void reportUpstreamChanged(Network network) {
+    private void reportUpstreamChanged(UpstreamNetworkState ns) {
         final int length = mTetheringEventCallbacks.beginBroadcast();
+        final Network network = (ns != null) ? ns.network : null;
+        final NetworkCapabilities capabilities = (ns != null) ? ns.networkCapabilities : null;
         try {
             for (int i = 0; i < length; i++) {
                 try {
@@ -1959,6 +2046,9 @@
         } finally {
             mTetheringEventCallbacks.finishBroadcast();
         }
+        // Need to notify capabilities change after upstream network changed because new network's
+        // capabilities should be checked every time.
+        mNotificationUpdater.onUpstreamCapabilitiesChanged(capabilities);
     }
 
     private void reportConfigurationChanged(TetheringConfigurationParcel config) {
@@ -2025,6 +2115,20 @@
         }
     }
 
+    // if ro.tether.denied = true we default to no tethering
+    // gservices could set the secure setting to 1 though to enable it on a build where it
+    // had previously been turned off.
+    boolean isTetheringSupported() {
+        final int defaultVal =
+                SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
+        final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
+        final boolean tetherEnabledInSettings = tetherSupported
+                && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
+
+        return tetherEnabledInSettings && hasTetherableConfiguration();
+    }
+
     void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
         // Binder.java closes the resource for us.
         @SuppressWarnings("resource")
@@ -2105,6 +2209,12 @@
         return false;
     }
 
+    private void updateConnectedClients(final List<WifiClient> wifiClients) {
+        if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, wifiClients)) {
+            reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
+        }
+    }
+
     private IpServer.Callback makeControlCallback() {
         return new IpServer.Callback() {
             @Override
@@ -2119,10 +2229,7 @@
 
             @Override
             public void dhcpLeasesChanged() {
-                if (mConnectedClientsTracker.updateConnectedClients(
-                        mForwardedDownstreams, null /* wifiClients */)) {
-                    reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
-                }
+                updateConnectedClients(null /* wifiClients */);
             }
         };
     }
@@ -2145,7 +2252,7 @@
         // If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
         // Thus we give a chance for TetherMasterSM to recover to InitialState
         // by sending CMD_CLEAR_ERROR
-        if (error == TETHER_ERROR_MASTER_ERROR) {
+        if (error == TETHER_ERROR_INTERNAL_ERROR) {
             mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who);
         }
         int which;
@@ -2207,7 +2314,7 @@
         final TetherState tetherState = new TetherState(
                 new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
-                             mDeps.getIpServerDependencies()));
+                             mConfig.enableBpfOffload, mDeps.getIpServerDependencies()));
         mTetherStates.put(iface, tetherState);
         tetherState.ipServer.start();
     }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
similarity index 84%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
rename to packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 7e9e26f..48a600d 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.content.Context.TELEPHONY_SERVICE;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
@@ -33,7 +33,6 @@
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.networkstack.tethering.R;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -74,11 +73,23 @@
     private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
 
     /**
+     * Override enabling BPF offload configuration for tethering.
+     */
+    public static final String OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD =
+            "override_tether_enable_bpf_offload";
+
+    /**
      * Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
      */
     public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
             "tether_enable_legacy_dhcp_server";
 
+    /**
+     * Default value that used to periodic polls tether offload stats from tethering offload HAL
+     * to make the data warnings work.
+     */
+    public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000;
+
     public final String[] tetherableUsbRegexs;
     public final String[] tetherableWifiRegexs;
     public final String[] tetherableWifiP2pRegexs;
@@ -90,6 +101,8 @@
     public final String[] legacyDhcpRanges;
     public final String[] defaultIPv4DNS;
     public final boolean enableLegacyDhcpServer;
+    // TODO: Add to TetheringConfigurationParcel if required.
+    public final boolean enableBpfOffload;
 
     public final String[] provisioningApp;
     public final String provisioningAppNoUi;
@@ -97,6 +110,8 @@
 
     public final int activeDataSubId;
 
+    private final int mOffloadPollInterval;
+
     public TetheringConfiguration(Context ctx, SharedLog log, int id) {
         final SharedLog configLog = log.forSubComponent("config");
 
@@ -117,11 +132,12 @@
         isDunRequired = checkDunRequired(ctx);
 
         chooseUpstreamAutomatically = getResourceBoolean(
-                res, R.bool.config_tether_upstream_automatic);
+                res, R.bool.config_tether_upstream_automatic, false /** defaultValue */);
         preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
 
         legacyDhcpRanges = getLegacyDhcpRanges(res);
         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
+        enableBpfOffload = getEnableBpfOffload(res);
         enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
 
         provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
@@ -130,6 +146,10 @@
                 R.integer.config_mobile_hotspot_provision_check_period,
                 0 /* No periodic re-check */);
 
+        mOffloadPollInterval = getResourceInteger(res,
+                R.integer.config_tether_offload_poll_interval,
+                DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+
         configLog.log(toString());
     }
 
@@ -190,10 +210,16 @@
         dumpStringArray(pw, "legacyDhcpRanges", legacyDhcpRanges);
         dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
 
+        pw.print("offloadPollInterval: ");
+        pw.println(mOffloadPollInterval);
+
         dumpStringArray(pw, "provisioningApp", provisioningApp);
         pw.print("provisioningAppNoUi: ");
         pw.println(provisioningAppNoUi);
 
+        pw.print("enableBpfOffload: ");
+        pw.println(enableBpfOffload);
+
         pw.print("enableLegacyDhcpServer: ");
         pw.println(enableLegacyDhcpServer);
     }
@@ -209,10 +235,12 @@
                 makeString(tetherableBluetoothRegexs)));
         sj.add(String.format("isDunRequired:%s", isDunRequired));
         sj.add(String.format("chooseUpstreamAutomatically:%s", chooseUpstreamAutomatically));
+        sj.add(String.format("offloadPollInterval:%d", mOffloadPollInterval));
         sj.add(String.format("preferredUpstreamIfaceTypes:%s",
                 toIntArray(preferredUpstreamIfaceTypes)));
         sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
         sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
+        sj.add(String.format("enableBpfOffload:%s", enableBpfOffload));
         sj.add(String.format("enableLegacyDhcpServer:%s", enableLegacyDhcpServer));
         return String.format("TetheringConfiguration{%s}", sj.toString());
     }
@@ -247,6 +275,10 @@
         return (tm != null) ? tm.isTetheringApnRequired() : false;
     }
 
+    public int getOffloadPollInterval() {
+        return mOffloadPollInterval;
+    }
+
     private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
         final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
         final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
@@ -313,11 +345,11 @@
         }
     }
 
-    private static boolean getResourceBoolean(Resources res, int resId) {
+    private static boolean getResourceBoolean(Resources res, int resId, boolean defaultValue) {
         try {
             return res.getBoolean(resId);
         } catch (Resources.NotFoundException e404) {
-            return false;
+            return defaultValue;
         }
     }
 
@@ -338,14 +370,36 @@
         }
     }
 
+    private boolean getEnableBpfOffload(final Resources res) {
+        // Get BPF offload config
+        // Priority 1: Device config
+        // Priority 2: Resource config
+        // Priority 3: Default value
+        final boolean defaultValue = getResourceBoolean(
+                res, R.bool.config_tether_enable_bpf_offload, true /** default value */);
+
+        return getDeviceConfigBoolean(OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD, defaultValue);
+    }
+
     private boolean getEnableLegacyDhcpServer(final Resources res) {
-        return getResourceBoolean(res, R.bool.config_tether_enable_legacy_dhcp_server)
-                || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER);
+        return getResourceBoolean(
+                res, R.bool.config_tether_enable_legacy_dhcp_server, false /** defaultValue */)
+                || getDeviceConfigBoolean(
+                TETHER_ENABLE_LEGACY_DHCP_SERVER, false /** defaultValue */);
+    }
+
+    private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
+        // Due to the limitation of static mock for testing, using #getDeviceConfigProperty instead
+        // of DeviceConfig#getBoolean. If using #getBoolean here, the test can't know that the
+        // returned boolean value comes from device config or default value (because of null
+        // property string). See the test case testBpfOffload{*} in TetheringConfigurationTest.java.
+        final String value = getDeviceConfigProperty(name);
+        return value != null ? Boolean.parseBoolean(value) : defaultValue;
     }
 
     @VisibleForTesting
-    protected boolean getDeviceConfigBoolean(final String name) {
-        return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, false /** defaultValue */);
+    protected String getDeviceConfigProperty(String name) {
+        return DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, name);
     }
 
     private Resources getResources(Context ctx, int subId) {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
similarity index 79%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
rename to packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 0330dad..ce546c7 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.net.INetd;
@@ -47,6 +48,19 @@
     }
 
     /**
+     * Get a reference to the offload controller to be used by tethering.
+     */
+    @NonNull
+    public OffloadController getOffloadController(@NonNull Handler h,
+            @NonNull SharedLog log, @NonNull OffloadController.Dependencies deps) {
+        final NetworkStatsManager statsManager =
+                (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE);
+        return new OffloadController(h, getOffloadHardwareInterface(h, log),
+                getContext().getContentResolver(), statsManager, log, deps);
+    }
+
+
+    /**
      * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
      */
     public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
@@ -82,9 +96,9 @@
     /**
      * Get a reference to the EntitlementManager to be used by tethering.
      */
-    public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
-            SharedLog log, int what) {
-        return new EntitlementManager(ctx, target, log, what);
+    public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
+            Runnable callback) {
+        return new EntitlementManager(ctx, h, log, callback);
     }
 
     /**
@@ -106,8 +120,9 @@
     /**
      * Get a reference to the TetheringNotificationUpdater to be used by tethering.
      */
-    public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) {
-        return new TetheringNotificationUpdater(ctx);
+    public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx,
+            @NonNull final Looper looper) {
+        return new TetheringNotificationUpdater(ctx, looper);
     }
 
     /**
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
similarity index 98%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
rename to packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
index 4dd6830..ff38f71 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import android.annotation.Nullable;
 import android.net.LinkProperties;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
new file mode 100644
index 0000000..d03deda
--- /dev/null
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
@@ -0,0 +1,348 @@
+/*
+ * 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.networkstack.tethering;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.text.TextUtils.isEmpty;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.net.NetworkCapabilities;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.SparseArray;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IntDef;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A class to display tethering-related notifications.
+ *
+ * <p>This class is not thread safe, it is intended to be used only from the tethering handler
+ * thread. However the constructor is an exception, as it is called on another thread ;
+ * therefore for thread safety all members of this class MUST either be final or initialized
+ * to their default value (0, false or null).
+ *
+ * @hide
+ */
+public class TetheringNotificationUpdater {
+    private static final String TAG = TetheringNotificationUpdater.class.getSimpleName();
+    private static final String CHANNEL_ID = "TETHERING_STATUS";
+    private static final String WIFI_DOWNSTREAM = "WIFI";
+    private static final String USB_DOWNSTREAM = "USB";
+    private static final String BLUETOOTH_DOWNSTREAM = "BT";
+    @VisibleForTesting
+    static final String ACTION_DISABLE_TETHERING =
+            "com.android.server.connectivity.tethering.DISABLE_TETHERING";
+    private static final boolean NOTIFY_DONE = true;
+    private static final boolean NO_NOTIFY = false;
+    @VisibleForTesting
+    static final int EVENT_SHOW_NO_UPSTREAM = 1;
+    // Id to update and cancel restricted notification. Must be unique within the tethering app.
+    @VisibleForTesting
+    static final int RESTRICTED_NOTIFICATION_ID = 1001;
+    // Id to update and cancel no upstream notification. Must be unique within the tethering app.
+    @VisibleForTesting
+    static final int NO_UPSTREAM_NOTIFICATION_ID = 1002;
+    // Id to update and cancel roaming notification. Must be unique within the tethering app.
+    @VisibleForTesting
+    static final int ROAMING_NOTIFICATION_ID = 1003;
+    @VisibleForTesting
+    static final int NO_ICON_ID = 0;
+    @VisibleForTesting
+    static final int DOWNSTREAM_NONE = 0;
+    // Refer to TelephonyManager#getSimCarrierId for more details about carrier id.
+    @VisibleForTesting
+    static final int VERIZON_CARRIER_ID = 1839;
+    private final Context mContext;
+    private final NotificationManager mNotificationManager;
+    private final NotificationChannel mChannel;
+    private final Handler mHandler;
+
+    // WARNING : the constructor is called on a different thread. Thread safety therefore
+    // relies on these values being initialized to 0, false or null, and not any other value. If you
+    // need to change this, you will need to change the thread where the constructor is invoked, or
+    // to introduce synchronization.
+    // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
+    // This value has to be made 1 2 and 4, and OR'd with the others.
+    private int mDownstreamTypesMask = DOWNSTREAM_NONE;
+    private boolean mNoUpstream = false;
+    private boolean mRoaming = false;
+
+    // WARNING : this value is not able to being initialized to 0 and must have volatile because
+    // telephony service is not guaranteed that is up before tethering service starts. If telephony
+    // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid
+    // subscription id(0) to query resources. Therefore, initialized subscription id must be
+    // INVALID_SUBSCRIPTION_ID.
+    private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            RESTRICTED_NOTIFICATION_ID,
+            NO_UPSTREAM_NOTIFICATION_ID,
+            ROAMING_NOTIFICATION_ID
+    })
+    @interface NotificationId {}
+
+    private static final class MccMncOverrideInfo {
+        public final String visitedMccMnc;
+        public final int homeMcc;
+        public final int homeMnc;
+        MccMncOverrideInfo(String visitedMccMnc, int mcc, int mnc) {
+            this.visitedMccMnc = visitedMccMnc;
+            this.homeMcc = mcc;
+            this.homeMnc = mnc;
+        }
+    }
+
+    private static final SparseArray<MccMncOverrideInfo> sCarrierIdToMccMnc = new SparseArray<>();
+
+    static {
+        sCarrierIdToMccMnc.put(VERIZON_CARRIER_ID, new MccMncOverrideInfo("20404", 311, 480));
+    }
+
+    public TetheringNotificationUpdater(@NonNull final Context context,
+            @NonNull final Looper looper) {
+        mContext = context;
+        mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+        mChannel = new NotificationChannel(
+                CHANNEL_ID,
+                context.getResources().getString(R.string.notification_channel_tethering_status),
+                NotificationManager.IMPORTANCE_LOW);
+        mNotificationManager.createNotificationChannel(mChannel);
+        mHandler = new NotificationHandler(looper);
+    }
+
+    private class NotificationHandler extends Handler {
+        NotificationHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case EVENT_SHOW_NO_UPSTREAM:
+                    notifyTetheringNoUpstream();
+                    break;
+            }
+        }
+    }
+
+    /** Called when downstream has changed */
+    public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
+        updateActiveNotifications(
+                mActiveDataSubId, downstreamTypesMask, mNoUpstream, mRoaming);
+    }
+
+    /** Called when active data subscription id changed */
+    public void onActiveDataSubscriptionIdChanged(final int subId) {
+        updateActiveNotifications(subId, mDownstreamTypesMask, mNoUpstream, mRoaming);
+    }
+
+    /** Called when upstream network capabilities changed */
+    public void onUpstreamCapabilitiesChanged(@Nullable final NetworkCapabilities capabilities) {
+        final boolean isNoUpstream = (capabilities == null);
+        final boolean isRoaming = capabilities != null
+                && !capabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+        updateActiveNotifications(
+                mActiveDataSubId, mDownstreamTypesMask, isNoUpstream, isRoaming);
+    }
+
+    @NonNull
+    @VisibleForTesting
+    final Handler getHandler() {
+        return mHandler;
+    }
+
+    @NonNull
+    @VisibleForTesting
+    Resources getResourcesForSubId(@NonNull final Context context, final int subId) {
+        final Resources res = SubscriptionManager.getResourcesForSubId(context, subId);
+        final TelephonyManager tm =
+                ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+                        .createForSubscriptionId(mActiveDataSubId);
+        final int carrierId = tm.getSimCarrierId();
+        final String mccmnc = tm.getSimOperator();
+        final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId);
+        if (overrideInfo != null && overrideInfo.visitedMccMnc.equals(mccmnc)) {
+            // Re-configure MCC/MNC value to specific carrier to get right resources.
+            final Configuration config = res.getConfiguration();
+            config.mcc = overrideInfo.homeMcc;
+            config.mnc = overrideInfo.homeMnc;
+            return context.createConfigurationContext(config).getResources();
+        }
+        return res;
+    }
+
+    private void updateActiveNotifications(final int subId, final int downstreamTypes,
+            final boolean noUpstream, final boolean isRoaming) {
+        final boolean tetheringActiveChanged =
+                (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE);
+        final boolean subIdChanged = subId != mActiveDataSubId;
+        final boolean upstreamChanged = noUpstream != mNoUpstream;
+        final boolean roamingChanged = isRoaming != mRoaming;
+        final boolean updateAll = tetheringActiveChanged || subIdChanged;
+        mActiveDataSubId = subId;
+        mDownstreamTypesMask = downstreamTypes;
+        mNoUpstream = noUpstream;
+        mRoaming = isRoaming;
+
+        if (updateAll || upstreamChanged) updateNoUpstreamNotification();
+        if (updateAll || roamingChanged) updateRoamingNotification();
+    }
+
+    private void updateNoUpstreamNotification() {
+        final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
+
+        if (tetheringInactive || !mNoUpstream || setupNoUpstreamNotification() == NO_NOTIFY) {
+            clearNotification(NO_UPSTREAM_NOTIFICATION_ID);
+            mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM);
+        }
+    }
+
+    private void updateRoamingNotification() {
+        final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
+
+        if (tetheringInactive || !mRoaming || setupRoamingNotification() == NO_NOTIFY) {
+            clearNotification(ROAMING_NOTIFICATION_ID);
+        }
+    }
+
+    @VisibleForTesting
+    void tetheringRestrictionLifted() {
+        clearNotification(RESTRICTED_NOTIFICATION_ID);
+    }
+
+    private void clearNotification(@NotificationId final int id) {
+        mNotificationManager.cancel(null /* tag */, id);
+    }
+
+    @VisibleForTesting
+    void notifyTetheringDisabledByRestriction() {
+        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+        final String title = res.getString(R.string.disable_tether_notification_title);
+        final String message = res.getString(R.string.disable_tether_notification_message);
+        if (isEmpty(title) || isEmpty(message)) return;
+
+        final PendingIntent pi = PendingIntent.getActivity(
+                mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                new Intent(Settings.ACTION_TETHER_SETTINGS),
+                Intent.FLAG_ACTIVITY_NEW_TASK,
+                null /* options */);
+
+        showNotification(R.drawable.stat_sys_tether_general, title, message,
+                RESTRICTED_NOTIFICATION_ID, false /* ongoing */, pi, new Action[0]);
+    }
+
+    private void notifyTetheringNoUpstream() {
+        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+        final String title = res.getString(R.string.no_upstream_notification_title);
+        final String message = res.getString(R.string.no_upstream_notification_message);
+        final String disableButton =
+                res.getString(R.string.no_upstream_notification_disable_button);
+        if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return;
+
+        final Intent intent = new Intent(ACTION_DISABLE_TETHERING);
+        intent.setPackage(mContext.getPackageName());
+        final PendingIntent pi = PendingIntent.getBroadcast(
+                mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                intent,
+                0 /* flags */);
+        final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build();
+
+        showNotification(R.drawable.stat_sys_tether_general, title, message,
+                NO_UPSTREAM_NOTIFICATION_ID, true /* ongoing */, null /* pendingIntent */, action);
+    }
+
+    private boolean setupRoamingNotification() {
+        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+        final boolean upstreamRoamingNotification =
+                res.getBoolean(R.bool.config_upstream_roaming_notification);
+
+        if (!upstreamRoamingNotification) return NO_NOTIFY;
+
+        final String title = res.getString(R.string.upstream_roaming_notification_title);
+        final String message = res.getString(R.string.upstream_roaming_notification_message);
+        if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY;
+
+        final PendingIntent pi = PendingIntent.getActivity(
+                mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+                0 /* requestCode */,
+                new Intent(Settings.ACTION_TETHER_SETTINGS),
+                Intent.FLAG_ACTIVITY_NEW_TASK,
+                null /* options */);
+
+        showNotification(R.drawable.stat_sys_tether_general, title, message,
+                ROAMING_NOTIFICATION_ID, true /* ongoing */, pi, new Action[0]);
+        return NOTIFY_DONE;
+    }
+
+    private boolean setupNoUpstreamNotification() {
+        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+        final int delayToShowUpstreamNotification =
+                res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul);
+
+        if (delayToShowUpstreamNotification < 0) return NO_NOTIFY;
+
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM),
+                delayToShowUpstreamNotification);
+        return NOTIFY_DONE;
+    }
+
+    private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
+            @NonNull final String message, @NotificationId final int id, final boolean ongoing,
+            @Nullable PendingIntent pi, @NonNull final Action... actions) {
+        final Notification notification =
+                new Notification.Builder(mContext, mChannel.getId())
+                        .setSmallIcon(iconId)
+                        .setContentTitle(title)
+                        .setContentText(message)
+                        .setOngoing(ongoing)
+                        .setColor(mContext.getColor(
+                                android.R.color.system_notification_accent_color))
+                        .setVisibility(Notification.VISIBILITY_PUBLIC)
+                        .setCategory(Notification.CATEGORY_STATUS)
+                        .setContentIntent(pi)
+                        .setActions(actions)
+                        .build();
+
+        mNotificationManager.notify(null /* tag */, id, notification);
+    }
+}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
new file mode 100644
index 0000000..c11e862
--- /dev/null
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -0,0 +1,376 @@
+/*
+ * 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.networkstack.tethering;
+
+import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
+import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.content.Intent;
+import android.net.IIntResultListener;
+import android.net.INetworkStackConnector;
+import android.net.ITetheringConnector;
+import android.net.ITetheringEventCallback;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.NetworkStack;
+import android.net.TetheringRequestParcel;
+import android.net.dhcp.DhcpServerCallbacks;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.ip.IpServer;
+import android.os.Binder;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Android service used to manage tethering.
+ *
+ * <p>The service returns a binder for the system server to communicate with the tethering.
+ */
+public class TetheringService extends Service {
+    private static final String TAG = TetheringService.class.getSimpleName();
+
+    private TetheringConnector mConnector;
+
+    @Override
+    public void onCreate() {
+        final TetheringDependencies deps = makeTetheringDependencies();
+        // The Tethering object needs a fully functional context to start, so this can't be done
+        // in the constructor.
+        mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this);
+    }
+
+    /**
+     * Make a reference to Tethering object.
+     */
+    @VisibleForTesting
+    public Tethering makeTethering(TetheringDependencies deps) {
+        System.loadLibrary("tetherutilsjni");
+        return new Tethering(deps);
+    }
+
+    @NonNull
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mConnector;
+    }
+
+    private static class TetheringConnector extends ITetheringConnector.Stub {
+        private final TetheringService mService;
+        private final Tethering mTethering;
+
+        TetheringConnector(Tethering tether, TetheringService service) {
+            mTethering = tether;
+            mService = service;
+        }
+
+        @Override
+        public void tether(String iface, String callerPkg, String callingAttributionTag,
+                IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+
+            try {
+                listener.onResult(mTethering.tether(iface));
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        public void untether(String iface, String callerPkg, String callingAttributionTag,
+                IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+
+            try {
+                listener.onResult(mTethering.untether(iface));
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        public void setUsbTethering(boolean enable, String callerPkg, String callingAttributionTag,
+                IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+
+            try {
+                listener.onResult(mTethering.setUsbTethering(enable));
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        public void startTethering(TetheringRequestParcel request, String callerPkg,
+                String callingAttributionTag, IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg,
+                    callingAttributionTag,
+                    request.exemptFromEntitlementCheck /* onlyAllowPrivileged */,
+                    listener)) {
+                return;
+            }
+
+            mTethering.startTethering(request, listener);
+        }
+
+        @Override
+        public void stopTethering(int type, String callerPkg, String callingAttributionTag,
+                IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+
+            try {
+                mTethering.stopTethering(type);
+                listener.onResult(TETHER_ERROR_NO_ERROR);
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
+                boolean showEntitlementUi, String callerPkg, String callingAttributionTag) {
+            if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, receiver)) return;
+
+            mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
+        }
+
+        @Override
+        public void registerTetheringEventCallback(ITetheringEventCallback callback,
+                String callerPkg) {
+            try {
+                if (!hasTetherAccessPermission()) {
+                    callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
+                    return;
+                }
+                mTethering.registerTetheringEventCallback(callback);
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
+                String callerPkg) {
+            try {
+                if (!hasTetherAccessPermission()) {
+                    callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
+                    return;
+                }
+                mTethering.unregisterTetheringEventCallback(callback);
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        public void stopAllTethering(String callerPkg, String callingAttributionTag,
+                IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+
+            try {
+                mTethering.untetherAll();
+                listener.onResult(TETHER_ERROR_NO_ERROR);
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        public void isTetheringSupported(String callerPkg, String callingAttributionTag,
+                IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+
+            try {
+                listener.onResult(TETHER_ERROR_NO_ERROR);
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
+                    @Nullable String[] args) {
+            mTethering.dump(fd, writer, args);
+        }
+
+        private boolean checkAndNotifyCommonError(final String callerPkg,
+                final String callingAttributionTag, final IIntResultListener listener) {
+            return checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+                    false /* onlyAllowPrivileged */, listener);
+        }
+
+        private boolean checkAndNotifyCommonError(final String callerPkg,
+                final String callingAttributionTag, final boolean onlyAllowPrivileged,
+                final IIntResultListener listener) {
+            try {
+                if (!hasTetherChangePermission(callerPkg, callingAttributionTag,
+                        onlyAllowPrivileged)) {
+                    listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+                    return true;
+                }
+                if (!mTethering.isTetheringSupported()) {
+                    listener.onResult(TETHER_ERROR_UNSUPPORTED);
+                    return true;
+                }
+            } catch (RemoteException e) {
+                return true;
+            }
+
+            return false;
+        }
+
+        private boolean checkAndNotifyCommonError(final String callerPkg,
+                final String callingAttributionTag, final ResultReceiver receiver) {
+            if (!hasTetherChangePermission(callerPkg, callingAttributionTag,
+                    false /* onlyAllowPrivileged */)) {
+                receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null);
+                return true;
+            }
+            if (!mTethering.isTetheringSupported()) {
+                receiver.send(TETHER_ERROR_UNSUPPORTED, null);
+                return true;
+            }
+
+            return false;
+        }
+
+        private boolean hasTetherPrivilegedPermission() {
+            return mService.checkCallingOrSelfPermission(TETHER_PRIVILEGED) == PERMISSION_GRANTED;
+        }
+
+        private boolean hasTetherChangePermission(final String callerPkg,
+                final String callingAttributionTag, final boolean onlyAllowPrivileged) {
+            if (hasTetherPrivilegedPermission()) return true;
+
+            if (onlyAllowPrivileged || mTethering.isTetherProvisioningRequired()) return false;
+
+            int uid = Binder.getCallingUid();
+
+            // If callerPkg's uid is not same as Binder.getCallingUid(),
+            // checkAndNoteWriteSettingsOperation will return false and the operation will be
+            // denied.
+            return mService.checkAndNoteWriteSettingsOperation(mService, uid, callerPkg,
+                    callingAttributionTag, false /* throwException */);
+        }
+
+        private boolean hasTetherAccessPermission() {
+            if (hasTetherPrivilegedPermission()) return true;
+
+            return mService.checkCallingOrSelfPermission(
+                    ACCESS_NETWORK_STATE) == PERMISSION_GRANTED;
+        }
+    }
+
+    /**
+     * Check if the package is a allowed to write settings. This also accounts that such an access
+     * happened.
+     *
+     * @return {@code true} iff the package is allowed to write settings.
+     */
+    @VisibleForTesting
+    boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
+            @NonNull String callingPackage, @Nullable String callingAttributionTag,
+            boolean throwException) {
+        return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage,
+                throwException);
+    }
+
+    /**
+     * An injection method for testing.
+     */
+    @VisibleForTesting
+    public TetheringDependencies makeTetheringDependencies() {
+        return new TetheringDependencies() {
+            @Override
+            public NetworkRequest getDefaultNetworkRequest() {
+                // TODO: b/147280869, add a proper system API to replace this.
+                final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
+                        .clearCapabilities()
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                        .build();
+                return trackDefaultRequest;
+            }
+
+            @Override
+            public Looper getTetheringLooper() {
+                final HandlerThread tetherThread = new HandlerThread("android.tethering");
+                tetherThread.start();
+                return tetherThread.getLooper();
+            }
+
+            @Override
+            public Context getContext() {
+                return TetheringService.this;
+            }
+
+            @Override
+            public IpServer.Dependencies getIpServerDependencies() {
+                return new IpServer.Dependencies() {
+                    @Override
+                    public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+                            DhcpServerCallbacks cb) {
+                        try {
+                            final INetworkStackConnector service = getNetworkStackConnector();
+                            if (service == null) return;
+
+                            service.makeDhcpServer(ifName, params, cb);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Fail to make dhcp server");
+                            try {
+                                cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
+                            } catch (RemoteException re) { }
+                        }
+                    }
+                };
+            }
+
+            // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring
+            // networkStackClient.
+            static final int NETWORKSTACK_TIMEOUT_MS = 60_000;
+            private INetworkStackConnector getNetworkStackConnector() {
+                IBinder connector;
+                try {
+                    final long before = System.currentTimeMillis();
+                    while ((connector = NetworkStack.getService()) == null) {
+                        if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+                            Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
+                            return null;
+                        }
+                        Thread.sleep(200);
+                    }
+                } catch (InterruptedException e) {
+                    Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector");
+                    return null;
+                }
+                return INetworkStackConnector.Stub.asInterface(connector);
+            }
+
+            @Override
+            public BluetoothAdapter getBluetoothAdapter() {
+                return BluetoothAdapter.getDefaultAdapter();
+            }
+        };
+    }
+}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
similarity index 96%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
rename to packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 2875f71..320427c 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
@@ -43,7 +43,6 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.StateMachine;
 
 import java.util.HashMap;
@@ -244,7 +243,8 @@
         // Additionally, we log a message to aid in any subsequent debugging.
         mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest);
 
-        cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
+        cm().requestNetwork(mobileUpstreamRequest, 0, legacyType, mHandler,
+                mMobileNetworkCallback);
     }
 
     /** Release mobile network request. */
@@ -585,21 +585,23 @@
      */
     @VisibleForTesting
     public static NetworkCapabilities networkCapabilitiesForType(int type) {
-        final NetworkCapabilities nc = new NetworkCapabilities();
+        final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
 
         // Map from type to transports.
         final int notFound = -1;
         final int transport = sLegacyTypeToTransport.get(type, notFound);
-        Preconditions.checkArgument(transport != notFound, "unknown legacy type: " + type);
-        nc.addTransportType(transport);
+        if (transport == notFound) {
+            throw new IllegalArgumentException("unknown legacy type: " + type);
+        }
+        builder.addTransportType(transport);
 
         if (type == TYPE_MOBILE_DUN) {
-            nc.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
             // DUN is restricted network, see NetworkCapabilities#FORCE_RESTRICTED_CAPABILITIES.
-            nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         } else {
-            nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
         }
-        return nc;
+        return builder.build();
     }
 }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java
similarity index 96%
rename from packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java
rename to packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java
index 68bb837..bab9f84 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkState.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkState.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import android.net.LinkProperties;
 import android.net.Network;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
deleted file mode 100644
index b97f752..0000000
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * 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_BLUETOOTH;
-import static android.net.TetheringManager.TETHERING_USB;
-import static android.net.TetheringManager.TETHERING_WIFI;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.ArrayRes;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.IntRange;
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.networkstack.tethering.R;
-
-/**
- * A class to display tethering-related notifications.
- *
- * <p>This class is not thread safe, it is intended to be used only from the tethering handler
- * thread. However the constructor is an exception, as it is called on another thread ;
- * therefore for thread safety all members of this class MUST either be final or initialized
- * to their default value (0, false or null).
- *
- * @hide
- */
-public class TetheringNotificationUpdater {
-    private static final String TAG = TetheringNotificationUpdater.class.getSimpleName();
-    private static final String CHANNEL_ID = "TETHERING_STATUS";
-    private static final boolean NOTIFY_DONE = true;
-    private static final boolean NO_NOTIFY = false;
-    // Id to update and cancel tethering notification. Must be unique within the tethering app.
-    private static final int NOTIFY_ID = 20191115;
-    @VisibleForTesting
-    static final int NO_ICON_ID = 0;
-    @VisibleForTesting
-    static final int DOWNSTREAM_NONE = 0;
-    private final Context mContext;
-    private final NotificationManager mNotificationManager;
-    private final NotificationChannel mChannel;
-    // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
-    // This value has to be made 1 2 and 4, and OR'd with the others.
-    // WARNING : the constructor is called on a different thread. Thread safety therefore
-    // relies on this value being initialized to 0, and not any other value. If you need
-    // to change this, you will need to change the thread where the constructor is invoked,
-    // or to introduce synchronization.
-    private int mDownstreamTypesMask = DOWNSTREAM_NONE;
-
-    public TetheringNotificationUpdater(@NonNull final Context context) {
-        mContext = context;
-        mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-        mChannel = new NotificationChannel(
-                CHANNEL_ID,
-                context.getResources().getString(R.string.notification_channel_tethering_status),
-                NotificationManager.IMPORTANCE_LOW);
-        mNotificationManager.createNotificationChannel(mChannel);
-    }
-
-    /** Called when downstream has changed */
-    public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
-        if (mDownstreamTypesMask == downstreamTypesMask) return;
-        mDownstreamTypesMask = downstreamTypesMask;
-        updateNotification();
-    }
-
-    private void updateNotification() {
-        final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
-
-        if (tetheringInactive || setupNotification() == NO_NOTIFY) {
-            clearNotification();
-        }
-    }
-
-    private void clearNotification() {
-        mNotificationManager.cancel(null /* tag */, NOTIFY_ID);
-    }
-
-    /**
-     * Returns the downstream types mask which convert from given string.
-     *
-     * @param types This string has to be made by "WIFI", "USB", "BT", and OR'd with the others.
-     *
-     * @return downstream types mask value.
-     */
-    @IntRange(from = 0, to = 7)
-    private int getDownstreamTypesMask(@NonNull final String types) {
-        int downstreamTypesMask = DOWNSTREAM_NONE;
-        final String[] downstreams = types.split("\\|");
-        for (String downstream : downstreams) {
-            if ("USB".equals(downstream.trim())) {
-                downstreamTypesMask |= (1 << TETHERING_USB);
-            } else if ("WIFI".equals(downstream.trim())) {
-                downstreamTypesMask |= (1 << TETHERING_WIFI);
-            } else if ("BT".equals(downstream.trim())) {
-                downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
-            }
-        }
-        return downstreamTypesMask;
-    }
-
-    /**
-     * Returns the icons {@link android.util.SparseArray} which get from given string-array resource
-     * id.
-     *
-     * @param id String-array resource id
-     *
-     * @return {@link android.util.SparseArray} with downstream types and icon id info.
-     */
-    @NonNull
-    private SparseArray<Integer> getIcons(@ArrayRes int id) {
-        final Resources res = mContext.getResources();
-        final String[] array = res.getStringArray(id);
-        final SparseArray<Integer> icons = new SparseArray<>();
-        for (String config : array) {
-            if (TextUtils.isEmpty(config)) continue;
-
-            final String[] elements = config.split(";");
-            if (elements.length != 2) {
-                Log.wtf(TAG,
-                        "Unexpected format in Tethering notification configuration : " + config);
-                continue;
-            }
-
-            final String[] types = elements[0].split(",");
-            for (String type : types) {
-                int mask = getDownstreamTypesMask(type);
-                if (mask == DOWNSTREAM_NONE) continue;
-                icons.put(mask, res.getIdentifier(
-                        elements[1].trim(), null /* defType */, null /* defPackage */));
-            }
-        }
-        return icons;
-    }
-
-    private boolean setupNotification() {
-        final Resources res = mContext.getResources();
-        final SparseArray<Integer> downstreamIcons = getIcons(R.array.tethering_notification_icons);
-
-        final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID);
-        if (iconId == NO_ICON_ID) return NO_NOTIFY;
-
-        final String title = res.getString(R.string.tethering_notification_title);
-        final String message = res.getString(R.string.tethering_notification_message);
-
-        showNotification(iconId, title, message);
-        return NOTIFY_DONE;
-    }
-
-    private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
-            @NonNull final String message) {
-        final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
-        final PendingIntent pi = PendingIntent.getActivity(
-                mContext.createContextAsUser(UserHandle.CURRENT, 0),
-                0 /* requestCode */, intent, 0 /* flags */, null /* options */);
-        final Notification notification =
-                new Notification.Builder(mContext, mChannel.getId())
-                        .setSmallIcon(iconId)
-                        .setContentTitle(title)
-                        .setContentText(message)
-                        .setOngoing(true)
-                        .setColor(mContext.getColor(
-                                android.R.color.system_notification_accent_color))
-                        .setVisibility(Notification.VISIBILITY_PUBLIC)
-                        .setCategory(Notification.CATEGORY_STATUS)
-                        .setContentIntent(pi)
-                        .build();
-
-        mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification);
-    }
-}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
deleted file mode 100644
index 020b32a..0000000
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ /dev/null
@@ -1,390 +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.server.connectivity.tethering;
-
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
-import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
-import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
-import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
-
-import android.app.Service;
-import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
-import android.content.Intent;
-import android.net.IIntResultListener;
-import android.net.INetworkStackConnector;
-import android.net.ITetheringConnector;
-import android.net.ITetheringEventCallback;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.TetheringRequestParcel;
-import android.net.dhcp.DhcpServerCallbacks;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.ip.IpServer;
-import android.net.util.SharedLog;
-import android.os.Binder;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.SystemProperties;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Android service used to manage tethering.
- *
- * <p>The service returns a binder for the system server to communicate with the tethering.
- */
-public class TetheringService extends Service {
-    private static final String TAG = TetheringService.class.getSimpleName();
-
-    private final SharedLog mLog = new SharedLog(TAG);
-    private TetheringConnector mConnector;
-    private Context mContext;
-    private TetheringDependencies mDeps;
-    private Tethering mTethering;
-    private UserManager mUserManager;
-
-    @Override
-    public void onCreate() {
-        mLog.mark("onCreate");
-        mDeps = getTetheringDependencies();
-        mContext = mDeps.getContext();
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        mTethering = makeTethering(mDeps);
-    }
-
-    /**
-     * Make a reference to Tethering object.
-     */
-    @VisibleForTesting
-    public Tethering makeTethering(TetheringDependencies deps) {
-        System.loadLibrary("tetherutilsjni");
-        return new Tethering(deps);
-    }
-
-    /**
-     * Create a binder connector for the system server to communicate with the tethering.
-     */
-    private synchronized IBinder makeConnector() {
-        if (mConnector == null) {
-            mConnector = new TetheringConnector(mTethering, TetheringService.this);
-        }
-        return mConnector;
-    }
-
-    @NonNull
-    @Override
-    public IBinder onBind(Intent intent) {
-        mLog.mark("onBind");
-        return makeConnector();
-    }
-
-    private static class TetheringConnector extends ITetheringConnector.Stub {
-        private final TetheringService mService;
-        private final Tethering mTethering;
-
-        TetheringConnector(Tethering tether, TetheringService service) {
-            mTethering = tether;
-            mService = service;
-        }
-
-        @Override
-        public void tether(String iface, String callerPkg, IIntResultListener listener) {
-            if (checkAndNotifyCommonError(callerPkg, listener)) return;
-
-            try {
-                listener.onResult(mTethering.tether(iface));
-            } catch (RemoteException e) { }
-        }
-
-        @Override
-        public void untether(String iface, String callerPkg, IIntResultListener listener) {
-            if (checkAndNotifyCommonError(callerPkg, listener)) return;
-
-            try {
-                listener.onResult(mTethering.untether(iface));
-            } catch (RemoteException e) { }
-        }
-
-        @Override
-        public void setUsbTethering(boolean enable, String callerPkg, IIntResultListener listener) {
-            if (checkAndNotifyCommonError(callerPkg, listener)) return;
-
-            try {
-                listener.onResult(mTethering.setUsbTethering(enable));
-            } catch (RemoteException e) { }
-        }
-
-        @Override
-        public void startTethering(TetheringRequestParcel request, String callerPkg,
-                IIntResultListener listener) {
-            if (checkAndNotifyCommonError(callerPkg, listener)) return;
-
-            mTethering.startTethering(request, listener);
-        }
-
-        @Override
-        public void stopTethering(int type, String callerPkg, IIntResultListener listener) {
-            if (checkAndNotifyCommonError(callerPkg, listener)) return;
-
-            try {
-                mTethering.stopTethering(type);
-                listener.onResult(TETHER_ERROR_NO_ERROR);
-            } catch (RemoteException e) { }
-        }
-
-        @Override
-        public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
-                boolean showEntitlementUi, String callerPkg) {
-            if (checkAndNotifyCommonError(callerPkg, receiver)) return;
-
-            mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
-        }
-
-        @Override
-        public void registerTetheringEventCallback(ITetheringEventCallback callback,
-                String callerPkg) {
-            try {
-                if (!mService.hasTetherAccessPermission()) {
-                    callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
-                    return;
-                }
-                mTethering.registerTetheringEventCallback(callback);
-            } catch (RemoteException e) { }
-        }
-
-        @Override
-        public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
-                String callerPkg) {
-            try {
-                if (!mService.hasTetherAccessPermission()) {
-                    callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
-                    return;
-                }
-                mTethering.unregisterTetheringEventCallback(callback);
-            } catch (RemoteException e) { }
-        }
-
-        @Override
-        public void stopAllTethering(String callerPkg, IIntResultListener listener) {
-            if (checkAndNotifyCommonError(callerPkg, listener)) return;
-
-            try {
-                mTethering.untetherAll();
-                listener.onResult(TETHER_ERROR_NO_ERROR);
-            } catch (RemoteException e) { }
-        }
-
-        @Override
-        public void isTetheringSupported(String callerPkg, IIntResultListener listener) {
-            if (checkAndNotifyCommonError(callerPkg, listener)) return;
-
-            try {
-                listener.onResult(TETHER_ERROR_NO_ERROR);
-            } catch (RemoteException e) { }
-        }
-
-        @Override
-        protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
-                    @Nullable String[] args) {
-            mTethering.dump(fd, writer, args);
-        }
-
-        private boolean checkAndNotifyCommonError(String callerPkg, IIntResultListener listener) {
-            try {
-                if (!mService.hasTetherChangePermission(callerPkg)) {
-                    listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
-                    return true;
-                }
-                if (!mService.isTetheringSupported()) {
-                    listener.onResult(TETHER_ERROR_UNSUPPORTED);
-                    return true;
-                }
-            } catch (RemoteException e) {
-                return true;
-            }
-
-            return false;
-        }
-
-        private boolean checkAndNotifyCommonError(String callerPkg, ResultReceiver receiver) {
-            if (!mService.hasTetherChangePermission(callerPkg)) {
-                receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null);
-                return true;
-            }
-            if (!mService.isTetheringSupported()) {
-                receiver.send(TETHER_ERROR_UNSUPPORTED, null);
-                return true;
-            }
-
-            return false;
-        }
-
-    }
-
-    // if ro.tether.denied = true we default to no tethering
-    // gservices could set the secure setting to 1 though to enable it on a build where it
-    // had previously been turned off.
-    private boolean isTetheringSupported() {
-        final int defaultVal =
-                SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
-        final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
-        final boolean tetherEnabledInSettings = tetherSupported
-                && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
-
-        return tetherEnabledInSettings && mTethering.hasTetherableConfiguration();
-    }
-
-    private boolean hasTetherChangePermission(String callerPkg) {
-        if (checkCallingOrSelfPermission(
-                android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) {
-            return true;
-        }
-
-        if (mTethering.isTetherProvisioningRequired()) return false;
-
-
-        int uid = Binder.getCallingUid();
-        // If callerPkg's uid is not same as Binder.getCallingUid(),
-        // checkAndNoteWriteSettingsOperation will return false and the operation will be denied.
-        if (Settings.checkAndNoteWriteSettingsOperation(mContext, uid, callerPkg,
-                false /* throwException */)) {
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean hasTetherAccessPermission() {
-        if (checkCallingOrSelfPermission(
-                android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) {
-            return true;
-        }
-
-        if (checkCallingOrSelfPermission(
-                android.Manifest.permission.ACCESS_NETWORK_STATE) == PERMISSION_GRANTED) {
-            return true;
-        }
-
-        return false;
-    }
-
-
-    /**
-     * An injection method for testing.
-     */
-    @VisibleForTesting
-    public TetheringDependencies getTetheringDependencies() {
-        if (mDeps == null) {
-            mDeps = new TetheringDependencies() {
-                @Override
-                public NetworkRequest getDefaultNetworkRequest() {
-                    // TODO: b/147280869, add a proper system API to replace this.
-                    final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
-                            .clearCapabilities()
-                            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                            .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
-                            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
-                            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                            .build();
-                    return trackDefaultRequest;
-                }
-
-                @Override
-                public Looper getTetheringLooper() {
-                    final HandlerThread tetherThread = new HandlerThread("android.tethering");
-                    tetherThread.start();
-                    return tetherThread.getLooper();
-                }
-
-                @Override
-                public boolean isTetheringSupported() {
-                    return TetheringService.this.isTetheringSupported();
-                }
-
-                @Override
-                public Context getContext() {
-                    return TetheringService.this;
-                }
-
-                @Override
-                public IpServer.Dependencies getIpServerDependencies() {
-                    return new IpServer.Dependencies() {
-                        @Override
-                        public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
-                                DhcpServerCallbacks cb) {
-                            try {
-                                final INetworkStackConnector service = getNetworkStackConnector();
-                                if (service == null) return;
-
-                                service.makeDhcpServer(ifName, params, cb);
-                            } catch (RemoteException e) {
-                                Log.e(TAG, "Fail to make dhcp server");
-                                try {
-                                    cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
-                                } catch (RemoteException re) { }
-                            }
-                        }
-                    };
-                }
-
-                // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring
-                // networkStackClient.
-                static final int NETWORKSTACK_TIMEOUT_MS = 60_000;
-                private INetworkStackConnector getNetworkStackConnector() {
-                    IBinder connector;
-                    try {
-                        final long before = System.currentTimeMillis();
-                        while ((connector = (IBinder) mContext.getSystemService(
-                                Context.NETWORK_STACK_SERVICE)) == null) {
-                            if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
-                                Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
-                                return null;
-                            }
-                            Thread.sleep(200);
-                        }
-                    } catch (InterruptedException e) {
-                        Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector");
-                        return null;
-                    }
-                    return INetworkStackConnector.Stub.asInterface(connector);
-                }
-
-                @Override
-                public BluetoothAdapter getBluetoothAdapter() {
-                    return BluetoothAdapter.getDefaultAdapter();
-                }
-            };
-        }
-        return mDeps;
-    }
-}
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
new file mode 100644
index 0000000..3305ed0
--- /dev/null
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -0,0 +1,86 @@
+//
+// 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.
+//
+java_defaults {
+    name: "TetheringIntegrationTestsDefaults",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "NetworkStackApiStableLib",
+        "androidx.test.rules",
+        "frameworks-base-testutils",
+        "mockito-target-extended-minus-junit4",
+        "net-tests-utils",
+        "testables",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+    ],
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+    jarjar_rules: ":NetworkStackJarJarRules",
+}
+
+android_library {
+    name: "TetheringIntegrationTestsLib",
+    platform_apis: true,
+    defaults: ["TetheringIntegrationTestsDefaults"],
+    visibility: ["//cts/tests/tests/tethering"]
+}
+
+android_test {
+    name: "TetheringIntegrationTests",
+    platform_apis: true,
+    defaults: ["TetheringIntegrationTestsDefaults"],
+    test_suites: [
+        "device-tests",
+        "mts",
+    ],
+    compile_multilib: "both",
+}
+
+// Special version of the tethering tests that includes all tests necessary for code coverage
+// purposes. This is currently the union of TetheringTests, TetheringIntegrationTests and
+// NetworkStackTests.
+android_test {
+    name: "TetheringCoverageTests",
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: ["device-tests", "mts"],
+    test_config: "AndroidTest_Coverage.xml",
+    defaults: ["libnetworkstackutilsjni_deps"],
+    static_libs: [
+        "NetworkStaticLibTestsLib",
+        "NetworkStackTestsLib",
+        "TetheringTestsLib",
+        "TetheringIntegrationTestsLib",
+    ],
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+        // For NetworkStackUtils included in NetworkStackBase
+        "libnetworkstackutilsjni",
+    ],
+    compile_multilib: "both",
+    manifest: "AndroidManifest_coverage.xml",
+}
\ No newline at end of file
diff --git a/packages/Tethering/tests/integration/AndroidManifest.xml b/packages/Tethering/tests/integration/AndroidManifest.xml
new file mode 100644
index 0000000..fddfaad
--- /dev/null
+++ b/packages/Tethering/tests/integration/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.networkstack.tethering.tests.integration">
+
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.networkstack.tethering.tests.integration"
+        android:label="Tethering integration tests">
+    </instrumentation>
+</manifest>
diff --git a/packages/Tethering/tests/integration/AndroidManifest_coverage.xml b/packages/Tethering/tests/integration/AndroidManifest_coverage.xml
new file mode 100644
index 0000000..06de00d
--- /dev/null
+++ b/packages/Tethering/tests/integration/AndroidManifest_coverage.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.android.networkstack.tethering.tests.coverage">
+
+    <application tools:replace="android:label"
+                 android:debuggable="true"
+                 android:label="Tethering coverage tests">
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.networkstack.tethering.tests.coverage"
+                     android:label="Tethering coverage tests">
+    </instrumentation>
+</manifest>
diff --git a/packages/Tethering/tests/integration/AndroidTest_Coverage.xml b/packages/Tethering/tests/integration/AndroidTest_Coverage.xml
new file mode 100644
index 0000000..3def209
--- /dev/null
+++ b/packages/Tethering/tests/integration/AndroidTest_Coverage.xml
@@ -0,0 +1,12 @@
+<configuration description="Runs coverage tests for Tethering">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="TetheringCoverageTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="TetheringCoverageTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.networkstack.tethering.tests.coverage" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
new file mode 100644
index 0000000..4bac9da
--- /dev/null
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -0,0 +1,550 @@
+/*
+ * 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.net;
+
+import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.net.EthernetManager.TetheredInterfaceCallback;
+import android.net.EthernetManager.TetheredInterfaceRequest;
+import android.net.TetheringManager.StartTetheringCallback;
+import android.net.TetheringManager.TetheringEventCallback;
+import android.net.TetheringManager.TetheringRequest;
+import android.net.dhcp.DhcpAckPacket;
+import android.net.dhcp.DhcpOfferPacket;
+import android.net.dhcp.DhcpPacket;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.SystemClock;
+import android.system.Os;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TapPacketReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+import java.net.Inet4Address;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class EthernetTetheringTest {
+
+    private static final String TAG = EthernetTetheringTest.class.getSimpleName();
+    private static final int TIMEOUT_MS = 1000;
+    private static final int PACKET_READ_TIMEOUT_MS = 100;
+    private static final int DHCP_DISCOVER_ATTEMPTS = 10;
+    private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] {
+            DhcpPacket.DHCP_SUBNET_MASK,
+            DhcpPacket.DHCP_ROUTER,
+            DhcpPacket.DHCP_DNS_SERVER,
+            DhcpPacket.DHCP_LEASE_TIME,
+    };
+    private static final String DHCP_HOSTNAME = "testhostname";
+
+    private final Context mContext = InstrumentationRegistry.getContext();
+    private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class);
+    private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class);
+
+    private TestNetworkInterface mTestIface;
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private TapPacketReader mTapPacketReader;
+
+    private TetheredInterfaceRequester mTetheredInterfaceRequester;
+    private MyTetheringEventCallback mTetheringEventCallback;
+
+    private UiAutomation mUiAutomation =
+            InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+    @Before
+    public void setUp() throws Exception {
+        mHandlerThread = new HandlerThread(getClass().getSimpleName());
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
+        // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
+        // tethered client callbacks.
+        mUiAutomation.adoptShellPermissionIdentity(
+                MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED);
+    }
+
+    private void cleanUp() throws Exception {
+        mTm.stopTethering(TETHERING_ETHERNET);
+        if (mTetheringEventCallback != null) {
+            mTetheringEventCallback.awaitInterfaceUntethered();
+            mTetheringEventCallback.unregister();
+            mTetheringEventCallback = null;
+        }
+        if (mTapPacketReader != null) {
+            TapPacketReader reader = mTapPacketReader;
+            mHandler.post(() -> reader.stop());
+            mTapPacketReader = null;
+        }
+        mHandlerThread.quitSafely();
+        mTetheredInterfaceRequester.release();
+        mEm.setIncludeTestInterfaces(false);
+        maybeDeleteTestInterface();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        try {
+            cleanUp();
+        } finally {
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    @Test
+    public void testVirtualEthernetAlreadyExists() throws Exception {
+        // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
+        assumeFalse(mEm.isAvailable());
+
+        mTestIface = createTestInterface();
+        // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the
+        // interface will be placed in client mode, which will delete the link-local address.
+        // At that point NetworkInterface.getByName() will cease to work on the interface, because
+        // starting in R NetworkInterface can no longer see interfaces without IP addresses.
+        int mtu = getMTU(mTestIface);
+
+        Log.d(TAG, "Including test interfaces");
+        mEm.setIncludeTestInterfaces(true);
+
+        final String iface = mTetheredInterfaceRequester.getInterface();
+        assertEquals("TetheredInterfaceCallback for unexpected interface",
+                mTestIface.getInterfaceName(), iface);
+
+        checkVirtualEthernet(mTestIface, mtu);
+    }
+
+    @Test
+    public void testVirtualEthernet() throws Exception {
+        // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
+        assumeFalse(mEm.isAvailable());
+
+        CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface();
+
+        mEm.setIncludeTestInterfaces(true);
+
+        mTestIface = createTestInterface();
+
+        final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        assertEquals("TetheredInterfaceCallback for unexpected interface",
+                mTestIface.getInterfaceName(), iface);
+
+        checkVirtualEthernet(mTestIface, getMTU(mTestIface));
+    }
+
+    @Test
+    public void testStaticIpv4() throws Exception {
+        assumeFalse(mEm.isAvailable());
+
+        mEm.setIncludeTestInterfaces(true);
+
+        mTestIface = createTestInterface();
+
+        final String iface = mTetheredInterfaceRequester.getInterface();
+        assertEquals("TetheredInterfaceCallback for unexpected interface",
+                mTestIface.getInterfaceName(), iface);
+
+        assertInvalidStaticIpv4Request(iface, null, null);
+        assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64");
+        assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28");
+        assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28");
+        assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null);
+        assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28");
+        assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28");
+
+        final String localAddr = "192.0.2.3/28";
+        final String clientAddr = "192.0.2.2/28";
+        mTetheringEventCallback = enableEthernetTethering(iface,
+                requestWithStaticIpv4(localAddr, clientAddr));
+
+        mTetheringEventCallback.awaitInterfaceTethered();
+        assertInterfaceHasIpAddress(iface, localAddr);
+
+        byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
+        byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
+
+        FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor();
+        mTapPacketReader = makePacketReader(fd, getMTU(mTestIface));
+        DhcpResults dhcpResults = runDhcp(fd, client1);
+        assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
+
+        try {
+            runDhcp(fd, client2);
+            fail("Only one client should get an IP address");
+        } catch (TimeoutException expected) { }
+
+    }
+
+    @Test
+    public void testPhysicalEthernet() throws Exception {
+        assumeTrue(mEm.isAvailable());
+
+        // Get an interface to use.
+        final String iface = mTetheredInterfaceRequester.getInterface();
+
+        // Enable Ethernet tethering and check that it starts.
+        mTetheringEventCallback = enableEthernetTethering(iface);
+
+        // There is nothing more we can do on a physical interface without connecting an actual
+        // client, which is not possible in this test.
+    }
+
+    private static final class MyTetheringEventCallback implements TetheringEventCallback {
+        private final TetheringManager mTm;
+        private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1);
+        private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1);
+        private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
+        private final String mIface;
+
+        private volatile boolean mInterfaceWasTethered = false;
+        private volatile boolean mUnregistered = false;
+        private volatile Collection<TetheredClient> mClients = null;
+
+        MyTetheringEventCallback(TetheringManager tm, String iface) {
+            mTm = tm;
+            mIface = iface;
+        }
+
+        public void unregister() {
+            mTm.unregisterTetheringEventCallback(this);
+            mUnregistered = true;
+        }
+
+        @Override
+        public void onTetheredInterfacesChanged(List<String> interfaces) {
+            // Ignore stale callbacks registered by previous test cases.
+            if (mUnregistered) return;
+
+            final boolean wasTethered = mTetheringStartedLatch.getCount() == 0;
+            if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) {
+                // This interface is being tethered for the first time.
+                Log.d(TAG, "Tethering started: " + interfaces);
+                mInterfaceWasTethered = true;
+                mTetheringStartedLatch.countDown();
+            } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) {
+                Log.d(TAG, "Tethering stopped: " + interfaces);
+                mTetheringStoppedLatch.countDown();
+            }
+        }
+
+        public void awaitInterfaceTethered() throws Exception {
+            assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms",
+                    mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+
+        public void awaitInterfaceUntethered() throws Exception {
+            // Don't block teardown if the interface was never tethered.
+            // This is racy because the interface might become tethered right after this check, but
+            // that can only happen in tearDown if startTethering timed out, which likely means
+            // the test has already failed.
+            if (!mInterfaceWasTethered) return;
+
+            assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms",
+                    mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+
+        @Override
+        public void onError(String ifName, int error) {
+            // Ignore stale callbacks registered by previous test cases.
+            if (mUnregistered) return;
+
+            fail("TetheringEventCallback got error:" + error + " on iface " + ifName);
+        }
+
+        @Override
+        public void onClientsChanged(Collection<TetheredClient> clients) {
+            // Ignore stale callbacks registered by previous test cases.
+            if (mUnregistered) return;
+
+            Log.d(TAG, "Got clients changed: " + clients);
+            mClients = clients;
+            if (clients.size() > 0) {
+                mClientConnectedLatch.countDown();
+            }
+        }
+
+        public Collection<TetheredClient> awaitClientConnected() throws Exception {
+            assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms",
+                    mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            return mClients;
+        }
+    }
+
+    private MyTetheringEventCallback enableEthernetTethering(String iface,
+            TetheringRequest request) throws Exception {
+        MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface);
+        mTm.registerTetheringEventCallback(mHandler::post, callback);
+
+        StartTetheringCallback startTetheringCallback = new StartTetheringCallback() {
+            @Override
+            public void onTetheringFailed(int resultCode) {
+                fail("Unexpectedly got onTetheringFailed");
+            }
+        };
+        Log.d(TAG, "Starting Ethernet tethering");
+        mTm.startTethering(request, mHandler::post /* executor */,  startTetheringCallback);
+        callback.awaitInterfaceTethered();
+        return callback;
+    }
+
+    private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception {
+        return enableEthernetTethering(iface,
+                new TetheringRequest.Builder(TETHERING_ETHERNET).build());
+    }
+
+    private int getMTU(TestNetworkInterface iface) throws SocketException {
+        NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName());
+        assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif);
+        return nif.getMTU();
+    }
+
+    private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) {
+        final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu);
+        mHandler.post(() -> reader.start());
+        HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS);
+        return reader;
+    }
+
+    private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception {
+        FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
+        mTapPacketReader = makePacketReader(fd, mtu);
+        mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName());
+        checkTetheredClientCallbacks(fd);
+    }
+
+    private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception {
+        // We have to retransmit DHCP requests because IpServer declares itself to be ready before
+        // its DhcpServer is actually started. TODO: fix this race and remove this loop.
+        DhcpPacket offerPacket = null;
+        for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) {
+            Log.d(TAG, "Sending DHCP discover");
+            sendDhcpDiscover(fd, clientMacAddr);
+            offerPacket = getNextDhcpPacket();
+            if (offerPacket instanceof DhcpOfferPacket) break;
+        }
+        if (!(offerPacket instanceof DhcpOfferPacket)) {
+            throw new TimeoutException("No DHCPOFFER received on interface within timeout");
+        }
+
+        sendDhcpRequest(fd, offerPacket, clientMacAddr);
+        DhcpPacket ackPacket = getNextDhcpPacket();
+        if (!(ackPacket instanceof DhcpAckPacket)) {
+            throw new TimeoutException("No DHCPACK received on interface within timeout");
+        }
+
+        return ackPacket.toDhcpResults();
+    }
+
+    private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception {
+        // Create a fake client.
+        byte[] clientMacAddr = new byte[6];
+        new Random().nextBytes(clientMacAddr);
+
+        DhcpResults dhcpResults = runDhcp(fd, clientMacAddr);
+
+        final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected();
+        assertEquals(1, clients.size());
+        final TetheredClient client = clients.iterator().next();
+
+        // Check the MAC address.
+        assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress());
+        assertEquals(TETHERING_ETHERNET, client.getTetheringType());
+
+        // Check the hostname.
+        assertEquals(1, client.getAddresses().size());
+        TetheredClient.AddressInfo info = client.getAddresses().get(0);
+        assertEquals(DHCP_HOSTNAME, info.getHostname());
+
+        // Check the address is the one that was handed out in the DHCP ACK.
+        assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
+
+        // Check that the lifetime is correct +/- 10s.
+        final long now = SystemClock.elapsedRealtime();
+        final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000;
+        final String msg = String.format("IP address should have lifetime of %d, got %d",
+                dhcpResults.leaseDuration, actualLeaseDuration);
+        assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10);
+    }
+
+    private DhcpPacket getNextDhcpPacket() throws ParseException {
+        byte[] packet;
+        while ((packet = mTapPacketReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) {
+            try {
+                return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2);
+            } catch (DhcpPacket.ParseException e) {
+                // Not a DHCP packet. Continue.
+            }
+        }
+        return null;
+    }
+
+    private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback {
+        private final CountDownLatch mInterfaceAvailableLatch = new CountDownLatch(1);
+        private final Handler mHandler;
+        private final EthernetManager mEm;
+
+        private TetheredInterfaceRequest mRequest;
+        private final CompletableFuture<String> mFuture = new CompletableFuture<>();
+
+        TetheredInterfaceRequester(Handler handler, EthernetManager em) {
+            mHandler = handler;
+            mEm = em;
+        }
+
+        @Override
+        public void onAvailable(String iface) {
+            Log.d(TAG, "Ethernet interface available: " + iface);
+            mFuture.complete(iface);
+        }
+
+        @Override
+        public void onUnavailable() {
+            mFuture.completeExceptionally(new IllegalStateException("onUnavailable received"));
+        }
+
+        public CompletableFuture<String> requestInterface() {
+            assertNull("BUG: more than one tethered interface request", mRequest);
+            Log.d(TAG, "Requesting tethered interface");
+            mRequest = mEm.requestTetheredInterface(mHandler::post, this);
+            return mFuture;
+        }
+
+        public String getInterface() throws Exception {
+            return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+
+        public void release() {
+            if (mRequest != null) {
+                mFuture.obtrudeException(new IllegalStateException("Request already released"));
+                mRequest.release();
+                mRequest = null;
+            }
+        }
+    }
+
+    private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception {
+        ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2,
+                new Random().nextInt() /* transactionId */, (short) 0 /* secs */,
+                macAddress,  false /* unicast */, DHCP_REQUESTED_PARAMS,
+                false /* rapid commit */,  DHCP_HOSTNAME);
+        sendPacket(fd, packet);
+    }
+
+    private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress)
+            throws Exception {
+        DhcpResults results = offerPacket.toDhcpResults();
+        Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress();
+        Inet4Address serverIdentifier = results.serverAddress;
+        ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2,
+                0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */,
+                false /* broadcast */, macAddress, clientIp /* requestedIpAddress */,
+                serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME);
+        sendPacket(fd, packet);
+    }
+
+    private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception {
+        assertNotNull("Only tests on virtual interfaces can send packets", fd);
+        Os.write(fd, packet);
+    }
+
+    public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) {
+        // Check all fields except the deprecation and expiry times.
+        String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2);
+        assertTrue(msg, l1.isSameAddressAs(l2));
+        assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags());
+        assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope());
+    }
+
+    private TetheringRequest requestWithStaticIpv4(String local, String client) {
+        LinkAddress localAddr = local == null ? null : new LinkAddress(local);
+        LinkAddress clientAddr = client == null ? null : new LinkAddress(client);
+        return new TetheringRequest.Builder(TETHERING_ETHERNET)
+                .setStaticIpv4Addresses(localAddr, clientAddr).build();
+    }
+
+    private void assertInvalidStaticIpv4Request(String iface, String local, String client)
+            throws Exception {
+        try {
+            enableEthernetTethering(iface, requestWithStaticIpv4(local, client));
+            fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client);
+        } catch (IllegalArgumentException | NullPointerException expected) { }
+    }
+
+    private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception {
+        LinkAddress expectedAddr = new LinkAddress(expected);
+        NetworkInterface nif = NetworkInterface.getByName(iface);
+        for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
+            final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
+            if (expectedAddr.equals(addr)) {
+                return;
+            }
+        }
+        fail("Expected " + iface + " to have IP address " + expected + ", found "
+                + nif.getInterfaceAddresses());
+    }
+
+    private TestNetworkInterface createTestInterface() throws Exception {
+        TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
+        TestNetworkInterface iface = tnm.createTapInterface();
+        Log.d(TAG, "Created test interface " + iface.getInterfaceName());
+        assertNotNull(NetworkInterface.getByName(iface.getInterfaceName()));
+        return iface;
+    }
+
+    private void maybeDeleteTestInterface() throws Exception {
+        if (mTestIface != null) {
+            mTestIface.getFileDescriptor().close();
+            Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName());
+            mTestIface = null;
+        }
+    }
+}
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index ddc095f..9b7d683 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -14,36 +14,51 @@
 // limitations under the License.
 //
 
-android_test {
-    name: "TetheringTests",
-    certificate: "platform",
+// Tests in this folder are included both in unit tests and CTS.
+java_library {
+    name: "TetheringCommonTests",
+    srcs: [
+        "common/**/*.java",
+        "common/**/*.kt"
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "net-tests-utils",
+    ],
+    // TODO(b/147200698) change sdk_version to module-current and remove framework-minus-apex
+    sdk_version: "core_platform",
+    libs: [
+        "framework-minus-apex",
+        "framework-tethering",
+    ],
+    visibility: ["//cts/tests/tests/tethering"],
+}
+
+java_defaults {
+    name: "TetheringTestsDefaults",
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
     ],
-    test_suites: [
-        "device-tests",
-        "mts",
-    ],
-    compile_multilib: "both",
     static_libs: [
+        "TetheringApiCurrentLib",
+        "TetheringCommonTests",
         "androidx.test.rules",
         "frameworks-base-testutils",
-        "net-tests-utils",
         "mockito-target-extended-minus-junit4",
-        "TetheringApiCurrentLib",
+        "net-tests-utils",
         "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",
+        "ext",
+        "framework-minus-apex",
+        "framework-res",
         "framework-tethering",
     ],
     jni_libs: [
@@ -53,3 +68,25 @@
     ],
     jarjar_rules: "jarjar-rules.txt",
 }
+
+// Library containing the unit tests. This is used by the coverage test target to pull in the
+// unit test code. It is not currently used by the tests themselves because all the build
+// configuration needed by the tests is in the TetheringTestsDefaults rule.
+android_library {
+    name: "TetheringTestsLib",
+    defaults: ["TetheringTestsDefaults"],
+    visibility: [
+        "//frameworks/base/packages/Tethering/tests/integration",
+    ]
+}
+
+android_test {
+    name: "TetheringTests",
+    certificate: "platform",
+    test_suites: [
+        "device-tests",
+        "mts",
+    ],
+    defaults: ["TetheringTestsDefaults"],
+    compile_multilib: "both",
+}
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
index 530bc07..355342f 100644
--- a/packages/Tethering/tests/unit/AndroidManifest.xml
+++ b/packages/Tethering/tests/unit/AndroidManifest.xml
@@ -16,11 +16,18 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.networkstack.tethering.tests.unit">
 
-    <uses-permission android:name="android.permission.TETHER_PRIVILEGED"/>
-
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
+        <service
+            android:name="com.android.networkstack.tethering.MockTetheringService"
+            android:permission="android.permission.TETHER_PRIVILEGED"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.networkstack.tethering.TetheringService"/>
+            </intent-filter>
+        </service>
     </application>
+
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.networkstack.tethering.tests.unit"
         android:label="Tethering service tests">
diff --git a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt
similarity index 69%
rename from packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
rename to packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt
index d85389a..55c59dd 100644
--- a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
+++ b/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt
@@ -20,6 +20,7 @@
 import android.net.TetheredClient.AddressInfo
 import android.net.TetheringManager.TETHERING_BLUETOOTH
 import android.net.TetheringManager.TETHERING_USB
+import android.system.OsConstants.RT_SCOPE_UNIVERSE
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.testutils.assertParcelSane
@@ -30,16 +31,27 @@
 
 private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67))
 private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78))
-private val TEST_ADDR1 = LinkAddress(parseNumericAddress("192.168.113.3"), 24)
-private val TEST_ADDR2 = LinkAddress(parseNumericAddress("fe80::1:2:3"), 64)
-private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname")
+private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L)
+private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L)
+private val TEST_HOSTNAME = "test_hostname"
+private val TEST_OTHER_HOSTNAME = "test_other_hostname"
+private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, TEST_HOSTNAME)
 private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null)
 
+private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress(
+        parseNumericAddress(addr),
+        prefixLength,
+        0 /* flags */,
+        RT_SCOPE_UNIVERSE,
+        expTime /* deprecationTime */,
+        expTime /* expirationTime */)
+
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class TetheredClientTest {
     @Test
     fun testParceling() {
+        assertParcelSane(TEST_ADDRINFO1, fieldCount = 2)
         assertParcelSane(makeTestClient(), fieldCount = 3)
     }
 
@@ -56,7 +68,7 @@
         // Different hostname
         assertNotEquals(makeTestClient(), TetheredClient(
                 TEST_MACADDR,
-                listOf(AddressInfo(TEST_ADDR1, "test_other_hostname"), TEST_ADDRINFO2),
+                listOf(AddressInfo(TEST_ADDR1, TEST_OTHER_HOSTNAME), TEST_ADDRINFO2),
                 TETHERING_BLUETOOTH))
 
         // Null hostname
@@ -88,6 +100,21 @@
                 TETHERING_USB), client1.addAddresses(client2))
     }
 
+    @Test
+    fun testGetters() {
+        assertEquals(TEST_MACADDR, makeTestClient().macAddress)
+        assertEquals(listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), makeTestClient().addresses)
+        assertEquals(TETHERING_BLUETOOTH, makeTestClient().tetheringType)
+    }
+
+    @Test
+    fun testAddressInfo_Getters() {
+        assertEquals(TEST_ADDR1, TEST_ADDRINFO1.address)
+        assertEquals(TEST_ADDR2, TEST_ADDRINFO2.address)
+        assertEquals(TEST_HOSTNAME, TEST_ADDRINFO1.hostname)
+        assertEquals(null, TEST_ADDRINFO2.hostname)
+    }
+
     private fun makeTestClient() = TetheredClient(
             TEST_MACADDR,
             listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt
index 921fbed..1ea56cd 100644
--- a/packages/Tethering/tests/unit/jarjar-rules.txt
+++ b/packages/Tethering/tests/unit/jarjar-rules.txt
@@ -4,7 +4,6 @@
 rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1
 rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1
 rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1
-rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1
 rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
 rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
diff --git a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
index e8add98..a8857b2 100644
--- a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
@@ -42,7 +42,9 @@
 @SmallTest
 public class DhcpServingParamsParcelExtTest {
     private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123");
+    private static final Inet4Address TEST_CLIENT_ADDRESS = inet4Addr("192.168.0.42");
     private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b;
+    private static final int TEST_CLIENT_ADDRESS_PARCELED = 0xc0a8002a;
     private static final int TEST_PREFIX_LENGTH = 17;
     private static final int TEST_LEASE_TIME_SECS = 120;
     private static final int TEST_MTU = 1000;
@@ -105,6 +107,12 @@
         assertFalse(mParcel.metered);
     }
 
+    @Test
+    public void testSetClientAddr() {
+        mParcel.setSingleClientAddr(TEST_CLIENT_ADDRESS);
+        assertEquals(TEST_CLIENT_ADDRESS_PARCELED, mParcel.singleClientAddr);
+    }
+
     private static Inet4Address inet4Addr(String addr) {
         return (Inet4Address) parseNumericAddress(addr);
     }
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 948266d..307ebf1 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -17,11 +17,13 @@
 package android.net.ip;
 
 import static android.net.INetd.IF_STATE_UP;
+import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_NCM;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
@@ -38,12 +40,12 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 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;
@@ -66,11 +68,14 @@
 import android.net.LinkProperties;
 import android.net.MacAddress;
 import android.net.RouteInfo;
+import android.net.TetherOffloadRuleParcel;
 import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpEventCallbacks;
 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.ip.RouterAdvertisementDaemon.RaParams;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
@@ -85,6 +90,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Captor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
@@ -92,6 +98,8 @@
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -104,6 +112,7 @@
     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;
+    private static final boolean DEFAULT_USING_BPF_OFFLOAD = true;
 
     private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
             IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
@@ -128,10 +137,11 @@
     private NeighborEventConsumer mNeighborEventConsumer;
 
     private void initStateMachine(int interfaceType) throws Exception {
-        initStateMachine(interfaceType, false /* usingLegacyDhcp */);
+        initStateMachine(interfaceType, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD);
     }
 
-    private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
+    private void initStateMachine(int interfaceType, boolean usingLegacyDhcp,
+            boolean usingBpfOffload) throws Exception {
         doAnswer(inv -> {
             final IDhcpServerCallbacks cb = inv.getArgument(2);
             new Thread(() -> {
@@ -163,7 +173,7 @@
 
         mIpServer = new IpServer(
                 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
-                mCallback, usingLegacyDhcp, mDependencies);
+                mCallback, usingLegacyDhcp, usingBpfOffload, mDependencies);
         mIpServer.start();
         mNeighborEventConsumer = neighborCaptor.getValue();
 
@@ -177,17 +187,18 @@
 
     private void initTetheredStateMachine(int interfaceType, String upstreamIface)
             throws Exception {
-        initTetheredStateMachine(interfaceType, upstreamIface, false);
+        initTetheredStateMachine(interfaceType, upstreamIface, false,
+                DEFAULT_USING_BPF_OFFLOAD);
     }
 
     private void initTetheredStateMachine(int interfaceType, String upstreamIface,
-            boolean usingLegacyDhcp) throws Exception {
-        initStateMachine(interfaceType, usingLegacyDhcp);
+            boolean usingLegacyDhcp, boolean usingBpfOffload) throws Exception {
+        initStateMachine(interfaceType, usingLegacyDhcp, usingBpfOffload);
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
         if (upstreamIface != null) {
             LinkProperties lp = new LinkProperties();
             lp.setInterfaceName(upstreamIface);
-            dispatchTetherConnectionChanged(upstreamIface, lp);
+            dispatchTetherConnectionChanged(upstreamIface, lp, 0);
         }
         reset(mNetd, mCallback);
     }
@@ -202,7 +213,8 @@
         when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
                 .thenReturn(mIpNeighborMonitor);
         mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
-                mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
+                mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD,
+                mDependencies);
         mIpServer.start();
         mLooper.dispatchAll();
         verify(mCallback).updateInterfaceState(
@@ -448,7 +460,7 @@
         usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
                 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
         usbTeardownOrder.verify(mCallback).updateInterfaceState(
-                mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
+                mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_FORWARDING_ERROR);
         usbTeardownOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
@@ -491,8 +503,62 @@
     }
 
     @Test
+    public void startsDhcpServerOnNcm() throws Exception {
+        initStateMachine(TETHERING_NCM);
+        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+
+        assertDhcpStarted(new IpPrefix("192.168.42.0/24"));
+    }
+
+    @Test
+    public void testOnNewPrefixRequest() throws Exception {
+        initStateMachine(TETHERING_NCM);
+        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
+
+        final IDhcpEventCallbacks eventCallbacks;
+        final ArgumentCaptor<IDhcpEventCallbacks> dhcpEventCbsCaptor =
+                 ArgumentCaptor.forClass(IDhcpEventCallbacks.class);
+        verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), dhcpEventCbsCaptor.capture());
+        eventCallbacks = dhcpEventCbsCaptor.getValue();
+        assertDhcpStarted(new IpPrefix("192.168.42.0/24"));
+
+        // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
+        // onNewPrefixRequest callback.
+        eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
+        mLooper.dispatchAll();
+
+        final ArgumentCaptor<LinkProperties> lpCaptor =
+                ArgumentCaptor.forClass(LinkProperties.class);
+        InOrder inOrder = inOrder(mNetd, mCallback);
+        inOrder.verify(mCallback).updateInterfaceState(
+                mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
+        inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
+        inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
+        // One for ipv4 route, one for ipv6 link local route.
+        inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
+                any(), any());
+        inOrder.verify(mNetd).tetherApplyDnsInterfaces();
+        inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
+        verifyNoMoreInteractions(mCallback);
+
+        final LinkProperties linkProperties = lpCaptor.getValue();
+        final List<LinkAddress> linkAddresses = linkProperties.getLinkAddresses();
+        assertEquals(1, linkProperties.getLinkAddresses().size());
+        assertEquals(1, linkProperties.getRoutes().size());
+        final IpPrefix prefix = new IpPrefix(linkAddresses.get(0).getAddress(),
+                linkAddresses.get(0).getPrefixLength());
+        assertNotEquals(prefix, new IpPrefix("192.168.42.0/24"));
+
+        verify(mDhcpServer).updateParams(mDhcpParamsCaptor.capture(), any());
+        assertDhcpServingParams(mDhcpParamsCaptor.getValue(), prefix);
+    }
+
+    @Test
     public void doesNotStartDhcpServerIfDisabled() throws Exception {
-        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
+        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */,
+                DEFAULT_USING_BPF_OFFLOAD);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE);
 
         verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
@@ -514,9 +580,69 @@
         mLooper.dispatchAll();
     }
 
+    /**
+     * Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable
+     * AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as:
+     *
+     * private void checkFooCalled(StableParcelable p, ...) {
+     *     ArgumentCaptor<FooParam> captor = ArgumentCaptor.forClass(FooParam.class);
+     *     verify(mMock).foo(captor.capture());
+     *     Foo foo = captor.getValue();
+     *     assertFooMatchesExpectations(foo);
+     * }
+     *
+     * almost works, but not quite. This is because if the code under test calls foo() twice, the
+     * first call to checkFooCalled() matches both the calls, putting both calls into the captor,
+     * and then fails with TooManyActualInvocations. It also makes it harder to use other mockito
+     * features such as never(), inOrder(), etc.
+     *
+     * This approach isn't great because if the match fails, the error message is unhelpful
+     * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does
+     * work.
+     *
+     * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the
+     * TooManyActualInvocations problem described above by forcing the caller of the custom assert
+     * method to specify all expected invocations in one call. This is useful when the stable
+     * parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and
+     * RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here
+     * because there is no such object.
+     */
+    private static class TetherOffloadRuleParcelMatcher implements
+            ArgumentMatcher<TetherOffloadRuleParcel> {
+        public final int upstreamIfindex;
+        public final InetAddress dst;
+        public final MacAddress dstMac;
+
+        TetherOffloadRuleParcelMatcher(int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
+            this.upstreamIfindex = upstreamIfindex;
+            this.dst = dst;
+            this.dstMac = dstMac;
+        }
+
+        public boolean matches(TetherOffloadRuleParcel parcel) {
+            return upstreamIfindex == parcel.inputInterfaceIndex
+                    && (TEST_IFACE_PARAMS.index == parcel.outputInterfaceIndex)
+                    && Arrays.equals(dst.getAddress(), parcel.destination)
+                    && (128 == parcel.prefixLength)
+                    && Arrays.equals(TEST_IFACE_PARAMS.macAddr.toByteArray(), parcel.srcL2Address)
+                    && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address);
+        }
+
+        public String toString() {
+            return String.format("TetherOffloadRuleParcelMatcher(%d, %s, %s",
+                    upstreamIfindex, dst.getHostAddress(), dstMac);
+        }
+    }
+
+    private TetherOffloadRuleParcel matches(
+            int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
+        return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac));
+    }
+
     @Test
     public void addRemoveipv6ForwardingRules() throws Exception {
-        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */);
+        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+                DEFAULT_USING_BPF_OFFLOAD);
 
         final int myIfindex = TEST_IFACE_PARAMS.index;
         final int notMyIfindex = myIfindex - 1;
@@ -526,6 +652,7 @@
         final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2");
         final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1");
         final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234");
+        final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
         final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
         final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
 
@@ -537,13 +664,11 @@
 
         // 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()));
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
         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()));
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
         reset(mNetd);
 
         // Link-local and multicast neighbors are ignored.
@@ -553,13 +678,14 @@
         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()));
+        // NUD_FAILED events do not have a MAC address.
+        recvNewNeigh(myIfindex, neighA, NUD_FAILED, null);
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macNull));
         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()));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macNull));
         reset(mNetd);
 
         // Upstream changes result in deleting and re-adding the rules.
@@ -570,23 +696,17 @@
         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()));
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1);
+        inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA));
+        inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
+        inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB));
+        inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
         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()));
+        dispatchTetherConnectionChanged(null, null, 0);
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB));
         reset(mNetd);
 
         // If the upstream is IPv4-only, no rules are added.
@@ -597,49 +717,142 @@
 
         // Rules can be added again once upstream IPv6 connectivity is available.
         lp.setInterfaceName(UPSTREAM_IFACE);
-        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
         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());
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
+        verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
 
         // If upstream IPv6 connectivity is lost, rules are removed.
         reset(mNetd);
-        dispatchTetherConnectionChanged(UPSTREAM_IFACE, null);
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
 
         // When the interface goes down, rules are removed.
         lp.setInterfaceName(UPSTREAM_IFACE);
-        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
         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()));
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
         reset(mNetd);
 
         mIpServer.stop();
         mLooper.dispatchAll();
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
         reset(mNetd);
     }
 
+    @Test
+    public void enableDisableUsingBpfOffload() throws Exception {
+        final int myIfindex = TEST_IFACE_PARAMS.index;
+        final InetAddress neigh = InetAddresses.parseNumericAddress("2001:db8::1");
+        final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
+        final MacAddress macNull = MacAddress.fromString("00:00:00:00:00:00");
+
+        reset(mNetd);
+
+        // Expect that rules can be only added/removed when the BPF offload config is enabled.
+        // Note that the usingBpfOffload false case is not a realistic test case. Because IP
+        // neighbor monitor doesn't start if BPF offload is disabled, there should have no
+        // neighbor event listening. This is used for testing the protection check just in case.
+        // TODO: Perhaps remove this test once we don't need this check anymore.
+        for (boolean usingBpfOffload : new boolean[]{true, false}) {
+            initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+                    usingBpfOffload);
+
+            // A neighbor is added.
+            recvNewNeigh(myIfindex, neigh, NUD_REACHABLE, macA);
+            if (usingBpfOffload) {
+                verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neigh, macA));
+            } else {
+                verify(mNetd, never()).tetherOffloadRuleAdd(any());
+            }
+            reset(mNetd);
+
+            // A neighbor is deleted.
+            recvDelNeigh(myIfindex, neigh, NUD_STALE, macA);
+            if (usingBpfOffload) {
+                verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neigh, macNull));
+            } else {
+                verify(mNetd, never()).tetherOffloadRuleRemove(any());
+            }
+            reset(mNetd);
+        }
+    }
+
+    @Test
+    public void doesNotStartIpNeighborMonitorIfBpfOffloadDisabled() throws Exception {
+        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */,
+                false /* usingBpfOffload */);
+
+        // IP neighbor monitor doesn't start if BPF offload is disabled.
+        verify(mIpNeighborMonitor, never()).start();
+    }
+
+    private LinkProperties buildIpv6OnlyLinkProperties(final String iface) {
+        final LinkProperties linkProp = new LinkProperties();
+        linkProp.setInterfaceName(iface);
+        linkProp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+        linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, iface, RTN_UNICAST));
+        final InetAddress dns = InetAddresses.parseNumericAddress("2001:4860:4860::8888");
+        linkProp.addDnsServer(dns);
+
+        return linkProp;
+    }
+
+    @Test
+    public void testAdjustTtlValue() throws Exception {
+        final ArgumentCaptor<RaParams> raParamsCaptor =
+                ArgumentCaptor.forClass(RaParams.class);
+        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
+        verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+        final RaParams noV6Params = raParamsCaptor.getValue();
+        assertEquals(65, noV6Params.hopLimit);
+        reset(mRaDaemon);
+
+        when(mNetd.getProcSysNet(
+                INetd.IPV6, INetd.CONF, UPSTREAM_IFACE, "hop_limit")).thenReturn("64");
+        final LinkProperties lp = buildIpv6OnlyLinkProperties(UPSTREAM_IFACE);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 1);
+        verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+        final RaParams nonCellularParams = raParamsCaptor.getValue();
+        assertEquals(65, nonCellularParams.hopLimit);
+        reset(mRaDaemon);
+
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
+        verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+        final RaParams noUpstream = raParamsCaptor.getValue();
+        assertEquals(65, nonCellularParams.hopLimit);
+        reset(mRaDaemon);
+
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
+        verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture());
+        final RaParams cellularParams = raParamsCaptor.getValue();
+        assertEquals(63, cellularParams.hopLimit);
+        reset(mRaDaemon);
+    }
+
+    private void assertDhcpServingParams(final DhcpServingParamsParcel params,
+            final IpPrefix prefix) {
+        // Last address byte is random
+        assertTrue(prefix.contains(intToInet4AddressHTH(params.serverAddr)));
+        assertEquals(prefix.getPrefixLength(), params.serverAddrPrefixLength);
+        assertEquals(1, params.defaultRouters.length);
+        assertEquals(params.serverAddr, params.defaultRouters[0]);
+        assertEquals(1, params.dnsServers.length);
+        assertEquals(params.serverAddr, params.dnsServers[0]);
+        assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
+        if (mIpServer.interfaceType() == TETHERING_NCM) {
+            assertTrue(params.changePrefixOnDecline);
+        }
+    }
+
     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)).startWithCallbacks(
                 any(), any());
-        final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
-        // Last address byte is random
-        assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
-        assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
-        assertEquals(1, params.defaultRouters.length);
-        assertEquals(params.serverAddr, params.defaultRouters[0]);
-        assertEquals(1, params.dnsServers.length);
-        assertEquals(params.serverAddr, params.dnsServers[0]);
-        assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
+        assertDhcpServingParams(mDhcpParamsCaptor.getValue(), expectedPrefix);
     }
 
     /**
@@ -670,9 +883,10 @@
      * @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) {
+    private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp,
+            int ttlAdjustment) {
         dispatchTetherConnectionChanged(upstreamIface);
-        mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp);
+        mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp);
         mLooper.dispatchAll();
     }
 
diff --git a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
new file mode 100644
index 0000000..1499f3b
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.util;
+
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.net.LinkAddress;
+import android.net.TetheringRequestParcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.MiscAssertsKt;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetheringUtilsTest {
+    private static final LinkAddress TEST_SERVER_ADDR = new LinkAddress("192.168.43.1/24");
+    private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24");
+    private TetheringRequestParcel mTetheringRequest;
+
+    @Before
+    public void setUp() {
+        mTetheringRequest = makeTetheringRequestParcel();
+    }
+
+    public TetheringRequestParcel makeTetheringRequestParcel() {
+        final TetheringRequestParcel request = new TetheringRequestParcel();
+        request.tetheringType = TETHERING_WIFI;
+        request.localIPv4Address = TEST_SERVER_ADDR;
+        request.staticClientAddress = TEST_CLIENT_ADDR;
+        request.exemptFromEntitlementCheck = false;
+        request.showProvisioningUi = true;
+        return request;
+    }
+
+    @Test
+    public void testIsTetheringRequestEquals() throws Exception {
+        TetheringRequestParcel request = makeTetheringRequestParcel();
+
+        assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest));
+        assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+        assertTrue(TetheringUtils.isTetheringRequestEquals(null, null));
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, null));
+        assertFalse(TetheringUtils.isTetheringRequestEquals(null, mTetheringRequest));
+
+        request = makeTetheringRequestParcel();
+        request.tetheringType = TETHERING_USB;
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+
+        request = makeTetheringRequestParcel();
+        request.localIPv4Address = null;
+        request.staticClientAddress = null;
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+
+        request = makeTetheringRequestParcel();
+        request.exemptFromEntitlementCheck = true;
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+
+        request = makeTetheringRequestParcel();
+        request.showProvisioningUi = false;
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+
+        MiscAssertsKt.assertFieldCountEquals(5, TetheringRequestParcel.class);
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
similarity index 89%
rename from packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
rename to packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
index 56f3e21..d915354 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/ConnectedClientsTrackerTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering
+package com.android.networkstack.tethering
 
 import android.net.LinkAddress
 import android.net.MacAddress
@@ -46,23 +46,28 @@
 
     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)),
+            makeAddrInfo("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 client2Exp30AddrInfo = makeAddrInfo(
+            "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)),
+            makeAddrInfo("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)),
+            listOf(makeAddrInfo("2001:db8:34::34/72", "other_other_hostname", clock.time + 10)),
             TETHERING_USB)
 
+    private fun makeAddrInfo(addr: String, hostname: String?, expTime: Long) =
+            LinkAddress(addr).let {
+                AddressInfo(LinkAddress(it.address, it.prefixLength, it.flags, it.scope,
+                        expTime /* deprecationTime */, expTime /* expirationTime */), hostname)
+            }
+
     @Test
     fun testUpdateConnectedClients() {
         doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
@@ -154,4 +159,4 @@
             return time
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
similarity index 73%
rename from packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
rename to packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index 3a1d4a6..72fa916 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
@@ -31,11 +33,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -44,7 +47,7 @@
 import android.content.res.Resources;
 import android.net.util.SharedLog;
 import android.os.Bundle;
-import android.os.Message;
+import android.os.Handler;
 import android.os.PersistableBundle;
 import android.os.ResultReceiver;
 import android.os.SystemProperties;
@@ -55,29 +58,22 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
 import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.networkstack.tethering.R;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 
-import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public final class EntitlementManagerTest {
 
-    private static final int EVENT_EM_UPDATE = 1;
     private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
     private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
 
@@ -92,8 +88,8 @@
     private final PersistableBundle mCarrierConfig = new PersistableBundle();
     private final TestLooper mLooper = new TestLooper();
     private Context mMockContext;
+    private Runnable mPermissionChangeCallback;
 
-    private TestStateMachine mSM;
     private WrappedEntitlementManager mEnMgr;
     private TetheringConfiguration mConfig;
     private MockitoSession mMockingSession;
@@ -114,9 +110,9 @@
         public int uiProvisionCount = 0;
         public int silentProvisionCount = 0;
 
-        public WrappedEntitlementManager(Context ctx, StateMachine target,
-                SharedLog log, int what) {
-            super(ctx, target, log, what);
+        public WrappedEntitlementManager(Context ctx, Handler h, SharedLog log,
+                Runnable callback) {
+            super(ctx, h, log, callback);
         }
 
         public void reset() {
@@ -151,9 +147,8 @@
         doReturn(false).when(
                 () -> SystemProperties.getBoolean(
                 eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean()));
-        doReturn(false).when(
-                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
-                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+        doReturn(null).when(
+                () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), anyString()));
 
         when(mResources.getStringArray(R.array.config_tether_dhcp_range))
                 .thenReturn(new String[0]);
@@ -171,8 +166,9 @@
         when(mLog.forSubComponent(anyString())).thenReturn(mLog);
 
         mMockContext = new MockContext(mContext);
-        mSM = new TestStateMachine();
-        mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE);
+        mPermissionChangeCallback = spy(() -> { });
+        mEnMgr = new WrappedEntitlementManager(mMockContext, new Handler(mLooper.getLooper()), mLog,
+                mPermissionChangeCallback);
         mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
         mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
         mEnMgr.setTetheringConfigurationFetcher(() -> {
@@ -182,10 +178,6 @@
 
     @After
     public void tearDown() throws Exception {
-        if (mSM != null) {
-            mSM.quit();
-            mSM = null;
-        }
         mMockingSession.finishMocking();
     }
 
@@ -253,19 +245,16 @@
 
     @Test
     public void testRequestLastEntitlementCacheValue() throws Exception {
-        final CountDownLatch mCallbacklatch = new CountDownLatch(1);
         // 1. Entitlement check is not required.
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         ResultReceiver receiver = new ResultReceiver(null) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
-                mCallbacklatch.countDown();
             }
         };
         mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
         mLooper.dispatchAll();
-        callbackTimeoutHelper(mCallbacklatch);
         assertEquals(0, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
 
@@ -275,54 +264,47 @@
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
-                mCallbacklatch.countDown();
             }
         };
         mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
         mLooper.dispatchAll();
-        callbackTimeoutHelper(mCallbacklatch);
         assertEquals(0, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
         // 3. No cache value and ui entitlement check is needed.
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         receiver = new ResultReceiver(null) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
-                assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
-                mCallbacklatch.countDown();
+                assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
             }
         };
         mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
         mLooper.dispatchAll();
-        callbackTimeoutHelper(mCallbacklatch);
         assertEquals(1, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
-        // 4. Cache value is TETHER_ERROR_PROVISION_FAILED and don't need to run entitlement check.
+        // 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement
+        // check.
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         receiver = new ResultReceiver(null) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
-                assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
-                mCallbacklatch.countDown();
+                assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
             }
         };
         mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
         mLooper.dispatchAll();
-        callbackTimeoutHelper(mCallbacklatch);
         assertEquals(0, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
-        // 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed.
+        // 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed.
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         receiver = new ResultReceiver(null) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
-                mCallbacklatch.countDown();
             }
         };
         mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
         mLooper.dispatchAll();
-        callbackTimeoutHelper(mCallbacklatch);
         assertEquals(1, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
         // 6. Cache value is TETHER_ERROR_NO_ERROR.
@@ -331,12 +313,10 @@
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
-                mCallbacklatch.countDown();
             }
         };
         mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
         mLooper.dispatchAll();
-        callbackTimeoutHelper(mCallbacklatch);
         assertEquals(0, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
         // 7. Test get value for other downstream type.
@@ -344,84 +324,125 @@
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
-                mCallbacklatch.countDown();
             }
         };
         mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
         mLooper.dispatchAll();
-        callbackTimeoutHelper(mCallbacklatch);
+        assertEquals(0, mEnMgr.uiProvisionCount);
+        mEnMgr.reset();
+        // 8. Test get value for invalid downstream type.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
+            }
+        };
+        mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true);
+        mLooper.dispatchAll();
         assertEquals(0, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
     }
 
-    void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
-        if (!latch.await(1, TimeUnit.SECONDS)) {
-            fail("Timout, fail to receive callback");
-        }
+    private void assertPermissionChangeCallback(InOrder inOrder) {
+        inOrder.verify(mPermissionChangeCallback, times(1)).run();
+    }
+
+    private void assertNoPermissionChange(InOrder inOrder) {
+        inOrder.verifyNoMoreInteractions();
     }
 
     @Test
     public void verifyPermissionResult() {
+        final InOrder inOrder = inOrder(mPermissionChangeCallback);
         setupForRequiredProvisioning();
         mEnMgr.notifyUpstream(true);
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
         mLooper.dispatchAll();
+        // Permitted: true -> false
+        assertPermissionChangeCallback(inOrder);
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
+
         mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
         mLooper.dispatchAll();
+        // Permitted: false -> false
+        assertNoPermissionChange(inOrder);
+
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
         mLooper.dispatchAll();
+        // Permitted: false -> true
+        assertPermissionChangeCallback(inOrder);
         assertTrue(mEnMgr.isCellularUpstreamPermitted());
     }
 
     @Test
     public void verifyPermissionIfAllNotApproved() {
+        final InOrder inOrder = inOrder(mPermissionChangeCallback);
         setupForRequiredProvisioning();
         mEnMgr.notifyUpstream(true);
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
         mLooper.dispatchAll();
+        // Permitted: true -> false
+        assertPermissionChangeCallback(inOrder);
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
         mLooper.dispatchAll();
+        // Permitted: false -> false
+        assertNoPermissionChange(inOrder);
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
         mLooper.dispatchAll();
+        // Permitted: false -> false
+        assertNoPermissionChange(inOrder);
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
     }
 
     @Test
     public void verifyPermissionIfAnyApproved() {
+        final InOrder inOrder = inOrder(mPermissionChangeCallback);
         setupForRequiredProvisioning();
         mEnMgr.notifyUpstream(true);
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
         mLooper.dispatchAll();
+        // Permitted: true -> true
+        assertNoPermissionChange(inOrder);
         assertTrue(mEnMgr.isCellularUpstreamPermitted());
-        mLooper.dispatchAll();
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
         mLooper.dispatchAll();
+        // Permitted: true -> true
+        assertNoPermissionChange(inOrder);
         assertTrue(mEnMgr.isCellularUpstreamPermitted());
+
         mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
         mLooper.dispatchAll();
+        // Permitted: true -> false
+        assertPermissionChangeCallback(inOrder);
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
-
     }
 
     @Test
     public void verifyPermissionWhenProvisioningNotStarted() {
+        final InOrder inOrder = inOrder(mPermissionChangeCallback);
         assertTrue(mEnMgr.isCellularUpstreamPermitted());
+        assertNoPermissionChange(inOrder);
         setupForRequiredProvisioning();
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
+        assertNoPermissionChange(inOrder);
     }
 
     @Test
     public void testRunTetherProvisioning() {
+        final InOrder inOrder = inOrder(mPermissionChangeCallback);
         setupForRequiredProvisioning();
         // 1. start ui provisioning, upstream is mobile
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
@@ -431,16 +452,22 @@
         mLooper.dispatchAll();
         assertEquals(1, mEnMgr.uiProvisionCount);
         assertEquals(0, mEnMgr.silentProvisionCount);
+        // Permitted: true -> true
+        assertNoPermissionChange(inOrder);
         assertTrue(mEnMgr.isCellularUpstreamPermitted());
         mEnMgr.reset();
+
         // 2. start no-ui provisioning
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false);
         mLooper.dispatchAll();
         assertEquals(0, mEnMgr.uiProvisionCount);
         assertEquals(1, mEnMgr.silentProvisionCount);
+        // Permitted: true -> true
+        assertNoPermissionChange(inOrder);
         assertTrue(mEnMgr.isCellularUpstreamPermitted());
         mEnMgr.reset();
+
         // 3. tear down mobile, then start ui provisioning
         mEnMgr.notifyUpstream(false);
         mLooper.dispatchAll();
@@ -448,28 +475,58 @@
         mLooper.dispatchAll();
         assertEquals(0, mEnMgr.uiProvisionCount);
         assertEquals(0, mEnMgr.silentProvisionCount);
+        assertNoPermissionChange(inOrder);
         mEnMgr.reset();
+
         // 4. switch upstream back to mobile
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         mEnMgr.notifyUpstream(true);
         mLooper.dispatchAll();
         assertEquals(1, mEnMgr.uiProvisionCount);
         assertEquals(0, mEnMgr.silentProvisionCount);
+        // Permitted: true -> true
+        assertNoPermissionChange(inOrder);
         assertTrue(mEnMgr.isCellularUpstreamPermitted());
         mEnMgr.reset();
+
         // 5. tear down mobile, then switch SIM
         mEnMgr.notifyUpstream(false);
         mLooper.dispatchAll();
         mEnMgr.reevaluateSimCardProvisioning(mConfig);
         assertEquals(0, mEnMgr.uiProvisionCount);
         assertEquals(0, mEnMgr.silentProvisionCount);
+        assertNoPermissionChange(inOrder);
         mEnMgr.reset();
+
         // 6. switch upstream back to mobile again
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.notifyUpstream(true);
         mLooper.dispatchAll();
         assertEquals(0, mEnMgr.uiProvisionCount);
         assertEquals(3, mEnMgr.silentProvisionCount);
+        // Permitted: true -> false
+        assertPermissionChangeCallback(inOrder);
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
+        mEnMgr.reset();
+
+        // 7. start ui provisioning, upstream is mobile, downstream is ethernet
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true);
+        mLooper.dispatchAll();
+        assertEquals(1, mEnMgr.uiProvisionCount);
+        assertEquals(0, mEnMgr.silentProvisionCount);
+        // Permitted: false -> true
+        assertPermissionChangeCallback(inOrder);
+        assertTrue(mEnMgr.isCellularUpstreamPermitted());
+        mEnMgr.reset();
+
+        // 8. downstream is invalid
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true);
+        mLooper.dispatchAll();
+        assertEquals(0, mEnMgr.uiProvisionCount);
+        assertEquals(0, mEnMgr.silentProvisionCount);
+        assertNoPermissionChange(inOrder);
         mEnMgr.reset();
     }
 
@@ -477,7 +534,7 @@
     public void testCallStopTetheringWhenUiProvisioningFail() {
         setupForRequiredProvisioning();
         verify(mEntitlementFailedListener, times(0)).onUiEntitlementFailed(TETHERING_WIFI);
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.notifyUpstream(true);
         mLooper.dispatchAll();
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
@@ -486,31 +543,32 @@
         verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI);
     }
 
-    public class TestStateMachine extends StateMachine {
-        public final ArrayList<Message> messages = new ArrayList<>();
-        private final State
-                mLoggingState = new EntitlementManagerTest.TestStateMachine.LoggingState();
+    @Test
+    public void testsetExemptedDownstreamType() throws Exception {
+        setupForRequiredProvisioning();
+        // Cellular upstream is not permitted when no entitlement result.
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
 
-        class LoggingState extends State {
-            @Override public void enter() {
-                messages.clear();
-            }
+        // If there is exempted downstream and no other non-exempted downstreams, cellular is
+        // permitted.
+        mEnMgr.setExemptedDownstreamType(TETHERING_WIFI);
+        assertTrue(mEnMgr.isCellularUpstreamPermitted());
 
-            @Override public void exit() {
-                messages.clear();
-            }
+        // If second downstream run entitlement check fail, cellular upstream is not permitted.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
+        mEnMgr.notifyUpstream(true);
+        mLooper.dispatchAll();
+        mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
+        mLooper.dispatchAll();
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
 
-            @Override public boolean processMessage(Message msg) {
-                messages.add(msg);
-                return false;
-            }
-        }
+        // When second downstream is down, exempted downstream can use cellular upstream.
+        assertEquals(1, mEnMgr.uiProvisionCount);
+        verify(mEntitlementFailedListener).onUiEntitlementFailed(TETHERING_USB);
+        mEnMgr.stopProvisioningIfNeeded(TETHERING_USB);
+        assertTrue(mEnMgr.isCellularUpstreamPermitted());
 
-        public TestStateMachine() {
-            super("EntitlementManagerTest.TestStateMachine", mLooper.getLooper());
-            addState(mLoggingState);
-            setInitialState(mLoggingState);
-            super.start();
-        }
+        mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
     }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
new file mode 100644
index 0000000..f2b5314
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.networkstack.tethering;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
+import static android.net.ip.IpServer.STATE_TETHERED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.RouteInfo;
+import android.net.ip.IpServer;
+import android.net.util.SharedLog;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IPv6TetheringCoordinatorTest {
+    private static final String TEST_DNS_SERVER = "2001:4860:4860::8888";
+    private static final String TEST_INTERFACE = "test_rmnet0";
+    private static final String TEST_IPV6_ADDRESS = "2001:db8::1/64";
+    private static final String TEST_IPV4_ADDRESS = "192.168.100.1/24";
+
+    private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
+    private ArrayList<IpServer> mNotifyList;
+
+    @Mock private SharedLog mSharedLog;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
+        mNotifyList = new ArrayList<IpServer>();
+        mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mSharedLog);
+    }
+
+    private UpstreamNetworkState createDualStackUpstream(final int transportType) {
+        final Network network = mock(Network.class);
+        final NetworkCapabilities netCap =
+                new NetworkCapabilities.Builder().addTransportType(transportType).build();
+        final InetAddress dns = InetAddresses.parseNumericAddress(TEST_DNS_SERVER);
+        final LinkProperties linkProp = new LinkProperties();
+        linkProp.setInterfaceName(TEST_INTERFACE);
+        linkProp.addLinkAddress(new LinkAddress(TEST_IPV6_ADDRESS));
+        linkProp.addLinkAddress(new LinkAddress(TEST_IPV4_ADDRESS));
+        linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, TEST_INTERFACE, RTN_UNICAST));
+        linkProp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, TEST_INTERFACE,
+                    RTN_UNICAST));
+        linkProp.addDnsServer(dns);
+        return new UpstreamNetworkState(linkProp, netCap, network);
+    }
+
+    private void assertOnlyOneV6AddressAndNoV4(LinkProperties lp) {
+        assertEquals(lp.getInterfaceName(), TEST_INTERFACE);
+        assertFalse(lp.hasIpv4Address());
+        final List<LinkAddress> addresses = lp.getLinkAddresses();
+        assertEquals(addresses.size(), 1);
+        final LinkAddress v6Address = addresses.get(0);
+        assertEquals(v6Address, new LinkAddress(TEST_IPV6_ADDRESS));
+    }
+
+    @Test
+    public void testUpdateIpv6Upstream() throws Exception {
+        // 1. Add first IpServer.
+        final IpServer firstServer = mock(IpServer.class);
+        mNotifyList.add(firstServer);
+        mIPv6TetheringCoordinator.addActiveDownstream(firstServer, STATE_TETHERED);
+        verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+        verifyNoMoreInteractions(firstServer);
+
+        // 2. Add second IpServer and it would not have ipv6 tethering.
+        final IpServer secondServer = mock(IpServer.class);
+        mNotifyList.add(secondServer);
+        mIPv6TetheringCoordinator.addActiveDownstream(secondServer, STATE_LOCAL_ONLY);
+        verifyNoMoreInteractions(secondServer);
+        reset(firstServer, secondServer);
+
+        // 3. No upstream.
+        mIPv6TetheringCoordinator.updateUpstreamNetworkState(null);
+        verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+        reset(firstServer, secondServer);
+
+        // 4. Update ipv6 mobile upstream.
+        final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR);
+        final ArgumentCaptor<LinkProperties> lp = ArgumentCaptor.forClass(LinkProperties.class);
+        mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream);
+        verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0),
+                lp.capture());
+        final LinkProperties v6OnlyLink = lp.getValue();
+        assertOnlyOneV6AddressAndNoV4(v6OnlyLink);
+        verifyNoMoreInteractions(firstServer);
+        verifyNoMoreInteractions(secondServer);
+        reset(firstServer, secondServer);
+
+        // 5. Remove first IpServer.
+        mNotifyList.remove(firstServer);
+        mIPv6TetheringCoordinator.removeActiveDownstream(firstServer);
+        verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+        verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0),
+                lp.capture());
+        final LinkProperties localOnlyLink = lp.getValue();
+        assertNotNull(localOnlyLink);
+        assertNotEquals(localOnlyLink, v6OnlyLink);
+        reset(firstServer, secondServer);
+
+        // 6. Remove second IpServer.
+        mNotifyList.remove(secondServer);
+        mIPv6TetheringCoordinator.removeActiveDownstream(secondServer);
+        verifyNoMoreInteractions(firstServer);
+        verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
new file mode 100644
index 0000000..071a290e
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
@@ -0,0 +1,72 @@
+/*
+ * 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.networkstack.tethering;
+
+import static android.Manifest.permission.WRITE_SETTINGS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.ITetheringConnector;
+import android.os.Binder;
+import android.os.IBinder;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class MockTetheringService extends TetheringService {
+    private final Tethering mTethering = mock(Tethering.class);
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new MockTetheringConnector(super.onBind(intent));
+    }
+
+    @Override
+    public Tethering makeTethering(TetheringDependencies deps) {
+        return mTethering;
+    }
+
+    @Override
+    boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
+            @NonNull String callingPackage, @Nullable String callingAttributionTag,
+            boolean throwException) {
+        // Test this does not verify the calling package / UID, as calling package could be shell
+        // and not match the UID.
+        return context.checkCallingOrSelfPermission(WRITE_SETTINGS) == PERMISSION_GRANTED;
+    }
+
+    public Tethering getTethering() {
+        return mTethering;
+    }
+
+    public class MockTetheringConnector extends Binder {
+        final IBinder mBase;
+        MockTetheringConnector(IBinder base) {
+            mBase = base;
+        }
+
+        public ITetheringConnector getTetheringConnector() {
+            return ITetheringConnector.Stub.asInterface(mBase);
+        }
+
+        public MockTetheringService getService() {
+            return MockTetheringService.this;
+        }
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
similarity index 82%
rename from packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
rename to packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
index 7e62e5a..b291438 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.METERED_NO;
@@ -26,16 +26,18 @@
 import static android.net.RouteInfo.RTN_UNICAST;
 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
 
-import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_IFACE;
-import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_UID;
-import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
+import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE;
+import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
+import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
 import static com.android.testutils.MiscAssertsKt.assertContainsAll;
 import static com.android.testutils.MiscAssertsKt.assertThrows;
-import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals;
+import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals;
+
+import static junit.framework.Assert.assertNotNull;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyObject;
@@ -61,11 +63,10 @@
 import android.net.NetworkStats;
 import android.net.NetworkStats.Entry;
 import android.net.RouteInfo;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
 import android.net.util.SharedLog;
 import android.os.Handler;
-import android.os.Looper;
+import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.test.mock.MockContentResolver;
@@ -74,7 +75,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TestableNetworkStatsProviderCbBinder;
 
 import org.junit.After;
 import org.junit.Before;
@@ -108,15 +109,22 @@
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
     @Mock private NetworkStatsManager mStatsManager;
-    @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb;
+    @Mock private TetheringConfiguration mTetherConfig;
+    // Late init since methods must be called by the thread that created this object.
+    private TestableNetworkStatsProviderCbBinder mTetherStatsProviderCb;
+    private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider;
     private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
             ArgumentCaptor.forClass(ArrayList.class);
-    private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
-            mTetherStatsProviderCaptor =
-            ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
     private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
             ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
     private MockContentResolver mContentResolver;
+    private final TestLooper mTestLooper = new TestLooper();
+    private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() {
+        @Override
+        public TetheringConfiguration getTetherConfig() {
+            return mTetherConfig;
+        }
+    };
 
     @Before public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -126,8 +134,7 @@
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
         FakeSettingsProvider.clearSettingsProvider();
-        when(mStatsManager.registerNetworkStatsProvider(anyString(), any()))
-                .thenReturn(mTetherStatsProviderCb);
+        when(mTetherConfig.getOffloadPollInterval()).thenReturn(-1); // Disabled.
     }
 
     @After public void tearDown() throws Exception {
@@ -147,15 +154,26 @@
         Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
     }
 
+    private void setOffloadPollInterval(int interval) {
+        when(mTetherConfig.getOffloadPollInterval()).thenReturn(interval);
+    }
+
     private void waitForIdle() {
-        HandlerUtilsKt.waitForIdle(new Handler(Looper.getMainLooper()), WAIT_FOR_IDLE_TIMEOUT);
+        mTestLooper.dispatchAll();
     }
 
     private OffloadController makeOffloadController() throws Exception {
-        OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
-                mHardware, mContentResolver, mStatsManager, new SharedLog("test"));
+        OffloadController offload = new OffloadController(new Handler(mTestLooper.getLooper()),
+                mHardware, mContentResolver, mStatsManager, new SharedLog("test"), mDeps);
+        final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
+                tetherStatsProviderCaptor =
+                ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
         verify(mStatsManager).registerNetworkStatsProvider(anyString(),
-                mTetherStatsProviderCaptor.capture());
+                tetherStatsProviderCaptor.capture());
+        mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
+        assertNotNull(mTetherStatsProvider);
+        mTetherStatsProviderCb = new TestableNetworkStatsProviderCbBinder();
+        mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
         return offload;
     }
 
@@ -343,9 +361,9 @@
         stacked.setInterfaceName("stacked");
         stacked.addLinkAddress(new LinkAddress("192.0.2.129/25"));
         stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null,
-                  RTN_UNICAST));
+                RTN_UNICAST));
         stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null,
-                  RTN_UNICAST));
+                RTN_UNICAST));
         assertTrue(lp.addStackedLink(stacked));
         offload.setUpstreamLinkProperties(lp);
         // No change in local addresses means no call to setLocalPrefixes().
@@ -413,9 +431,6 @@
         final OffloadController offload = makeOffloadController();
         offload.start();
 
-        final OffloadController.OffloadTetheringStatsProvider provider =
-                mTetherStatsProviderCaptor.getValue();
-
         final String ethernetIface = "eth1";
         final String mobileIface = "rmnet_data0";
 
@@ -443,31 +458,22 @@
         inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
 
         // Verify that the fetched stats are stored.
-        final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE);
-        final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID);
+        final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
+        final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
         final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
-                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
+                .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+                .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
 
         final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
-                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
+                .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+                .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
 
-        assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats));
-        assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats));
-
-        final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass(
-                NetworkStats.class);
-        final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass(
-                NetworkStats.class);
+        assertNetworkStatsEquals(expectedIfaceStats, ifaceStats);
+        assertNetworkStatsEquals(expectedUidStats, uidStats);
 
         // Force pushing stats update to verify the stats reported.
-        provider.pushTetherStats();
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
-                ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
-        assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue()));
-        assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue()));
-
+        mTetherStatsProvider.pushTetherStats();
+        mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStats, expectedUidStats);
 
         when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
                 new ForwardedStats(100000, 100000));
@@ -483,33 +489,30 @@
         inOrder.verifyNoMoreInteractions();
 
         // Verify that the stored stats is accumulated.
-        final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE);
-        final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID);
+        final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
+        final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
         final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
-                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
+                .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+                .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
 
         final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
-                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
+                .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+                .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
 
-        assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu));
-        assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu));
+        assertNetworkStatsEquals(expectedIfaceStatsAccu, ifaceStatsAccu);
+        assertNetworkStatsEquals(expectedUidStatsAccu, uidStatsAccu);
 
         // Verify that only diff of stats is reported.
-        reset(mTetherStatsProviderCb);
-        provider.pushTetherStats();
+        mTetherStatsProvider.pushTetherStats();
         final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
-                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
+                .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
+                .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
 
         final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
-                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
-                ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
-        assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue()));
-        assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue()));
+                .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
+                .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
+        mTetherStatsProviderCb.expectNotifyStatsUpdated(expectedIfaceStatsDiff,
+                expectedUidStatsDiff);
     }
 
     @Test
@@ -529,19 +532,18 @@
         lp.setInterfaceName(ethernetIface);
         offload.setUpstreamLinkProperties(lp);
 
-        AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue();
         final InOrder inOrder = inOrder(mHardware);
         when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
         when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
 
         // Applying an interface quota to the current upstream immediately sends it to the hardware.
-        provider.setLimit(ethernetIface, ethernetLimit);
+        mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
         waitForIdle();
         inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
         inOrder.verifyNoMoreInteractions();
 
         // Applying an interface quota to another upstream does not take any immediate action.
-        provider.setLimit(mobileIface, mobileLimit);
+        mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
 
@@ -554,7 +556,7 @@
 
         // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
         // to Long.MAX_VALUE.
-        provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+        mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
         waitForIdle();
         inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
 
@@ -562,7 +564,7 @@
         when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
         lp.setInterfaceName(ethernetIface);
         offload.setUpstreamLinkProperties(lp);
-        provider.setLimit(mobileIface, mobileLimit);
+        mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
 
@@ -571,7 +573,7 @@
         when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
         lp.setInterfaceName(mobileIface);
         offload.setUpstreamLinkProperties(lp);
-        provider.setLimit(mobileIface, mobileLimit);
+        mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware).getForwardedStats(ethernetIface);
         inOrder.verify(mHardware).stopOffloadControl();
@@ -587,7 +589,7 @@
 
         OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
         callback.onStoppedLimitReached();
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+        mTetherStatsProviderCb.expectNotifyStatsUpdated();
     }
 
     @Test
@@ -691,8 +693,8 @@
         verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
         verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
         // TODO: verify the exact stats reported.
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
-        verifyNoMoreInteractions(mTetherStatsProviderCb);
+        mTetherStatsProviderCb.expectNotifyStatsUpdated();
+        mTetherStatsProviderCb.assertNoCallback();
         verifyNoMoreInteractions(mHardware);
     }
 
@@ -756,8 +758,8 @@
         // Verify forwarded stats behaviour.
         verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
         verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
-        verifyNoMoreInteractions(mTetherStatsProviderCb);
+        mTetherStatsProviderCb.expectNotifyStatsUpdated();
+        mTetherStatsProviderCb.assertNoCallback();
 
         // TODO: verify local prefixes and downstreams are also pushed to the HAL.
         verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
@@ -776,4 +778,50 @@
         verifyNoMoreInteractions(mHardware);
     }
 
+    @Test
+    public void testOnSetAlert() throws Exception {
+        setupFunctioningHardwareInterface();
+        enableOffload();
+        setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+        final OffloadController offload = makeOffloadController();
+        offload.start();
+
+        // Initialize with fake eth upstream.
+        final String ethernetIface = "eth1";
+        InOrder inOrder = inOrder(mHardware);
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(ethernetIface);
+        offload.setUpstreamLinkProperties(lp);
+        // Previous upstream was null, so no stats are fetched.
+        inOrder.verify(mHardware, never()).getForwardedStats(any());
+
+        // Verify that set quota to 0 will immediately triggers an callback.
+        mTetherStatsProvider.onSetAlert(0);
+        waitForIdle();
+        mTetherStatsProviderCb.expectNotifyAlertReached();
+
+        // Verify that notifyAlertReached never fired if quota is not yet reached.
+        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+                new ForwardedStats(0, 0));
+        mTetherStatsProvider.onSetAlert(100);
+        mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+        waitForIdle();
+        mTetherStatsProviderCb.assertNoCallback();
+
+        // Verify that notifyAlertReached fired when quota is reached.
+        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+                new ForwardedStats(50, 50));
+        mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+        waitForIdle();
+        mTetherStatsProviderCb.expectNotifyAlertReached();
+
+        // Verify that set quota with UNLIMITED won't trigger any callback, and won't fetch
+        // any stats since the polling is stopped.
+        reset(mHardware);
+        mTetherStatsProvider.onSetAlert(NetworkStatsProvider.QUOTA_UNLIMITED);
+        mTestLooper.moveTimeForward(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+        waitForIdle();
+        mTetherStatsProviderCb.assertNoCallback();
+        verify(mHardware, never()).getForwardedStats(any());
+    }
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
new file mode 100644
index 0000000..f8ff1cb
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.networkstack.tethering;
+
+import static android.net.util.TetheringUtils.uint16;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
+import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
+import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
+import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
+import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
+import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.net.util.SharedLog;
+import android.os.Handler;
+import android.os.NativeHandle;
+import android.os.test.TestLooper;
+import android.system.OsConstants;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class OffloadHardwareInterfaceTest {
+    private static final String RMNET0 = "test_rmnet_data0";
+
+    private final TestLooper mTestLooper = new TestLooper();
+
+    private OffloadHardwareInterface mOffloadHw;
+    private ITetheringOffloadCallback mTetheringOffloadCallback;
+    private OffloadHardwareInterface.ControlCallback mControlCallback;
+
+    @Mock private IOffloadConfig mIOffloadConfig;
+    @Mock private IOffloadControl mIOffloadControl;
+    @Mock private NativeHandle mNativeHandle;
+
+    class MyDependencies extends OffloadHardwareInterface.Dependencies {
+        MyDependencies(SharedLog log) {
+            super(log);
+        }
+
+        @Override
+        public IOffloadConfig getOffloadConfig() {
+            return mIOffloadConfig;
+        }
+
+        @Override
+        public IOffloadControl getOffloadControl() {
+            return mIOffloadControl;
+        }
+
+        @Override
+        public NativeHandle createConntrackSocket(final int groups) {
+            return mNativeHandle;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        final SharedLog log = new SharedLog("test");
+        mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
+                new MyDependencies(log));
+        mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
+    }
+
+    private void startOffloadHardwareInterface() throws Exception {
+        mOffloadHw.initOffloadConfig();
+        mOffloadHw.initOffloadControl(mControlCallback);
+        final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor =
+                ArgumentCaptor.forClass(ITetheringOffloadCallback.class);
+        verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any());
+        mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue();
+    }
+
+    @Test
+    public void testGetForwardedStats() throws Exception {
+        startOffloadHardwareInterface();
+        final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0);
+        verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any());
+        assertNotNull(stats);
+    }
+
+    @Test
+    public void testSetLocalPrefixes() throws Exception {
+        startOffloadHardwareInterface();
+        final ArrayList<String> localPrefixes = new ArrayList<>();
+        localPrefixes.add("127.0.0.0/8");
+        localPrefixes.add("fe80::/64");
+        mOffloadHw.setLocalPrefixes(localPrefixes);
+        verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any());
+    }
+
+    @Test
+    public void testSetDataLimit() throws Exception {
+        startOffloadHardwareInterface();
+        final long limit = 12345;
+        mOffloadHw.setDataLimit(RMNET0, limit);
+        verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any());
+    }
+
+    @Test
+    public void testSetUpstreamParameters() throws Exception {
+        startOffloadHardwareInterface();
+        final String v4addr = "192.168.10.1";
+        final String v4gateway = "192.168.10.255";
+        final ArrayList<String> v6gws = new ArrayList<>(0);
+        v6gws.add("2001:db8::1");
+        mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws);
+        verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
+                eq(v6gws), any());
+
+        final ArgumentCaptor<ArrayList<String>> mArrayListCaptor =
+                ArgumentCaptor.forClass(ArrayList.class);
+        mOffloadHw.setUpstreamParameters(null, null, null, null);
+        verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""),
+                mArrayListCaptor.capture(), any());
+        assertEquals(mArrayListCaptor.getValue().size(), 0);
+    }
+
+    @Test
+    public void testUpdateDownstreamPrefix() throws Exception {
+        startOffloadHardwareInterface();
+        final String ifName = "wlan1";
+        final String prefix = "192.168.43.0/24";
+        mOffloadHw.addDownstreamPrefix(ifName, prefix);
+        verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any());
+
+        mOffloadHw.removeDownstreamPrefix(ifName, prefix);
+        verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any());
+    }
+
+    @Test
+    public void testTetheringOffloadCallback() throws Exception {
+        startOffloadHardwareInterface();
+
+        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
+        mTestLooper.dispatchAll();
+        verify(mControlCallback).onStarted();
+
+        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR);
+        mTestLooper.dispatchAll();
+        verify(mControlCallback).onStoppedError();
+
+        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED);
+        mTestLooper.dispatchAll();
+        verify(mControlCallback).onStoppedUnsupported();
+
+        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE);
+        mTestLooper.dispatchAll();
+        verify(mControlCallback).onSupportAvailable();
+
+        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED);
+        mTestLooper.dispatchAll();
+        verify(mControlCallback).onStoppedLimitReached();
+
+        final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP);
+        mTetheringOffloadCallback.updateTimeout(tcpParams);
+        mTestLooper.dispatchAll();
+        verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP),
+                eq(tcpParams.src.addr),
+                eq(uint16(tcpParams.src.port)),
+                eq(tcpParams.dst.addr),
+                eq(uint16(tcpParams.dst.port)));
+
+        final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP);
+        mTetheringOffloadCallback.updateTimeout(udpParams);
+        mTestLooper.dispatchAll();
+        verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP),
+                eq(udpParams.src.addr),
+                eq(uint16(udpParams.src.port)),
+                eq(udpParams.dst.addr),
+                eq(uint16(udpParams.dst.port)));
+    }
+
+    private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) {
+        final NatTimeoutUpdate params = new NatTimeoutUpdate();
+        params.proto = proto;
+        params.src.addr = "192.168.43.200";
+        params.src.port = 100;
+        params.dst.addr = "172.50.46.169";
+        params.dst.port = 150;
+        return params;
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
similarity index 81%
rename from packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
rename to packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 3635964..1999ad7 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
@@ -30,7 +30,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.when;
 
@@ -44,7 +43,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.test.BroadcastInterceptingContext;
-import com.android.networkstack.tethering.R;
 
 import org.junit.After;
 import org.junit.Before;
@@ -110,12 +108,14 @@
                 .mockStatic(DeviceConfig.class)
                 .strictness(Strictness.WARN)
                 .startMocking();
-        doReturn(false).when(
-                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
-                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+        doReturn(null).when(
+                () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
 
         when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
                 new String[0]);
+        when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
+                TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
         when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
         when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
                 .thenReturn(new String[]{ "test_wlan\\d" });
@@ -126,6 +126,8 @@
                 .thenReturn(new String[0]);
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
                 false);
+        initializeBpfOffloadConfiguration(true, null /* unset */);
+
         mHasTelephonyManager = true;
         mMockContext = new MockContext(mContext);
         mEnableLegacyDhcpServer = false;
@@ -277,13 +279,57 @@
         assertFalse(upstreamIterator.hasNext());
     }
 
+    private void initializeBpfOffloadConfiguration(
+            final boolean fromRes, final String fromDevConfig) {
+        when(mResources.getBoolean(R.bool.config_tether_enable_bpf_offload)).thenReturn(fromRes);
+        doReturn(fromDevConfig).when(
+                () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.OVERRIDE_TETHER_ENABLE_BPF_OFFLOAD)));
+    }
+
+    @Test
+    public void testBpfOffloadEnabledByResource() {
+        initializeBpfOffloadConfiguration(true, null /* unset */);
+        final TetheringConfiguration enableByRes =
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertTrue(enableByRes.enableBpfOffload);
+    }
+
+    @Test
+    public void testBpfOffloadEnabledByDeviceConfigOverride() {
+        for (boolean res : new boolean[]{true, false}) {
+            initializeBpfOffloadConfiguration(res, "true");
+            final TetheringConfiguration enableByDevConOverride =
+                    new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+            assertTrue(enableByDevConOverride.enableBpfOffload);
+        }
+    }
+
+    @Test
+    public void testBpfOffloadDisabledByResource() {
+        initializeBpfOffloadConfiguration(false, null /* unset */);
+        final TetheringConfiguration disableByRes =
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertFalse(disableByRes.enableBpfOffload);
+    }
+
+    @Test
+    public void testBpfOffloadDisabledByDeviceConfigOverride() {
+        for (boolean res : new boolean[]{true, false}) {
+            initializeBpfOffloadConfiguration(res, "false");
+            final TetheringConfiguration disableByDevConOverride =
+                    new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+            assertFalse(disableByDevConOverride.enableBpfOffload);
+        }
+    }
+
     @Test
     public void testNewDhcpServerDisabled() {
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
                 true);
-        doReturn(false).when(
-                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
-                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+        doReturn("false").when(
+                () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
 
         final TetheringConfiguration enableByRes =
                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -291,9 +337,9 @@
 
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
                 false);
-        doReturn(true).when(
-                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
-                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+        doReturn("true").when(
+                () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
 
         final TetheringConfiguration enableByDevConfig =
                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -304,9 +350,9 @@
     public void testNewDhcpServerEnabled() {
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
                 false);
-        doReturn(false).when(
-                () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
-                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
+        doReturn("false").when(
+                () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
+                eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER)));
 
         final TetheringConfiguration cfg =
                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -315,6 +361,23 @@
     }
 
     @Test
+    public void testOffloadIntervalByResource() {
+        final TetheringConfiguration intervalByDefault =
+                new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+        assertEquals(TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS,
+                intervalByDefault.getOffloadPollInterval());
+
+        final int[] testOverrides = {0, 3000, -1};
+        for (final int override : testOverrides) {
+            when(mResources.getInteger(R.integer.config_tether_offload_poll_interval)).thenReturn(
+                    override);
+            final TetheringConfiguration overrideByRes =
+                    new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+            assertEquals(override, overrideByRes.getOffloadPollInterval());
+        }
+    }
+
+    @Test
     public void testGetResourcesBySubId() {
         setUpResourceForSubId();
         final TetheringConfiguration cfg = new TetheringConfiguration(
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
new file mode 100644
index 0000000..745468f
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -0,0 +1,354 @@
+/*
+ * 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.networkstack.tethering
+
+import android.app.Notification
+import android.app.NotificationManager
+import android.content.Context
+import android.content.res.Resources
+import android.net.ConnectivityManager.TETHERING_WIFI
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
+import android.os.UserHandle
+import android.telephony.TelephonyManager
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.test.BroadcastInterceptingContext
+import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
+import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
+import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID
+import com.android.testutils.waitForIdle
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+const val TEST_SUBID = 1
+const val WIFI_MASK = 1 shl TETHERING_WIFI
+const val TEST_DISALLOW_TITLE = "Tether function is disallowed"
+const val TEST_DISALLOW_MESSAGE = "Please contact your admin"
+const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access"
+const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet."
+const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot"
+const val TEST_ROAMING_TITLE = "Hotspot is on"
+const val TEST_ROAMING_MESSAGE = "Additional charges may apply while roaming."
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class TetheringNotificationUpdaterTest {
+    // lateinit used here for mocks as they need to be reinitialized between each test and the test
+    // should crash if they are used before being initialized.
+    @Mock private lateinit var mockContext: Context
+    @Mock private lateinit var notificationManager: NotificationManager
+    @Mock private lateinit var telephonyManager: TelephonyManager
+    @Mock private lateinit var testResources: Resources
+
+    // lateinit for these classes under test, as they should be reset to a different instance for
+    // every test but should always be initialized before use (or the test should crash).
+    private lateinit var context: TestContext
+    private lateinit var notificationUpdater: TetheringNotificationUpdater
+    private lateinit var fakeTetheringThread: HandlerThread
+
+    private val ROAMING_CAPABILITIES = NetworkCapabilities()
+    private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING)
+    private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general
+    private val TIMEOUT_MS = 500L
+
+    private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
+        override fun createContextAsUser(user: UserHandle, flags: Int) =
+                if (user == UserHandle.ALL) mockContext else this
+        override fun getSystemService(name: String) =
+                if (name == Context.TELEPHONY_SERVICE) telephonyManager
+                else super.getSystemService(name)
+    }
+
+    private inner class WrappedNotificationUpdater(c: Context, looper: Looper)
+        : TetheringNotificationUpdater(c, looper) {
+        override fun getResourcesForSubId(c: Context, subId: Int) =
+                if (subId == TEST_SUBID) testResources else super.getResourcesForSubId(c, subId)
+    }
+
+    private fun setupResources() {
+        doReturn(5).`when`(testResources)
+                .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
+        doReturn(true).`when`(testResources)
+                .getBoolean(R.bool.config_upstream_roaming_notification)
+        doReturn(TEST_DISALLOW_TITLE).`when`(testResources)
+                .getString(R.string.disable_tether_notification_title)
+        doReturn(TEST_DISALLOW_MESSAGE).`when`(testResources)
+                .getString(R.string.disable_tether_notification_message)
+        doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources)
+                .getString(R.string.no_upstream_notification_title)
+        doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources)
+                .getString(R.string.no_upstream_notification_message)
+        doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources)
+                .getString(R.string.no_upstream_notification_disable_button)
+        doReturn(TEST_ROAMING_TITLE).`when`(testResources)
+                .getString(R.string.upstream_roaming_notification_title)
+        doReturn(TEST_ROAMING_MESSAGE).`when`(testResources)
+                .getString(R.string.upstream_roaming_notification_message)
+    }
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        context = TestContext(InstrumentationRegistry.getInstrumentation().context)
+        doReturn(notificationManager).`when`(mockContext)
+                .getSystemService(Context.NOTIFICATION_SERVICE)
+        fakeTetheringThread = HandlerThread(this::class.simpleName)
+        fakeTetheringThread.start()
+        notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper)
+        setupResources()
+    }
+
+    @After
+    fun tearDown() {
+        fakeTetheringThread.quitSafely()
+    }
+
+    private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
+    private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)
+
+    private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) {
+        verify(notificationManager, never()).cancel(any(), eq(id))
+
+        val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
+        verify(notificationManager, times(1))
+                .notify(any(), eq(id), notificationCaptor.capture())
+
+        val notification = notificationCaptor.getValue()
+        assertEquals(iconId, notification.smallIcon.resId)
+        assertEquals(title, notification.title())
+        assertEquals(text, notification.text())
+
+        reset(notificationManager)
+    }
+
+    private fun verifyNotificationCancelled(
+        notificationIds: List<Int>,
+        resetAfterVerified: Boolean = true
+    ) {
+        notificationIds.forEach {
+            verify(notificationManager, times(1)).cancel(any(), eq(it))
+        }
+        if (resetAfterVerified) reset(notificationManager)
+    }
+
+    @Test
+    fun testRestrictedNotification() {
+        // Set test sub id.
+        notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+
+        // User restrictions on. Show restricted notification.
+        notificationUpdater.notifyTetheringDisabledByRestriction()
+        verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
+                RESTRICTED_NOTIFICATION_ID)
+
+        // User restrictions off. Clear notification.
+        notificationUpdater.tetheringRestrictionLifted()
+        verifyNotificationCancelled(listOf(RESTRICTED_NOTIFICATION_ID))
+
+        // No downstream.
+        notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+        verifyZeroInteractions(notificationManager)
+
+        // User restrictions on again. Show restricted notification.
+        notificationUpdater.notifyTetheringDisabledByRestriction()
+        verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
+                RESTRICTED_NOTIFICATION_ID)
+    }
+
+    val MAX_BACKOFF_MS = 200L
+    /**
+     * Waits for all messages, including delayed ones, to be processed.
+     *
+     * This will wait until the handler has no more messages to be processed including
+     * delayed ones, or the timeout has expired. It uses an exponential backoff strategy
+     * to wait longer and longer to consume less CPU, with the max granularity being
+     * MAX_BACKOFF_MS.
+     *
+     * @return true if all messages have been processed including delayed ones, false if timeout
+     *
+     * TODO: Move this method to com.android.testutils.HandlerUtils.kt.
+     */
+    private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) {
+        fun hasMatchingMessages() =
+                if (what == null) hasMessagesOrCallbacks() else hasMessages(what)
+        val expiry = System.currentTimeMillis() + timeoutMs
+        var delay = 5L
+        while (System.currentTimeMillis() < expiry && hasMatchingMessages()) {
+            // None of Handler, Looper, Message and MessageQueue expose any way to retrieve
+            // the time when the next (let alone the last) message will be processed, so
+            // short of examining the internals with reflection sleep() is the only solution.
+            Thread.sleep(delay)
+            delay = (delay * 2)
+                    .coerceAtMost(expiry - System.currentTimeMillis())
+                    .coerceAtMost(MAX_BACKOFF_MS)
+        }
+
+        val timeout = expiry - System.currentTimeMillis()
+        if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms")
+        waitForIdle(timeout)
+    }
+
+    @Test
+    fun testNoUpstreamNotification() {
+        // Set test sub id.
+        notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+
+        // Wifi downstream.
+        notificationUpdater.onDownstreamChanged(WIFI_MASK)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+
+        // There is no upstream. Show no upstream notification.
+        notificationUpdater.onUpstreamCapabilitiesChanged(null)
+        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
+        verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
+                NO_UPSTREAM_NOTIFICATION_ID)
+
+        // Same capabilities changed. Nothing happened.
+        notificationUpdater.onUpstreamCapabilitiesChanged(null)
+        verifyZeroInteractions(notificationManager)
+
+        // Upstream come back. Clear no upstream notification.
+        notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID))
+
+        // No upstream again. Show no upstream notification.
+        notificationUpdater.onUpstreamCapabilitiesChanged(null)
+        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
+        verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
+                NO_UPSTREAM_NOTIFICATION_ID)
+
+        // No downstream.
+        notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+
+        // Wifi downstream and home capabilities.
+        notificationUpdater.onDownstreamChanged(WIFI_MASK)
+        notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+
+        // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to -1 and change to no upstream
+        // again. Don't put up no upstream notification.
+        doReturn(-1).`when`(testResources)
+                .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
+        notificationUpdater.onUpstreamCapabilitiesChanged(null)
+        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID))
+    }
+
+    @Test
+    fun testGetResourcesForSubId() {
+        doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt())
+        doReturn(1234).`when`(telephonyManager).getSimCarrierId()
+        doReturn("000000").`when`(telephonyManager).getSimOperator()
+
+        val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId.
+        val config = context.resources.configuration
+        var res = notificationUpdater.getResourcesForSubId(context, subId)
+        assertEquals(config.mcc, res.configuration.mcc)
+        assertEquals(config.mnc, res.configuration.mnc)
+
+        doReturn(VERIZON_CARRIER_ID).`when`(telephonyManager).getSimCarrierId()
+        res = notificationUpdater.getResourcesForSubId(context, subId)
+        assertEquals(config.mcc, res.configuration.mcc)
+        assertEquals(config.mnc, res.configuration.mnc)
+
+        doReturn("20404").`when`(telephonyManager).getSimOperator()
+        res = notificationUpdater.getResourcesForSubId(context, subId)
+        assertEquals(311, res.configuration.mcc)
+        assertEquals(480, res.configuration.mnc)
+    }
+
+    @Test
+    fun testRoamingNotification() {
+        // Set test sub id.
+        notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+
+        // Wifi downstream.
+        notificationUpdater.onDownstreamChanged(WIFI_MASK)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+
+        // Upstream capabilities changed to roaming state. Show roaming notification.
+        notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
+        verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
+                ROAMING_NOTIFICATION_ID)
+
+        // Same capabilities change. Nothing happened.
+        notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
+        verifyZeroInteractions(notificationManager)
+
+        // Upstream capabilities changed to home state. Clear roaming notification.
+        notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
+        verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID))
+
+        // Upstream capabilities changed to roaming state again. Show roaming notification.
+        notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
+        verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
+                ROAMING_NOTIFICATION_ID)
+
+        // No upstream. Clear roaming notification and show no upstream notification.
+        notificationUpdater.onUpstreamCapabilitiesChanged(null)
+        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
+        verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
+        verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
+                NO_UPSTREAM_NOTIFICATION_ID)
+
+        // No downstream.
+        notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+
+        // Wifi downstream again.
+        notificationUpdater.onDownstreamChanged(WIFI_MASK)
+        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
+        verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
+        verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
+                NO_UPSTREAM_NOTIFICATION_ID)
+
+        // Set R.bool.config_upstream_roaming_notification to false and change upstream
+        // network to roaming state again. No roaming notification.
+        doReturn(false).`when`(testResources)
+                .getBoolean(R.bool.config_upstream_roaming_notification)
+        notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
+        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
new file mode 100644
index 0000000..cf060ba
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -0,0 +1,473 @@
+/*
+ * 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.networkstack.tethering;
+
+import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.TETHER_PRIVILEGED;
+import static android.Manifest.permission.WRITE_SETTINGS;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.UiAutomation;
+import android.content.Intent;
+import android.net.IIntResultListener;
+import android.net.ITetheringConnector;
+import android.net.ITetheringEventCallback;
+import android.net.TetheringRequestParcel;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ServiceTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.networkstack.tethering.MockTetheringService.MockTetheringConnector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class TetheringServiceTest {
+    private static final String TEST_IFACE_NAME = "test_wlan0";
+    private static final String TEST_CALLER_PKG = "com.android.shell";
+    private static final String TEST_ATTRIBUTION_TAG = null;
+    @Mock private ITetheringEventCallback mITetheringEventCallback;
+    @Rule public ServiceTestRule mServiceTestRule;
+    private Tethering mTethering;
+    private Intent mMockServiceIntent;
+    private ITetheringConnector mTetheringConnector;
+    private UiAutomation mUiAutomation;
+
+    private class TestTetheringResult extends IIntResultListener.Stub {
+        private int mResult = -1; // Default value that does not match any result code.
+        @Override
+        public void onResult(final int resultCode) {
+            mResult = resultCode;
+        }
+
+        public void assertResult(final int expected) {
+            assertEquals(expected, mResult);
+        }
+    }
+
+    private class MyResultReceiver extends ResultReceiver {
+        MyResultReceiver(Handler handler) {
+            super(handler);
+        }
+        private int mResult = -1; // Default value that does not match any result code.
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            mResult = resultCode;
+        }
+
+        public void assertResult(int expected) {
+            assertEquals(expected, mResult);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mUiAutomation =
+            InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        mServiceTestRule = new ServiceTestRule();
+        mMockServiceIntent = new Intent(
+                InstrumentationRegistry.getTargetContext(),
+                MockTetheringService.class);
+        final MockTetheringConnector mockConnector =
+                (MockTetheringConnector) mServiceTestRule.bindService(mMockServiceIntent);
+        mTetheringConnector = mockConnector.getTetheringConnector();
+        final MockTetheringService service = mockConnector.getService();
+        mTethering = service.getTethering();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mServiceTestRule.unbindService();
+        mUiAutomation.dropShellPermissionIdentity();
+    }
+
+    private interface TestTetheringCall {
+        void runTetheringCall(TestTetheringResult result) throws Exception;
+    }
+
+    private void runAsNoPermission(final TestTetheringCall test) throws Exception {
+        runTetheringCall(test, new String[0]);
+    }
+
+    private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception {
+        runTetheringCall(test, TETHER_PRIVILEGED);
+    }
+
+    private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception {
+        runTetheringCall(test, ACCESS_NETWORK_STATE);
+    }
+
+    private void runAsWriteSettings(final TestTetheringCall test) throws Exception {
+        runTetheringCall(test, WRITE_SETTINGS);
+    }
+
+    private void runTetheringCall(final TestTetheringCall test, String... permissions)
+            throws Exception {
+        if (permissions.length > 0) mUiAutomation.adoptShellPermissionIdentity(permissions);
+        try {
+            when(mTethering.isTetheringSupported()).thenReturn(true);
+            test.runTetheringCall(new TestTetheringResult());
+        } finally {
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    private void verifyNoMoreInteractionsForTethering() {
+        verifyNoMoreInteractions(mTethering);
+        verifyNoMoreInteractions(mITetheringEventCallback);
+        reset(mTethering, mITetheringEventCallback);
+    }
+
+    private void runTether(final TestTetheringResult result) throws Exception {
+        when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
+        mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+        verify(mTethering).isTetheringSupported();
+        verify(mTethering).tether(TEST_IFACE_NAME);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testTether() throws Exception {
+        runAsNoPermission((result) -> {
+            mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+                    result);
+            verify(mTethering).isTetherProvisioningRequired();
+            result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsTetherPrivileged((result) -> {
+            runTether(result);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsWriteSettings((result) -> {
+            runTether(result);
+            verify(mTethering).isTetherProvisioningRequired();
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+
+    private void runUnTether(final TestTetheringResult result) throws Exception {
+        when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
+        mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+                result);
+        verify(mTethering).isTetheringSupported();
+        verify(mTethering).untether(TEST_IFACE_NAME);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testUntether() throws Exception {
+        runAsNoPermission((result) -> {
+            mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+                    result);
+            verify(mTethering).isTetherProvisioningRequired();
+            result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsTetherPrivileged((result) -> {
+            runUnTether(result);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsWriteSettings((result) -> {
+            runUnTether(result);
+            verify(mTethering).isTetherProvisioningRequired();
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+
+    private void runSetUsbTethering(final TestTetheringResult result) throws Exception {
+        when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR);
+        mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
+                TEST_ATTRIBUTION_TAG, result);
+        verify(mTethering).isTetheringSupported();
+        verify(mTethering).setUsbTethering(true /* enable */);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testSetUsbTethering() throws Exception {
+        runAsNoPermission((result) -> {
+            mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
+                    TEST_ATTRIBUTION_TAG, result);
+            verify(mTethering).isTetherProvisioningRequired();
+            result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsTetherPrivileged((result) -> {
+            runSetUsbTethering(result);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsWriteSettings((result) -> {
+            runSetUsbTethering(result);
+            verify(mTethering).isTetherProvisioningRequired();
+            verifyNoMoreInteractionsForTethering();
+        });
+
+    }
+
+    private void runStartTethering(final TestTetheringResult result,
+            final TetheringRequestParcel request) throws Exception {
+        mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+                result);
+        verify(mTethering).isTetheringSupported();
+        verify(mTethering).startTethering(eq(request), eq(result));
+    }
+
+    @Test
+    public void testStartTethering() throws Exception {
+        final TetheringRequestParcel request = new TetheringRequestParcel();
+        request.tetheringType = TETHERING_WIFI;
+
+        runAsNoPermission((result) -> {
+            mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+                    result);
+            verify(mTethering).isTetherProvisioningRequired();
+            result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsTetherPrivileged((result) -> {
+            runStartTethering(result, request);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsWriteSettings((result) -> {
+            runStartTethering(result, request);
+            verify(mTethering).isTetherProvisioningRequired();
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+
+    @Test
+    public void testStartTetheringWithExemptFromEntitlementCheck() throws Exception {
+        final TetheringRequestParcel request = new TetheringRequestParcel();
+        request.tetheringType = TETHERING_WIFI;
+        request.exemptFromEntitlementCheck = true;
+
+        runAsTetherPrivileged((result) -> {
+            runStartTethering(result, request);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsWriteSettings((result) -> {
+            mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+                    result);
+            result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+
+    private void runStopTethering(final TestTetheringResult result) throws Exception {
+        mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
+                TEST_ATTRIBUTION_TAG, result);
+        verify(mTethering).isTetheringSupported();
+        verify(mTethering).stopTethering(TETHERING_WIFI);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testStopTethering() throws Exception {
+        runAsNoPermission((result) -> {
+            mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
+                    TEST_ATTRIBUTION_TAG, result);
+            verify(mTethering).isTetherProvisioningRequired();
+            result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsTetherPrivileged((result) -> {
+            runStopTethering(result);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsWriteSettings((result) -> {
+            runStopTethering(result);
+            verify(mTethering).isTetherProvisioningRequired();
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+
+    private void runRequestLatestTetheringEntitlementResult() throws Exception {
+        final MyResultReceiver result = new MyResultReceiver(null);
+        mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
+                true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
+        verify(mTethering).isTetheringSupported();
+        verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI),
+                eq(result), eq(true) /* showEntitlementUi */);
+    }
+
+    @Test
+    public void testRequestLatestTetheringEntitlementResult() throws Exception {
+        // Run as no permission.
+        final MyResultReceiver result = new MyResultReceiver(null);
+        mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
+                true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
+        verify(mTethering).isTetherProvisioningRequired();
+        result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+        verifyNoMoreInteractions(mTethering);
+
+        runAsTetherPrivileged((none) -> {
+            runRequestLatestTetheringEntitlementResult();
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsWriteSettings((none) -> {
+            runRequestLatestTetheringEntitlementResult();
+            verify(mTethering).isTetherProvisioningRequired();
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+
+    private void runRegisterTetheringEventCallback() throws Exception {
+        mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback,
+                TEST_CALLER_PKG);
+        verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback));
+    }
+
+    @Test
+    public void testRegisterTetheringEventCallback() throws Exception {
+        runAsNoPermission((result) -> {
+            mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback,
+                    TEST_CALLER_PKG);
+            verify(mITetheringEventCallback).onCallbackStopped(
+                    TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsTetherPrivileged((none) -> {
+            runRegisterTetheringEventCallback();
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsAccessNetworkState((none) -> {
+            runRegisterTetheringEventCallback();
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+
+    private void runUnregisterTetheringEventCallback() throws Exception {
+        mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback,
+                TEST_CALLER_PKG);
+        verify(mTethering).unregisterTetheringEventCallback(eq(mITetheringEventCallback));
+    }
+
+    @Test
+    public void testUnregisterTetheringEventCallback() throws Exception {
+        runAsNoPermission((result) -> {
+            mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback,
+                    TEST_CALLER_PKG);
+            verify(mITetheringEventCallback).onCallbackStopped(
+                    TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsTetherPrivileged((none) -> {
+            runUnregisterTetheringEventCallback();
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsAccessNetworkState((none) -> {
+            runUnregisterTetheringEventCallback();
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+
+    private void runStopAllTethering(final TestTetheringResult result) throws Exception {
+        mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+        verify(mTethering).isTetheringSupported();
+        verify(mTethering).untetherAll();
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testStopAllTethering() throws Exception {
+        runAsNoPermission((result) -> {
+            mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+            verify(mTethering).isTetherProvisioningRequired();
+            result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsTetherPrivileged((result) -> {
+            runStopAllTethering(result);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsWriteSettings((result) -> {
+            runStopAllTethering(result);
+            verify(mTethering).isTetherProvisioningRequired();
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+
+    private void runIsTetheringSupported(final TestTetheringResult result) throws Exception {
+        mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+        verify(mTethering).isTetheringSupported();
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testIsTetheringSupported() throws Exception {
+        runAsNoPermission((result) -> {
+            mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+                    result);
+            verify(mTethering).isTetherProvisioningRequired();
+            result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsTetherPrivileged((result) -> {
+            runIsTetheringSupported(result);
+            verifyNoMoreInteractionsForTethering();
+        });
+
+        runAsWriteSettings((result) -> {
+            runIsTetheringSupported(result);
+            verify(mTethering).isTetherProvisioningRequired();
+            verifyNoMoreInteractionsForTethering();
+        });
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
similarity index 81%
rename from packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
rename to packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 2f7c88a..7734a3c 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
@@ -38,6 +38,7 @@
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -46,7 +47,8 @@
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -55,6 +57,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
@@ -82,6 +85,7 @@
 import android.net.ConnectivityManager;
 import android.net.EthernetManager;
 import android.net.EthernetManager.TetheredInterfaceRequest;
+import android.net.IIntResultListener;
 import android.net.INetd;
 import android.net.ITetheringEventCallback;
 import android.net.InetAddresses;
@@ -135,17 +139,20 @@
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.networkstack.tethering.R;
 import com.android.testutils.MiscAssertsKt;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.util.ArrayList;
@@ -166,6 +173,8 @@
     private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
     private static final String TEST_NCM_IFNAME = "test_ncm0";
     private static final String TETHERING_NAME = "Tethering";
+    private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+    private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
 
     private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
 
@@ -208,7 +217,9 @@
     private Tethering mTethering;
     private PhoneStateListener mPhoneStateListener;
     private InterfaceConfigurationParcel mInterfaceConfiguration;
-
+    private TetheringConfiguration mConfig;
+    private EntitlementManager mEntitleMgr;
+    private OffloadController mOffloadCtrl;
 
     private class TestContext extends BroadcastInterceptingContext {
         TestContext(Context base) {
@@ -294,14 +305,15 @@
         }
     }
 
-    private class MockTetheringConfiguration extends TetheringConfiguration {
-        MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
+    // MyTetheringConfiguration is used to override static method for testing.
+    private class MyTetheringConfiguration extends TetheringConfiguration {
+        MyTetheringConfiguration(Context ctx, SharedLog log, int id) {
             super(ctx, log, id);
         }
 
         @Override
-        protected boolean getDeviceConfigBoolean(final String name) {
-            return false;
+        protected String getDeviceConfigProperty(final String name) {
+            return null;
         }
 
         @Override
@@ -325,6 +337,15 @@
         }
 
         @Override
+        public OffloadController getOffloadController(Handler h, SharedLog log,
+                OffloadController.Dependencies deps) {
+            mOffloadCtrl = spy(super.getOffloadController(h, log, deps));
+            // Return real object here instead of mock because
+            // testReportFailCallbackIfOffloadNotSupported depend on real OffloadController object.
+            return mOffloadCtrl;
+        }
+
+        @Override
         public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
                 StateMachine target, SharedLog log, int what) {
             mUpstreamNetworkMonitorMasterSM = target;
@@ -349,6 +370,13 @@
         }
 
         @Override
+        public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
+                Runnable callback) {
+            mEntitleMgr = spy(super.getEntitlementManager(ctx, h, log, callback));
+            return mEntitleMgr;
+        }
+
+        @Override
         public boolean isTetheringSupported() {
             return true;
         }
@@ -356,7 +384,8 @@
         @Override
         public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
                 int subId) {
-            return new MockTetheringConfiguration(ctx, log, subId);
+            mConfig = spy(new MyTetheringConfiguration(ctx, log, subId));
+            return mConfig;
         }
 
         @Override
@@ -381,7 +410,7 @@
         }
 
         @Override
-        public TetheringNotificationUpdater getNotificationUpdater(Context ctx) {
+        public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) {
             return mNotificationUpdater;
         }
     }
@@ -439,23 +468,23 @@
         return buildMobileUpstreamState(false, true, true);
     }
 
+    // See FakeSettingsProvider#clearSettingsProvider() that this needs to be called before and
+    // after use.
+    @BeforeClass
+    public static void setupOnce() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mResources.getStringArray(R.array.config_tether_dhcp_range))
                 .thenReturn(new String[0]);
-        when(mResources.getStringArray(R.array.config_tether_usb_regexs))
-                .thenReturn(new String[] { "test_rndis\\d" });
-        when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
-                .thenReturn(new String[]{ "test_wlan\\d" });
-        when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
-                .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
-        when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
-                .thenReturn(new String[0]);
-        when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
-                .thenReturn(new String[] { "test_ncm\\d" });
-        when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
-        when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false);
         when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
                 false);
         when(mNetd.interfaceGetList())
@@ -474,6 +503,7 @@
         mServiceContext = new TestContext(mContext);
         mContentResolver = new MockContentResolver(mServiceContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        setTetheringSupported(true /* supported */);
         mIntents = new Vector<>();
         mBroadcastReceiver = new BroadcastReceiver() {
             @Override
@@ -494,16 +524,47 @@
         mPhoneStateListener = phoneListenerCaptor.getValue();
     }
 
+    private void setTetheringSupported(final boolean supported) {
+        Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED,
+                supported ? 1 : 0);
+        when(mUserManager.hasUserRestriction(
+                UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported);
+        // Setup tetherable configuration.
+        when(mResources.getStringArray(R.array.config_tether_usb_regexs))
+                .thenReturn(new String[] { "test_rndis\\d" });
+        when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
+                .thenReturn(new String[]{ "test_wlan\\d" });
+        when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
+                .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
+        when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+                .thenReturn(new String[0]);
+        when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
+                .thenReturn(new String[] { "test_ncm\\d" });
+        when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
+        when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
+    }
+
+    private void initTetheringUpstream(UpstreamNetworkState upstreamState) {
+        when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
+        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+    }
+
     private Tethering makeTethering() {
         mTetheringDependencies.reset();
         return new Tethering(mTetheringDependencies);
     }
 
-    private TetheringRequestParcel createTetheringRquestParcel(final int type) {
+    private TetheringRequestParcel createTetheringRequestParcel(final int type) {
+        return createTetheringRequestParcel(type, null, null, false);
+    }
+
+    private TetheringRequestParcel createTetheringRequestParcel(final int type,
+            final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt) {
         final TetheringRequestParcel request = new TetheringRequestParcel();
         request.tetheringType = type;
-        request.localIPv4Address = null;
-        request.exemptFromEntitlementCheck = false;
+        request.localIPv4Address = serverAddr;
+        request.staticClientAddress = clientAddr;
+        request.exemptFromEntitlementCheck = exempt;
         request.showProvisioningUi = false;
 
         return request;
@@ -616,7 +677,7 @@
 
     private void prepareNcmTethering() {
         // Emulate startTethering(TETHERING_NCM) called
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null);
         mLooper.dispatchAll();
         verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
 
@@ -624,12 +685,10 @@
     }
 
     private void prepareUsbTethering(UpstreamNetworkState upstreamState) {
-        when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
-        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
-                .thenReturn(upstreamState);
+        initTetheringUpstream(upstreamState);
 
         // Emulate pressing the USB tethering button in Settings UI.
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_USB), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null);
         mLooper.dispatchAll();
         verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
 
@@ -652,7 +711,7 @@
         verify(mNetd, times(1)).interfaceGetList();
 
         // UpstreamNetworkMonitor should receive selected upstream
-        verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any());
+        verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream();
         verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
     }
 
@@ -824,8 +883,7 @@
 
         // Then 464xlat comes up
         upstreamState = buildMobile464xlatUpstreamState();
-        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
-                .thenReturn(upstreamState);
+        initTetheringUpstream(upstreamState);
 
         // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
         mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
@@ -903,7 +961,7 @@
         when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
 
         // Emulate pressing the WiFi tethering button.
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startSoftAp(null);
         verifyNoMoreInteractions(mWifiManager);
@@ -931,7 +989,7 @@
         when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
 
         // Emulate pressing the WiFi tethering button.
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startSoftAp(null);
         verifyNoMoreInteractions(mWifiManager);
@@ -1008,7 +1066,7 @@
         doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
 
         // Emulate pressing the WiFi tethering button.
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startSoftAp(null);
         verifyNoMoreInteractions(mWifiManager);
@@ -1057,82 +1115,84 @@
     }
 
     private void runUserRestrictionsChange(
-            boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList,
+            boolean currentDisallow, boolean nextDisallow, boolean isTetheringActive,
             int expectedInteractionsWithShowNotification) throws  Exception {
         final Bundle newRestrictions = new Bundle();
         newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow);
         final Tethering mockTethering = mock(Tethering.class);
-        when(mockTethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList);
+        when(mockTethering.isTetheringActive()).thenReturn(isTetheringActive);
         when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions);
 
         final Tethering.UserRestrictionActionListener ural =
-                new Tethering.UserRestrictionActionListener(mUserManager, mockTethering);
+                new Tethering.UserRestrictionActionListener(
+                        mUserManager, mockTethering, mNotificationUpdater);
         ural.mDisallowTethering = currentDisallow;
 
         ural.onUserRestrictionsChanged();
 
-        verify(mockTethering, times(expectedInteractionsWithShowNotification))
-                .untetherAll();
+        verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification))
+                .notifyTetheringDisabledByRestriction();
+        verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll();
     }
 
     @Test
-    public void testDisallowTetheringWhenNoTetheringInterfaceIsActive() throws Exception {
-        final String[] emptyActiveIfacesList = new String[]{};
+    public void testDisallowTetheringWhenTetheringIsNotActive() throws Exception {
+        final boolean isTetheringActive = false;
         final boolean currDisallow = false;
         final boolean nextDisallow = true;
         final int expectedInteractionsWithShowNotification = 0;
 
-        runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList,
+        runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
                 expectedInteractionsWithShowNotification);
     }
 
     @Test
-    public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
+    public void testDisallowTetheringWhenTetheringIsActive() throws Exception {
+        final boolean isTetheringActive = true;
         final boolean currDisallow = false;
         final boolean nextDisallow = true;
         final int expectedInteractionsWithShowNotification = 1;
 
-        runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+        runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
                 expectedInteractionsWithShowNotification);
     }
 
     @Test
-    public void testAllowTetheringWhenNoTetheringInterfaceIsActive() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{};
+    public void testAllowTetheringWhenTetheringIsNotActive() throws Exception {
+        final boolean isTetheringActive = false;
         final boolean currDisallow = true;
         final boolean nextDisallow = false;
         final int expectedInteractionsWithShowNotification = 0;
 
-        runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+        runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
                 expectedInteractionsWithShowNotification);
     }
 
     @Test
-    public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
+    public void testAllowTetheringWhenTetheringIsActive() throws Exception {
+        final boolean isTetheringActive = true;
         final boolean currDisallow = true;
         final boolean nextDisallow = false;
         final int expectedInteractionsWithShowNotification = 0;
 
-        runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+        runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
                 expectedInteractionsWithShowNotification);
     }
 
     @Test
     public void testDisallowTetheringUnchanged() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
+        final boolean isTetheringActive = true;
         final int expectedInteractionsWithShowNotification = 0;
         boolean currDisallow = true;
         boolean nextDisallow = true;
 
-        runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+        runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
                 expectedInteractionsWithShowNotification);
 
         currDisallow = false;
         nextDisallow = false;
 
-        runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+        runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive,
                 expectedInteractionsWithShowNotification);
     }
 
@@ -1294,16 +1354,14 @@
         callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
         // 2. Enable wifi tethering.
         UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
-        when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
-        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
-                .thenReturn(upstreamState);
+        initTetheringUpstream(upstreamState);
         when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
         mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
         mLooper.dispatchAll();
         tetherState = callback.pollTetherStatesChanged();
         assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
 
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
         mLooper.dispatchAll();
         tetherState = callback.pollTetherStatesChanged();
@@ -1392,16 +1450,17 @@
         mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
         final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration();
         assertEquals(fakeSubId, newConfig.activeDataSubId);
+        verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId));
     }
 
     @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);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
         mLooper.dispatchAll();
         verify(mEm, times(1)).requestTetheredInterface(any(), any());
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
         mLooper.dispatchAll();
         verifyNoMoreInteractions(mEm);
         mTethering.stopTethering(TETHERING_ETHERNET);
@@ -1580,6 +1639,203 @@
         assertTrue(element + " not found in " + collection, collection.contains(element));
     }
 
+    private class ResultListener extends IIntResultListener.Stub {
+        private final int mExpectedResult;
+        private boolean mHasResult = false;
+        ResultListener(final int expectedResult) {
+            mExpectedResult = expectedResult;
+        }
+
+        @Override
+        public void onResult(final int resultCode) {
+            mHasResult = true;
+            if (resultCode != mExpectedResult) {
+                fail("expected result: " + mExpectedResult + " but actual result: " + resultCode);
+            }
+        }
+
+        public void assertHasResult() {
+            if (!mHasResult) fail("No callback result");
+        }
+    }
+
+    @Test
+    public void testMultipleStartTethering() throws Exception {
+        final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24");
+        final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24");
+        final String serverAddr = "192.168.20.1";
+        final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+        final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+        final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+
+        // Enable USB tethering and check that Tethering starts USB.
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+                  null, null, false), firstResult);
+        mLooper.dispatchAll();
+        firstResult.assertHasResult();
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+        verifyNoMoreInteractions(mUsbManager);
+
+        // Enable USB tethering again with the same request and expect no change to USB.
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+                  null, null, false), secondResult);
+        mLooper.dispatchAll();
+        secondResult.assertHasResult();
+        verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        reset(mUsbManager);
+
+        // Enable USB tethering with a different request and expect that USB is stopped and
+        // started.
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+                  serverLinkAddr, clientLinkAddr, false), thirdResult);
+        mLooper.dispatchAll();
+        thirdResult.assertHasResult();
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+
+        // Expect that when USB comes up, the DHCP server is configured with the requested address.
+        mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+        sendUsbBroadcast(true, true, true, TETHERING_USB);
+        mLooper.dispatchAll();
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
+        verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
+    }
+
+    @Test
+    public void testRequestStaticIp() throws Exception {
+        final LinkAddress serverLinkAddr = new LinkAddress("192.168.0.123/24");
+        final LinkAddress clientLinkAddr = new LinkAddress("192.168.0.42/24");
+        final String serverAddr = "192.168.0.123";
+        final int clientAddrParceled = 0xc0a8002a;
+        final ArgumentCaptor<DhcpServingParamsParcel> dhcpParamsCaptor =
+                ArgumentCaptor.forClass(DhcpServingParamsParcel.class);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+                  serverLinkAddr, clientLinkAddr, false), null);
+        mLooper.dispatchAll();
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+        mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+        sendUsbBroadcast(true, true, true, TETHERING_USB);
+        mLooper.dispatchAll();
+        verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
+        verify(mIpServerDependencies, times(1)).makeDhcpServer(any(), dhcpParamsCaptor.capture(),
+                any());
+        final DhcpServingParamsParcel params = dhcpParamsCaptor.getValue();
+        assertEquals(serverAddr, intToInet4AddressHTH(params.serverAddr).getHostAddress());
+        assertEquals(24, params.serverAddrPrefixLength);
+        assertEquals(clientAddrParceled, params.singleClientAddr);
+    }
+
+    @Test
+    public void testUpstreamNetworkChanged() {
+        final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
+                mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
+        final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
+        initTetheringUpstream(upstreamState);
+        stateMachine.chooseUpstreamType(true);
+
+        verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network));
+        verify(mNotificationUpdater, times(1)).onUpstreamCapabilitiesChanged(any());
+    }
+
+    @Test
+    public void testUpstreamCapabilitiesChanged() {
+        final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
+                mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
+        final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
+        initTetheringUpstream(upstreamState);
+        stateMachine.chooseUpstreamType(true);
+
+        stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
+        // Should have two onUpstreamCapabilitiesChanged().
+        // One is called by reportUpstreamChanged(). One is called by EVENT_ON_CAPABILITIES.
+        verify(mNotificationUpdater, times(2)).onUpstreamCapabilitiesChanged(any());
+        reset(mNotificationUpdater);
+
+        // Verify that onUpstreamCapabilitiesChanged won't be called if not current upstream network
+        // capabilities changed.
+        final UpstreamNetworkState upstreamState2 = new UpstreamNetworkState(
+                upstreamState.linkProperties, upstreamState.networkCapabilities, new Network(101));
+        stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState2);
+        verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any());
+    }
+
+    @Test
+    public void testDumpTetheringLog() throws Exception {
+        final FileDescriptor mockFd = mock(FileDescriptor.class);
+        final PrintWriter mockPw = mock(PrintWriter.class);
+        runUsbTethering(null);
+        mLooper.startAutoDispatch();
+        mTethering.dump(mockFd, mockPw, new String[0]);
+        verify(mConfig).dump(any());
+        verify(mEntitleMgr).dump(any());
+        verify(mOffloadCtrl).dump(any());
+        mLooper.stopAutoDispatch();
+    }
+
+    @Test
+    public void testExemptFromEntitlementCheck() throws Exception {
+        setupForRequiredProvisioning();
+        final TetheringRequestParcel wifiNotExemptRequest =
+                createTetheringRequestParcel(TETHERING_WIFI, null, null, false);
+        mTethering.startTethering(wifiNotExemptRequest, null);
+        mLooper.dispatchAll();
+        verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false);
+        verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI);
+        assertFalse(mEntitleMgr.isCellularUpstreamPermitted());
+        mTethering.stopTethering(TETHERING_WIFI);
+        mLooper.dispatchAll();
+        verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI);
+        reset(mEntitleMgr);
+
+        setupForRequiredProvisioning();
+        final TetheringRequestParcel wifiExemptRequest =
+                createTetheringRequestParcel(TETHERING_WIFI, null, null, true);
+        mTethering.startTethering(wifiExemptRequest, null);
+        mLooper.dispatchAll();
+        verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false);
+        verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI);
+        assertTrue(mEntitleMgr.isCellularUpstreamPermitted());
+        mTethering.stopTethering(TETHERING_WIFI);
+        mLooper.dispatchAll();
+        verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI);
+        reset(mEntitleMgr);
+
+        // If one app enables tethering without provisioning check first, then another app enables
+        // tethering of the same type but does not disable the provisioning check.
+        setupForRequiredProvisioning();
+        mTethering.startTethering(wifiExemptRequest, null);
+        mLooper.dispatchAll();
+        verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false);
+        verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI);
+        assertTrue(mEntitleMgr.isCellularUpstreamPermitted());
+        reset(mEntitleMgr);
+        setupForRequiredProvisioning();
+        mTethering.startTethering(wifiNotExemptRequest, null);
+        mLooper.dispatchAll();
+        verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false);
+        verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI);
+        assertFalse(mEntitleMgr.isCellularUpstreamPermitted());
+        mTethering.stopTethering(TETHERING_WIFI);
+        mLooper.dispatchAll();
+        verify(mEntitleMgr).stopProvisioningIfNeeded(TETHERING_WIFI);
+        reset(mEntitleMgr);
+    }
+
+    private void setupForRequiredProvisioning() {
+        // Produce some acceptable looking provision app setting if requested.
+        when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
+                .thenReturn(PROVISIONING_APP_NAME);
+        when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
+                .thenReturn(PROVISIONING_NO_UI_APP_NAME);
+        // Act like the CarrierConfigManager is present and ready unless told otherwise.
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+                .thenReturn(mCarrierConfigManager);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
+        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+        mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+        sendConfigurationChanged();
+    }
     // TODO: Test that a request for hotspot mode doesn't interfere with an
     // already operating tethering mode interface.
 }
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
similarity index 98%
rename from packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
rename to packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
index 5ed75bf..232588c7 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package com.android.networkstack.tethering;
 
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -24,7 +24,7 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
-import static com.android.server.connectivity.tethering.UpstreamNetworkMonitor.TYPE_NONE;
+import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -212,8 +212,8 @@
         mUNM.updateMobileRequiresDun(true);
         mUNM.registerMobileNetworkRequest();
         verify(mCM, times(1)).requestNetwork(
-                any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
-                any(Handler.class));
+                any(NetworkRequest.class), anyInt(), anyInt(), any(Handler.class),
+                any(NetworkCallback.class));
 
         assertTrue(mUNM.mobileNetworkRequested());
         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
@@ -649,8 +649,8 @@
         }
 
         @Override
-        public void requestNetwork(NetworkRequest req, NetworkCallback cb,
-                int timeoutMs, int legacyType, Handler h) {
+        public void requestNetwork(NetworkRequest req,
+                int timeoutMs, int legacyType, Handler h, NetworkCallback cb) {
             assertFalse(allCallbacks.containsKey(cb));
             allCallbacks.put(cb, h);
             assertFalse(requested.containsKey(cb));
diff --git a/services/Android.bp b/services/Android.bp
index 5019bb1..0d221b9 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -72,11 +72,7 @@
 
     libs: [
         "android.hidl.manager-V1.0-java",
-        "framework-tethering-stubs",
-    ],
-
-    plugins: [
-        "compat-changeid-annotation-processor",
+        "framework-tethering-stubs-module_libs_api",
     ],
 
     // Uncomment to enable output of certain warnings (deprecated, unchecked)
@@ -94,8 +90,8 @@
 }
 
 platform_compat_config {
-    name: "services-platform-compat-config",
-    src: ":services",
+   name: "services-platform-compat-config",
+   src: ":services",
 }
 
 filegroup {
@@ -119,26 +115,33 @@
         " --hide DeprecationMismatch" +
         " --hide HiddenTypedefConstant",
     visibility: ["//visibility:private"],
+    filter_packages: ["com.android."],
     check_api: {
         current: {
             api_file: "api/current.txt",
             removed_api_file: "api/removed.txt",
         },
         last_released: {
-            api_file: ":last-released-system-server-api",
+            api_file: ":android.api.system-server.latest",
             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",
+            new_since: ":android.api.system-server.latest",
             baseline_file: "api/lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/system-server/api",
+        dest: "android.txt",
+    },
 }
 
 java_library {
-    name: "services-stubs",
+    name: "android_system_server_stubs_current",
     srcs: [":services-stubs.sources"],
     installable: false,
+    static_libs: ["android_module_lib_stubs_current"],
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 123c5d5..74fddb2 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -28,9 +28,9 @@
         "android.hardware.power-V1.0-java",
         "android.hardware.tv.cec-V1.0-java",
         "android.hardware.vibrator-java",
+        "android.net.ipsec.ike.stubs.module_lib",
         "app-compat-annotations",
-        "framework-tethering-stubs",
-        "ike-stubs",
+        "framework-tethering-stubs-module_libs_api",
     ],
 
     required: [
@@ -53,12 +53,8 @@
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
         "android.hidl.manager-V1.2-java",
-        "dnsresolver_aidl_interface-V2-java",
-        "netd_event_listener_interface-java",
-    ],
-
-    plugins: [
-        "compat-changeid-annotation-processor",
+        "dnsresolver_aidl_interface-java",
+        "netd_aidl_interfaces-platform-java",
     ],
 }
 
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index f71ff7b..559f219 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1735,8 +1735,9 @@
 
         final long nowElapsed = mInjector.getElapsedRealtime();
         final long nominalTrigger = convertToElapsed(triggerAtTime, type);
-        // Try to prevent spamming by making sure we aren't firing alarms in the immediate future
-        final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY;
+        // Try to prevent spamming by making sure apps aren't firing alarms in the immediate future
+        final long minTrigger = nowElapsed
+                + (UserHandle.isCore(callingUid) ? 0L : mConstants.MIN_FUTURITY);
         final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
 
         final long maxElapsed;
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index 207e007..ecbf9a4 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -54,7 +54,6 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.StatLogger;
 import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage;
 import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
@@ -62,6 +61,7 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Class to keep track of the information related to "force app standby", which includes:
@@ -416,12 +416,12 @@
             }
             mStarted = true;
 
-            mIActivityManager = Preconditions.checkNotNull(injectIActivityManager());
-            mActivityManagerInternal = Preconditions.checkNotNull(injectActivityManagerInternal());
-            mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
-            mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
-            mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
-            mUsageStatsManagerInternal = Preconditions.checkNotNull(
+            mIActivityManager = Objects.requireNonNull(injectIActivityManager());
+            mActivityManagerInternal = Objects.requireNonNull(injectActivityManagerInternal());
+            mAppOpsManager = Objects.requireNonNull(injectAppOpsManager());
+            mAppOpsService = Objects.requireNonNull(injectIAppOpsService());
+            mPowerManagerInternal = Objects.requireNonNull(injectPowerManagerInternal());
+            mUsageStatsManagerInternal = Objects.requireNonNull(
                     injectUsageStatsManagerInternal());
 
             mFlagsObserver = new FeatureFlagsObserver();
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index fa8eda5..ebaebae 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -106,11 +106,13 @@
     private static final int USER_SWITCHED_TIME_MS = 200;
     // Delay for the addProxy function in msec
     private static final int ADD_PROXY_DELAY_MS = 100;
+    // Delay for retrying enable and disable in msec
+    private static final int ENABLE_DISABLE_DELAY_MS = 300;
 
     private static final int MESSAGE_ENABLE = 1;
     private static final int MESSAGE_DISABLE = 2;
-    private static final int MESSAGE_REGISTER_ADAPTER = 20;
-    private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
+    private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3;
+    private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4;
     private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
     private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
     private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
@@ -130,6 +132,7 @@
     private static final int RESTORE_SETTING_TO_OFF = 0;
 
     private static final int MAX_ERROR_RESTART_RETRIES = 6;
+    private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10;
 
     // Bluetooth persisted setting is off
     private static final int BLUETOOTH_OFF = 0;
@@ -160,6 +163,8 @@
     private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
     private boolean mBinding;
     private boolean mUnbinding;
+    private int mWaitForEnableRetry;
+    private int mWaitForDisableRetry;
 
     private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
 
@@ -583,10 +588,9 @@
             Slog.w(TAG, "Callback is null in registerAdapter");
             return null;
         }
-        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
-        msg.obj = callback;
-        mHandler.sendMessage(msg);
-
+        synchronized (mCallbacks) {
+            mCallbacks.register(callback);
+        }
         return mBluetooth;
     }
 
@@ -596,9 +600,9 @@
             return;
         }
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
-        msg.obj = callback;
-        mHandler.sendMessage(msg);
+        synchronized (mCallbacks) {
+            mCallbacks.unregister(callback);
+        }
     }
 
     public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
@@ -740,13 +744,7 @@
         }
     }
 
-    public int updateBleAppCount(IBinder token, boolean enable, String packageName) {
-        // Check if packageName belongs to callingUid
-        final int callingUid = Binder.getCallingUid();
-        final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-        if (!isCallerSystem) {
-            checkPackage(callingUid, packageName);
-        }
+    private int updateBleAppCount(IBinder token, boolean enable, String packageName) {
         ClientDeathRecipient r = mBleApps.get(token);
         if (r == null && enable) {
             ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName);
@@ -771,15 +769,96 @@
         if (DBG) {
             Slog.d(TAG, appCount + " registered Ble Apps");
         }
-        if (appCount == 0 && mEnable) {
-            disableBleScanMode();
-        }
-        if (appCount == 0 && !mEnableExternal) {
-            sendBrEdrDownCallback();
-        }
         return appCount;
     }
 
+    private boolean checkBluetoothPermissions(String packageName, boolean requireForeground) {
+        if (isBluetoothDisallowed()) {
+            if (DBG) {
+                Slog.d(TAG, "checkBluetoothPermissions: bluetooth disallowed");
+            }
+            return false;
+        }
+        // Check if packageName belongs to callingUid
+        final int callingUid = Binder.getCallingUid();
+        final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
+        if (!isCallerSystem) {
+            checkPackage(callingUid, packageName);
+
+            if (requireForeground && !checkIfCallerIsForegroundUser()) {
+                Slog.w(TAG, "Not allowed for non-active and non system user");
+                return false;
+            }
+
+            mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                    "Need BLUETOOTH ADMIN permission");
+        }
+        return true;
+    }
+
+    public boolean enableBle(String packageName, IBinder token) throws RemoteException {
+        if (!checkBluetoothPermissions(packageName, false)) {
+            if (DBG) {
+                Slog.d(TAG, "enableBle(): bluetooth disallowed");
+            }
+            return false;
+        }
+
+        if (DBG) {
+            Slog.d(TAG, "enableBle(" + packageName + "):  mBluetooth =" + mBluetooth
+                    + " mBinding = " + mBinding + " mState = "
+                    + BluetoothAdapter.nameForState(mState));
+        }
+        updateBleAppCount(token, true, packageName);
+
+        if (mState == BluetoothAdapter.STATE_ON
+                || mState == BluetoothAdapter.STATE_BLE_ON
+                || mState == BluetoothAdapter.STATE_TURNING_ON
+                || mState == BluetoothAdapter.STATE_TURNING_OFF) {
+            Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+            return true;
+        }
+        synchronized (mReceiver) {
+            // waive WRITE_SECURE_SETTINGS permission check
+            sendEnableMsg(false,
+                    BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
+        }
+        return true;
+    }
+
+    public boolean disableBle(String packageName, IBinder token) throws RemoteException {
+        if (!checkBluetoothPermissions(packageName, false)) {
+            if (DBG) {
+                Slog.d(TAG, "disableBLE(): bluetooth disallowed");
+            }
+            return false;
+        }
+
+        if (DBG) {
+            Slog.d(TAG, "disableBle(" + packageName + "):  mBluetooth =" + mBluetooth
+                    + " mBinding = " + mBinding + " mState = "
+                    + BluetoothAdapter.nameForState(mState));
+        }
+
+        if (mState == BluetoothAdapter.STATE_OFF) {
+            Slog.d(TAG, "disableBLE(): Already disabled");
+            return false;
+        }
+        updateBleAppCount(token, false, packageName);
+
+        if (mState == BluetoothAdapter.STATE_BLE_ON && !isBleAppPresent()) {
+            if (mEnable) {
+                disableBleScanMode();
+            }
+            if (!mEnableExternal) {
+                addActiveLog(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
+                        packageName, false);
+                sendBrEdrDownCallback();
+            }
+        }
+        return true;
+    }
+
     // Clear all apps using BLE scan only mode.
     private void clearBleApps() {
         mBleApps.clear();
@@ -806,6 +885,13 @@
                 Slog.e(TAG, "onBluetoothServiceUp: mBluetooth is null!");
                 return;
             }
+            if (!mEnableExternal && !isBleAppPresent() && isAirplaneModeOn()) {
+                // Airplane mode is turned on while enabling BLE only mode, disable
+                // BLE now.
+                disableBleScanMode();
+                sendBrEdrDownCallback();
+                return;
+            }
             if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) {
                 // This triggers transition to STATE_ON
                 mBluetooth.onLeServiceUp();
@@ -855,29 +941,19 @@
     }
 
     public boolean enableNoAutoConnect(String packageName) {
-        if (isBluetoothDisallowed()) {
+        if (!checkBluetoothPermissions(packageName, false)) {
             if (DBG) {
                 Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed");
             }
             return false;
         }
 
-        // Check if packageName belongs to callingUid
-        final int callingUid = Binder.getCallingUid();
-        final boolean isCallerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-        if (!isCallerSystem) {
-            checkPackage(callingUid, packageName);
-        }
-
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                "Need BLUETOOTH ADMIN permission");
-
         if (DBG) {
             Slog.d(TAG, "enableNoAutoConnect():  mBluetooth =" + mBluetooth + " mBinding = "
                     + mBinding);
         }
-        int callingAppId = UserHandle.getAppId(callingUid);
 
+        int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
         if (callingAppId != Process.NFC_UID) {
             throw new SecurityException("no permission to enable Bluetooth quietly");
         }
@@ -892,32 +968,19 @@
     }
 
     public boolean enable(String packageName) throws RemoteException {
-        final int callingUid = Binder.getCallingUid();
-        final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-
-        if (isBluetoothDisallowed()) {
+        if (!checkBluetoothPermissions(packageName, true)) {
             if (DBG) {
                 Slog.d(TAG, "enable(): not enabling - bluetooth disallowed");
             }
             return false;
         }
 
-        if (!callerSystem) {
-            // Check if packageName belongs to callingUid
-            checkPackage(callingUid, packageName);
-
-            if (!checkIfCallerIsForegroundUser()) {
-                Slog.w(TAG, "enable(): not allowed for non-active and non system user");
-                return false;
-            }
-
-            mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                    "Need BLUETOOTH ADMIN permission");
-
-            if (!isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName,
-                    callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
-                return false;
-            }
+        final int callingUid = Binder.getCallingUid();
+        final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
+        if (!callerSystem && !isEnabled() && mWirelessConsentRequired
+                && startConsentUiIfNeeded(packageName,
+                callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
+            return false;
         }
 
         if (DBG) {
@@ -939,25 +1002,19 @@
     }
 
     public boolean disable(String packageName, boolean persist) throws RemoteException {
+        if (!checkBluetoothPermissions(packageName, true)) {
+            if (DBG) {
+                Slog.d(TAG, "disable(): not disabling - bluetooth disallowed");
+            }
+            return false;
+        }
+
         final int callingUid = Binder.getCallingUid();
         final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID;
-
-        if (!callerSystem) {
-            // Check if packageName belongs to callingUid
-            checkPackage(callingUid, packageName);
-
-            if (!checkIfCallerIsForegroundUser()) {
-                Slog.w(TAG, "disable(): not allowed for non-active and non system user");
-                return false;
-            }
-
-            mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                    "Need BLUETOOTH ADMIN permission");
-
-            if (isEnabled() && mWirelessConsentRequired && startConsentUiIfNeeded(packageName,
-                    callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
-                return false;
-            }
+        if (!callerSystem && isEnabled() && mWirelessConsentRequired
+                && startConsentUiIfNeeded(packageName,
+                callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
+            return false;
         }
 
         if (DBG) {
@@ -1418,18 +1475,20 @@
      * Inform BluetoothAdapter instances that Adapter service is up
      */
     private void sendBluetoothServiceUpCallback() {
-        try {
-            int n = mCallbacks.beginBroadcast();
-            Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
-            for (int i = 0; i < n; i++) {
-                try {
-                    mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+        synchronized (mCallbacks) {
+            try {
+                int n = mCallbacks.beginBroadcast();
+                Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+                for (int i = 0; i < n; i++) {
+                    try {
+                        mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+                    }
                 }
+            } finally {
+                mCallbacks.finishBroadcast();
             }
-        } finally {
-            mCallbacks.finishBroadcast();
         }
     }
 
@@ -1437,18 +1496,20 @@
      * Inform BluetoothAdapter instances that Adapter service is down
      */
     private void sendBluetoothServiceDownCallback() {
-        try {
-            int n = mCallbacks.beginBroadcast();
-            Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
-            for (int i = 0; i < n; i++) {
-                try {
-                    mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+        synchronized (mCallbacks) {
+            try {
+                int n = mCallbacks.beginBroadcast();
+                Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+                for (int i = 0; i < n; i++) {
+                    try {
+                        mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+                    }
                 }
+            } finally {
+                mCallbacks.finishBroadcast();
             }
-        } finally {
-            mCallbacks.finishBroadcast();
         }
     }
 
@@ -1597,8 +1658,18 @@
                     break;
 
                 case MESSAGE_ENABLE:
+                    int quietEnable = msg.arg1;
+                    if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED)
+                            || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
+                        // We are handling enable or disable right now, wait for it.
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE,
+                                quietEnable, 0), ENABLE_DISABLE_DELAY_MS);
+                        break;
+                    }
+
                     if (DBG) {
-                        Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth);
+                        Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = "
+                                + mBluetooth);
                     }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     mEnable = true;
@@ -1621,7 +1692,7 @@
                         mBluetoothLock.readLock().unlock();
                     }
 
-                    mQuietEnable = (msg.arg1 == 1);
+                    mQuietEnable = (quietEnable == 1);
                     if (mBluetooth == null) {
                         handleEnable(mQuietEnable);
                     } else {
@@ -1630,7 +1701,8 @@
                         // the previous Bluetooth process has exited. The
                         // waiting period has three components:
                         // (a) Wait until the local state is STATE_OFF. This
-                        //     is accomplished by "waitForOnOff(false, true)".
+                        //     is accomplished by sending delay a message
+                        //     MESSAGE_HANDLE_ENABLE_DELAYED
                         // (b) Wait until the STATE_OFF state is updated to
                         //     all components.
                         // (c) Wait until the Bluetooth process exits, and
@@ -1640,29 +1712,109 @@
                         // message. The delay time is backed off if Bluetooth
                         // continuously failed to turn on itself.
                         //
-                        waitForOnOff(false, true);
-                        Message restartMsg =
-                                mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                        mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
+                        mWaitForEnableRetry = 0;
+                        Message enableDelayedMsg =
+                                mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
+                        mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
                     }
                     break;
 
                 case MESSAGE_DISABLE:
+                    if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding
+                            || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
+                        // We are handling enable or disable right now, wait for it.
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE),
+                                ENABLE_DISABLE_DELAY_MS);
+                        break;
+                    }
+
                     if (DBG) {
-                        Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth);
+                        Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth
+                                + ", mBinding = " + mBinding);
                     }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+
                     if (mEnable && mBluetooth != null) {
-                        waitForOnOff(true, false);
-                        mEnable = false;
-                        handleDisable();
-                        waitForOnOff(false, false);
+                        mWaitForDisableRetry = 0;
+                        Message disableDelayedMsg =
+                                mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
+                        mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
                     } else {
                         mEnable = false;
                         handleDisable();
                     }
                     break;
 
+                case MESSAGE_HANDLE_ENABLE_DELAYED: {
+                    // The Bluetooth is turning off, wait for STATE_OFF
+                    if (mState != BluetoothAdapter.STATE_OFF) {
+                        if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
+                            mWaitForEnableRetry++;
+                            Message enableDelayedMsg =
+                                    mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
+                            mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
+                            break;
+                        } else {
+                            Slog.e(TAG, "Wait for STATE_OFF timeout");
+                        }
+                    }
+                    // Either state is changed to STATE_OFF or reaches the maximum retry, we
+                    // should move forward to the next step.
+                    mWaitForEnableRetry = 0;
+                    Message restartMsg =
+                            mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                    mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
+                    Slog.d(TAG, "Handle enable is finished");
+                    break;
+                }
+
+                case MESSAGE_HANDLE_DISABLE_DELAYED: {
+                    boolean disabling = (msg.arg1 == 1);
+                    Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling);
+                    if (!disabling) {
+                        // The Bluetooth is turning on, wait for STATE_ON
+                        if (mState != BluetoothAdapter.STATE_ON) {
+                            if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
+                                mWaitForDisableRetry++;
+                                Message disableDelayedMsg = mHandler.obtainMessage(
+                                        MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
+                                mHandler.sendMessageDelayed(disableDelayedMsg,
+                                        ENABLE_DISABLE_DELAY_MS);
+                                break;
+                            } else {
+                                Slog.e(TAG, "Wait for STATE_ON timeout");
+                            }
+                        }
+                        // Either state is changed to STATE_ON or reaches the maximum retry, we
+                        // should move forward to the next step.
+                        mWaitForDisableRetry = 0;
+                        mEnable = false;
+                        handleDisable();
+                        // Wait for state exiting STATE_ON
+                        Message disableDelayedMsg =
+                                mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
+                        mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
+                    } else {
+                        // The Bluetooth is turning off, wait for exiting STATE_ON
+                        if (mState == BluetoothAdapter.STATE_ON) {
+                            if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
+                                mWaitForDisableRetry++;
+                                Message disableDelayedMsg = mHandler.obtainMessage(
+                                        MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
+                                mHandler.sendMessageDelayed(disableDelayedMsg,
+                                        ENABLE_DISABLE_DELAY_MS);
+                                break;
+                            } else {
+                                Slog.e(TAG, "Wait for exiting STATE_ON timeout");
+                            }
+                        }
+                        // Either state is exited from STATE_ON or reaches the maximum retry, we
+                        // should move forward to the next step.
+                        Slog.d(TAG, "Handle disable is finished");
+                    }
+                    break;
+                }
+
                 case MESSAGE_RESTORE_USER_SETTING:
                     if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) {
                         if (DBG) {
@@ -1685,17 +1837,6 @@
                                 mContext.getPackageName());
                     }
                     break;
-
-                case MESSAGE_REGISTER_ADAPTER: {
-                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
-                    mCallbacks.register(callback);
-                    break;
-                }
-                case MESSAGE_UNREGISTER_ADAPTER: {
-                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
-                    mCallbacks.unregister(callback);
-                    break;
-                }
                 case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
                     IBluetoothStateChangeCallback callback =
                             (IBluetoothStateChangeCallback) msg.obj;
@@ -2031,6 +2172,7 @@
         try {
             mBluetoothLock.writeLock().lock();
             if ((mBluetooth == null) && (!mBinding)) {
+                Slog.d(TAG, "binding Bluetooth service");
                 //Start bind timeout and bind
                 Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
                 mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
@@ -2418,6 +2560,12 @@
                 writer.println("  " + app.getPackageName());
             }
 
+            writer.println("\nBluetoothManagerService:");
+            writer.println("  mEnable:" + mEnable);
+            writer.println("  mQuietEnable:" + mQuietEnable);
+            writer.println("  mEnableExternal:" + mEnableExternal);
+            writer.println("  mQuietEnableExternal:" + mQuietEnableExternal);
+
             writer.println("");
             writer.flush();
             if (args.length == 0) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6211769..6e026ab 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -18,6 +18,14 @@
 
 import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_DNS_EVENTS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_TCP_METRICS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
@@ -40,6 +48,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -48,10 +57,9 @@
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import static java.util.Map.Entry;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
@@ -63,6 +71,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.net.CaptivePortal;
@@ -71,6 +80,7 @@
 import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
 import android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import android.net.ConnectivityManager;
+import android.net.DataStallReportParcelable;
 import android.net.ICaptivePortal;
 import android.net.IConnectivityDiagnosticsCallback;
 import android.net.IConnectivityManager;
@@ -89,7 +99,6 @@
 import android.net.IpMemoryStore;
 import android.net.IpPrefix;
 import android.net.LinkProperties;
-import android.net.LinkProperties.CompareResult;
 import android.net.MatchAllNetworkSpecifier;
 import android.net.NattSocketKeepalive;
 import android.net.Network;
@@ -108,11 +117,13 @@
 import android.net.NetworkStack;
 import android.net.NetworkStackClient;
 import android.net.NetworkState;
+import android.net.NetworkTestResultParcelable;
 import android.net.NetworkUtils;
 import android.net.NetworkWatchlistManager;
 import android.net.PrivateDnsConfigParcel;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
 import android.net.SocketKeepalive;
 import android.net.TetheringManager;
 import android.net.UidRange;
@@ -123,6 +134,8 @@
 import android.net.metrics.NetworkEvent;
 import android.net.netlink.InetDiagMessage;
 import android.net.shared.PrivateDnsConfig;
+import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
+import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
 import android.os.Binder;
@@ -275,9 +288,6 @@
     // connect anyway?" dialog after the user selects a network that doesn't validate.
     private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
 
-    // How long to dismiss network notification.
-    private static final int TIMEOUT_NOTIFICATION_DELAY_MS = 20 * 1000;
-
     // Default to 30s linger time-out. Modifiable only for testing.
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
     private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
@@ -525,18 +535,13 @@
     private static final int EVENT_PROVISIONING_NOTIFICATION = 43;
 
     /**
-     * This event can handle dismissing notification by given network id.
-     */
-    private static final int EVENT_TIMEOUT_NOTIFICATION = 44;
-
-    /**
      * Used to specify whether a network should be used even if connectivity is partial.
      * arg1 = whether to accept the network if its connectivity is partial (1 for true or 0 for
      * false)
      * arg2 = whether to remember this choice in the future (1 for true or 0 for false)
      * obj  = network
      */
-    private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45;
+    private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 44;
 
     /**
      * Event for NetworkMonitor to inform ConnectivityService that the probe status has changed.
@@ -545,7 +550,7 @@
      * arg1 = A bitmask to describe which probes are completed.
      * arg2 = A bitmask to describe which probes are successful.
      */
-    public static final int EVENT_PROBE_STATUS_CHANGED = 46;
+    public static final int EVENT_PROBE_STATUS_CHANGED = 45;
 
     /**
      * Event for NetworkMonitor to inform ConnectivityService that captive portal data has changed.
@@ -553,7 +558,7 @@
      * arg2 = netId
      * obj = captive portal data
      */
-    private static final int EVENT_CAPPORT_DATA_CHANGED = 47;
+    private static final int EVENT_CAPPORT_DATA_CHANGED = 46;
 
     /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
@@ -658,8 +663,8 @@
     final MultipathPolicyTracker mMultipathPolicyTracker;
 
     @VisibleForTesting
-    final Map<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo>
-            mConnectivityDiagnosticsCallbacks = new HashMap<>();
+    final Map<IBinder, ConnectivityDiagnosticsCallbackInfo> mConnectivityDiagnosticsCallbacks =
+            new HashMap<>();
 
     /**
      * Implements support for the legacy "one network per network type" model.
@@ -933,7 +938,7 @@
          * @see IpConnectivityMetrics.Logger
          */
         public IpConnectivityMetrics.Logger getMetricsLogger() {
-            return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
+            return Objects.requireNonNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
                     "no IpConnectivityMetrics service");
         }
 
@@ -962,10 +967,10 @@
             IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd, Dependencies deps) {
         if (DBG) log("ConnectivityService starting up");
 
-        mDeps = checkNotNull(deps, "missing Dependencies");
+        mDeps = Objects.requireNonNull(deps, "missing Dependencies");
         mSystemProperties = mDeps.getSystemProperties();
         mNetIdManager = mDeps.makeNetIdManager();
-        mContext = checkNotNull(context, "missing Context");
+        mContext = Objects.requireNonNull(context, "missing Context");
 
         mMetricsLog = logger;
         mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
@@ -995,13 +1000,13 @@
 
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
 
-        mNMS = checkNotNull(netManager, "missing INetworkManagementService");
-        mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
-        mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
-        mPolicyManagerInternal = checkNotNull(
+        mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
+        mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
+        mPolicyManager = Objects.requireNonNull(policyManager, "missing INetworkPolicyManager");
+        mPolicyManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(NetworkPolicyManagerInternal.class),
                 "missing NetworkPolicyManagerInternal");
-        mDnsResolver = checkNotNull(dnsresolver, "missing IDnsResolver");
+        mDnsResolver = Objects.requireNonNull(dnsresolver, "missing IDnsResolver");
         mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
 
         mNetd = netd;
@@ -1676,7 +1681,7 @@
         if (newNc.getNetworkSpecifier() != null) {
             newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
         }
-        newNc.setAdministratorUids(Collections.EMPTY_LIST);
+        newNc.setAdministratorUids(new int[0]);
 
         return newNc;
     }
@@ -1720,7 +1725,7 @@
         }
 
         if (checkSettingsPermission(callerPid, callerUid)) {
-            return lp.makeSensitiveFieldsParcelingCopy();
+            return new LinkProperties(lp, true /* parcelSensitiveFields */);
         }
 
         final LinkProperties newLp = new LinkProperties(lp);
@@ -1737,7 +1742,7 @@
             nc.setSingleUid(callerUid);
         }
         nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
-        nc.setAdministratorUids(Collections.EMPTY_LIST);
+        nc.setAdministratorUids(new int[0]);
 
         // Clear owner UID; this can never come from an app.
         nc.setOwnerUid(INVALID_UID);
@@ -1824,11 +1829,12 @@
      * @return {@code true} on success, {@code false} on failure
      */
     @Override
-    public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+    public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress,
+            String callingPackageName, String callingAttributionTag) {
         if (disallowedBecauseSystemCaller()) {
             return false;
         }
-        enforceChangePermission();
+        enforceChangePermission(callingPackageName, callingAttributionTag);
         if (mProtectedNetworks.contains(networkType)) {
             enforceConnectivityRestrictedNetworksPermission();
         }
@@ -2082,8 +2088,8 @@
                 "ConnectivityService");
     }
 
-    private void enforceChangePermission() {
-        ConnectivityManager.enforceChangePermission(mContext);
+    private void enforceChangePermission(String callingPkg, String callingAttributionTag) {
+        ConnectivityManager.enforceChangePermission(mContext, callingPkg, callingAttributionTag);
     }
 
     private void enforceSettingsPermission() {
@@ -2098,6 +2104,20 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
+    private void enforceNetworkFactoryOrSettingsPermission() {
+        enforceAnyPermissionOf(
+                android.Manifest.permission.NETWORK_SETTINGS,
+                android.Manifest.permission.NETWORK_FACTORY,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+    }
+
+    private void enforceNetworkFactoryOrTestNetworksPermission() {
+        enforceAnyPermissionOf(
+                android.Manifest.permission.MANAGE_TEST_NETWORKS,
+                android.Manifest.permission.NETWORK_FACTORY,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+    }
+
     private boolean checkSettingsPermission() {
         return checkAnyPermissionOf(
                 android.Manifest.permission.NETWORK_SETTINGS,
@@ -2147,7 +2167,8 @@
     private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
         return checkAnyPermissionOf(pid, uid,
                 android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
-                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+                android.Manifest.permission.NETWORK_SETTINGS);
     }
 
     private void enforceConnectivityRestrictedNetworksPermission() {
@@ -2238,14 +2259,9 @@
             if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                 final NetworkInfo ni = intent.getParcelableExtra(
                         ConnectivityManager.EXTRA_NETWORK_INFO);
-                if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {
-                    intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
-                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                } else {
-                    BroadcastOptions opts = BroadcastOptions.makeBasic();
-                    opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
-                    options = opts.toBundle();
-                }
+                final BroadcastOptions opts = BroadcastOptions.makeBasic();
+                opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
+                options = opts.toBundle();
                 final IBatteryStats bs = mDeps.getBatteryStatsService();
                 try {
                     bs.noteConnectivityChanged(intent.getIntExtra(
@@ -2706,15 +2722,25 @@
 
             switch (msg.what) {
                 case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
-                    final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
+                    NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
                     if (networkCapabilities.hasConnectivityManagedCapability()) {
                         Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
                     }
+                    if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+                        // Make sure the original object is not mutated. NetworkAgent normally
+                        // makes a copy of the capabilities when sending the message through
+                        // the Messenger, but if this ever changes, not making a defensive copy
+                        // here will give attack vectors to clients using this code path.
+                        networkCapabilities = new NetworkCapabilities(networkCapabilities);
+                        networkCapabilities.restrictCapabilitesForTestNetwork(nai.creatorUid);
+                    }
                     updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
-                    handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
+                    LinkProperties newLp = (LinkProperties) msg.obj;
+                    processLinkPropertiesFromAgent(nai, newLp);
+                    handleUpdateLinkProperties(nai, newLp);
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
@@ -2796,14 +2822,6 @@
 
                     handleNetworkTested(nai, results.mTestResult,
                             (results.mRedirectUrl == null) ? "" : results.mRedirectUrl);
-
-                    // Invoke ConnectivityReport generation for this Network test event.
-                    final Message m =
-                            mConnectivityDiagnosticsHandler.obtainMessage(
-                                    ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED,
-                                    new ConnectivityReportEvent(results.mTimestampMillis, nai));
-                    m.setData(msg.getData());
-                    mConnectivityDiagnosticsHandler.sendMessage(m);
                     break;
                 }
                 case EVENT_PROVISIONING_NOTIFICATION: {
@@ -2870,13 +2888,6 @@
             final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
             final boolean wasValidated = nai.lastValidated;
             final boolean wasDefault = isDefaultNetwork(nai);
-            // Only show a connected notification if the network is pending validation
-            // after the captive portal app was open, and it has now validated.
-            if (nai.captivePortalValidationPending && valid) {
-                // User is now logged in, network validated.
-                nai.captivePortalValidationPending = false;
-                showNetworkNotification(nai, NotificationType.LOGGED_IN);
-            }
 
             if (DBG) {
                 final String logMsg = !TextUtils.isEmpty(redirectUrl)
@@ -2989,23 +3000,36 @@
 
         @Override
         public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
-            notifyNetworkTestedWithExtras(testResult, redirectUrl, SystemClock.elapsedRealtime(),
-                    PersistableBundle.EMPTY);
+            // Legacy version of notifyNetworkTestedWithExtras.
+            // Would only be called if the system has a NetworkStack module older than the
+            // framework, which does not happen in practice.
+            Slog.wtf(TAG, "Deprecated notifyNetworkTested called: no action taken");
         }
 
         @Override
-        public void notifyNetworkTestedWithExtras(
-                int testResult,
-                @Nullable String redirectUrl,
-                long timestampMillis,
-                @NonNull PersistableBundle extras) {
-            final Message msg =
-                    mTrackerHandler.obtainMessage(
-                            EVENT_NETWORK_TESTED,
-                            new NetworkTestedResults(
-                                    mNetId, testResult, timestampMillis, redirectUrl));
-            msg.setData(new Bundle(extras));
+        public void notifyNetworkTestedWithExtras(NetworkTestResultParcelable p) {
+            // Notify mTrackerHandler and mConnectivityDiagnosticsHandler of the event. Both use
+            // the same looper so messages will be processed in sequence.
+            final Message msg = mTrackerHandler.obtainMessage(
+                    EVENT_NETWORK_TESTED,
+                    new NetworkTestedResults(
+                            mNetId, p.result, p.timestampMillis, p.redirectUrl));
             mTrackerHandler.sendMessage(msg);
+
+            // Invoke ConnectivityReport generation for this Network test event.
+            final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(mNetId);
+            if (nai == null) return;
+            final Message m = mConnectivityDiagnosticsHandler.obtainMessage(
+                    ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED,
+                    new ConnectivityReportEvent(p.timestampMillis, nai));
+
+            final PersistableBundle extras = new PersistableBundle();
+            extras.putInt(KEY_NETWORK_VALIDATION_RESULT, p.result);
+            extras.putInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, p.probesSucceeded);
+            extras.putInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK, p.probesAttempted);
+
+            m.setData(new Bundle(extras));
+            mConnectivityDiagnosticsHandler.sendMessage(m);
         }
 
         @Override
@@ -3054,18 +3078,8 @@
         }
 
         @Override
-        public void notifyDataStallSuspected(
-                long timestampMillis, int detectionMethod, PersistableBundle extras) {
-            final Message msg =
-                    mConnectivityDiagnosticsHandler.obtainMessage(
-                            ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED,
-                            detectionMethod, mNetId, timestampMillis);
-            msg.setData(new Bundle(extras));
-
-            // NetworkStateTrackerHandler currently doesn't take any actions based on data
-            // stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
-            // the cost of going through two handlers.
-            mConnectivityDiagnosticsHandler.sendMessage(msg);
+        public void notifyDataStallSuspected(DataStallReportParcelable p) {
+            ConnectivityService.this.notifyDataStallSuspected(p, mNetId);
         }
 
         @Override
@@ -3079,6 +3093,37 @@
         }
     }
 
+    private void notifyDataStallSuspected(DataStallReportParcelable p, int netId) {
+        log("Data stall detected with methods: " + p.detectionMethod);
+
+        final PersistableBundle extras = new PersistableBundle();
+        int detectionMethod = 0;
+        if (hasDataStallDetectionMethod(p, DETECTION_METHOD_DNS_EVENTS)) {
+            extras.putInt(KEY_DNS_CONSECUTIVE_TIMEOUTS, p.dnsConsecutiveTimeouts);
+            detectionMethod |= DETECTION_METHOD_DNS_EVENTS;
+        }
+        if (hasDataStallDetectionMethod(p, DETECTION_METHOD_TCP_METRICS)) {
+            extras.putInt(KEY_TCP_PACKET_FAIL_RATE, p.tcpPacketFailRate);
+            extras.putInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS,
+                    p.tcpMetricsCollectionPeriodMillis);
+            detectionMethod |= DETECTION_METHOD_TCP_METRICS;
+        }
+
+        final Message msg = mConnectivityDiagnosticsHandler.obtainMessage(
+                ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED, detectionMethod, netId,
+                p.timestampMillis);
+        msg.setData(new Bundle(extras));
+
+        // NetworkStateTrackerHandler currently doesn't take any actions based on data
+        // stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
+        // the cost of going through two handlers.
+        mConnectivityDiagnosticsHandler.sendMessage(msg);
+    }
+
+    private boolean hasDataStallDetectionMethod(DataStallReportParcelable p, int detectionMethod) {
+        return (p.detectionMethod & detectionMethod) != 0;
+    }
+
     private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
         return isPrivateDnsValidationRequired(nai.networkCapabilities);
     }
@@ -3153,7 +3198,7 @@
             }
         }
 
-        nai.clatd.setNat64Prefix(prefix);
+        nai.clatd.setNat64PrefixFromDns(prefix);
         handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
     }
 
@@ -3339,6 +3384,8 @@
                         getNetworkPermission(networkAgent.networkCapabilities));
             }
             mDnsResolver.createNetworkCache(networkAgent.network.netId);
+            mDnsManager.updateTransportsForNetwork(networkAgent.network.netId,
+                    networkAgent.networkCapabilities.getTransportTypes());
             return true;
         } catch (RemoteException | ServiceSpecificException e) {
             loge("Error creating network " + networkAgent.network.netId + ": "
@@ -3757,12 +3804,6 @@
                 new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
         appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        // This runs on a random binder thread, but getNetworkAgentInfoForNetwork is thread-safe,
-        // and captivePortalValidationPending is volatile.
-        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-        if (nai != null) {
-            nai.captivePortalValidationPending = true;
-        }
         Binder.withCleanCallingIdentity(() ->
                 mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
     }
@@ -3881,14 +3922,6 @@
         final String action;
         final boolean highPriority;
         switch (type) {
-            case LOGGED_IN:
-                action = Settings.ACTION_WIFI_SETTINGS;
-                mHandler.removeMessages(EVENT_TIMEOUT_NOTIFICATION);
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NOTIFICATION,
-                        nai.network.netId, 0), TIMEOUT_NOTIFICATION_DELAY_MS);
-                // High priority because it is a direct result of the user logging in to a portal.
-                highPriority = true;
-                break;
             case NO_INTERNET:
                 action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED;
                 // High priority because it is only displayed for explicitly selected networks.
@@ -3916,7 +3949,7 @@
         }
 
         Intent intent = new Intent(action);
-        if (type != NotificationType.LOGGED_IN && type != NotificationType.PRIVATE_DNS_BROKEN) {
+        if (type != NotificationType.PRIVATE_DNS_BROKEN) {
             intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             intent.setClassName("com.android.settings",
@@ -4132,9 +4165,6 @@
                 case EVENT_DATA_SAVER_CHANGED:
                     handleRestrictBackgroundChanged(toBool(msg.arg1));
                     break;
-                case EVENT_TIMEOUT_NOTIFICATION:
-                    mNotifier.clearNotification(msg.arg1, NotificationType.LOGGED_IN);
-                    break;
             }
         }
     }
@@ -5358,7 +5388,7 @@
     // specific SSID/SignalStrength, or the calling app has permission to do so.
     private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc,
             int callerPid, int callerUid, String callerPackageName) {
-        if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) {
+        if (null != nc.getSsid() && !checkSettingsPermission(callerPid, callerUid)) {
             throw new SecurityException("Insufficient permissions to request a specific SSID");
         }
 
@@ -5423,10 +5453,26 @@
         }
     }
 
+    private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
+        final PackageManager pm = mContext.getPackageManager();
+        final int userId = UserHandle.getCallingUserId();
+        try {
+            final int callingVersion = pm.getApplicationInfoAsUser(
+                    callingPackageName, 0 /* flags */, userId).targetSdkVersion;
+            if (callingVersion < version) return false;
+        } catch (PackageManager.NameNotFoundException e) { }
+        return true;
+    }
+
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
             Messenger messenger, int timeoutMs, IBinder binder, int legacyType,
-            @NonNull String callingPackageName) {
+            @NonNull String callingPackageName, @Nullable String callingAttributionTag) {
+        if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
+            if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) {
+                throw new SecurityException("Insufficient permissions to specify legacy type");
+            }
+        }
         final int callingUid = Binder.getCallingUid();
         final NetworkRequest.Type type = (networkCapabilities == null)
                 ? NetworkRequest.Type.TRACK_DEFAULT
@@ -5439,7 +5485,8 @@
             enforceAccessPermission();
         } else {
             networkCapabilities = new NetworkCapabilities(networkCapabilities);
-            enforceNetworkRequestPermissions(networkCapabilities);
+            enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
+                    callingAttributionTag);
             // TODO: this is incorrect. We mark the request as metered or not depending on the state
             // of the app when the request is filed, but we never change the request if the app
             // changes network state. http://b/29964605
@@ -5474,11 +5521,12 @@
         return networkRequest;
     }
 
-    private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities) {
+    private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
+            String callingPackageName, String callingAttributionTag) {
         if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) == false) {
             enforceConnectivityRestrictedNetworksPermission();
         } else {
-            enforceChangePermission();
+            enforceChangePermission(callingPackageName, callingAttributionTag);
         }
     }
 
@@ -5529,11 +5577,13 @@
 
     @Override
     public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
-            PendingIntent operation, @NonNull String callingPackageName) {
-        checkNotNull(operation, "PendingIntent cannot be null.");
+            PendingIntent operation, @NonNull String callingPackageName,
+            @Nullable String callingAttributionTag) {
+        Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         final int callingUid = Binder.getCallingUid();
         networkCapabilities = new NetworkCapabilities(networkCapabilities);
-        enforceNetworkRequestPermissions(networkCapabilities);
+        enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
+                callingAttributionTag);
         enforceMeteredApnPolicy(networkCapabilities);
         ensureRequestableCapabilities(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
@@ -5559,7 +5609,7 @@
 
     @Override
     public void releasePendingNetworkRequest(PendingIntent operation) {
-        checkNotNull(operation, "PendingIntent cannot be null.");
+        Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
                 getCallingUid(), 0, operation));
     }
@@ -5618,7 +5668,7 @@
     @Override
     public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
             PendingIntent operation, @NonNull String callingPackageName) {
-        checkNotNull(operation, "PendingIntent cannot be null.");
+        Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         final int callingUid = Binder.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
@@ -5679,7 +5729,7 @@
 
     @Override
     public int registerNetworkProvider(Messenger messenger, String name) {
-        enforceNetworkFactoryPermission();
+        enforceNetworkFactoryOrSettingsPermission();
         NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
                 null /* asyncChannel */, nextNetworkProviderId(),
                 () -> unregisterNetworkProvider(messenger));
@@ -5689,7 +5739,7 @@
 
     @Override
     public void unregisterNetworkProvider(Messenger messenger) {
-        enforceNetworkFactoryPermission();
+        enforceNetworkFactoryOrSettingsPermission();
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
     }
 
@@ -5709,7 +5759,11 @@
 
     @Override
     public void declareNetworkRequestUnfulfillable(NetworkRequest request) {
-        enforceNetworkFactoryPermission();
+        if (request.hasTransport(TRANSPORT_TEST)) {
+            enforceNetworkFactoryOrTestNetworksPermission();
+        } else {
+            enforceNetworkFactoryPermission();
+        }
         mHandler.post(() -> handleReleaseNetworkRequest(request, Binder.getCallingUid(), true));
     }
 
@@ -5803,22 +5857,34 @@
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
-        enforceNetworkFactoryPermission();
+        if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+            enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
+            // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
+            // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
+            // sees capabilities that may be malicious, which might prevent mistakes in the future.
+            networkCapabilities = new NetworkCapabilities(networkCapabilities);
+            networkCapabilities.restrictCapabilitesForTestNetwork(Binder.getCallingUid());
+        } else {
+            enforceNetworkFactoryPermission();
+        }
 
         LinkProperties lp = new LinkProperties(linkProperties);
-        lp.ensureDirectlyConnectedRoutes();
+
         // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
         // satisfies mDefaultRequest.
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
                 currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
-                this, mNetd, mDnsResolver, mNMS, providerId);
-        // Make sure the network capabilities reflect what the agent info says.
+                this, mNetd, mDnsResolver, mNMS, providerId, Binder.getCallingUid());
+
+        // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
         nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
+        processLinkPropertiesFromAgent(nai, nai.linkProperties);
+
         final String extraInfo = networkInfo.getExtraInfo();
         final String name = TextUtils.isEmpty(extraInfo)
-                ? nai.networkCapabilities.getSSID() : extraInfo;
+                ? nai.networkCapabilities.getSsid() : extraInfo;
         if (DBG) log("registerNetworkAgent " + nai);
         final long token = Binder.clearCallingIdentity();
         try {
@@ -5853,13 +5919,18 @@
         updateUids(nai, null, nai.networkCapabilities);
     }
 
+    private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
+        lp.ensureDirectlyConnectedRoutes();
+        nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix());
+    }
+
     private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
             @NonNull LinkProperties oldLp) {
         int netId = networkAgent.network.netId;
 
-        // The NetworkAgentInfo does not know whether clatd is running on its network or not, or
-        // whether there is a NAT64 prefix. Before we do anything else, make sure its LinkProperties
-        // are accurate.
+        // The NetworkAgent does not know whether clatd is running on its network or not, or whether
+        // a NAT64 prefix was discovered by the DNS resolver. Before we do anything else, make sure
+        // the LinkProperties for the network are accurate.
         networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
 
         updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
@@ -5906,7 +5977,8 @@
             // Start or stop DNS64 detection and 464xlat according to network state.
             networkAgent.clatd.update();
             notifyIfacesChangedForNetworkStats();
-            networkAgent.networkMonitor().notifyLinkPropertiesChanged(newLp);
+            networkAgent.networkMonitor().notifyLinkPropertiesChanged(
+                    new LinkProperties(newLp, true /* parcelSensitiveFields */));
             if (networkAgent.everConnected) {
                 notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
             }
@@ -5976,15 +6048,49 @@
         }
     }
 
+    // TODO: move to frameworks/libs/net.
+    private RouteInfoParcel convertRouteInfo(RouteInfo route) {
+        final String nextHop;
+
+        switch (route.getType()) {
+            case RouteInfo.RTN_UNICAST:
+                if (route.hasGateway()) {
+                    nextHop = route.getGateway().getHostAddress();
+                } else {
+                    nextHop = INetd.NEXTHOP_NONE;
+                }
+                break;
+            case RouteInfo.RTN_UNREACHABLE:
+                nextHop = INetd.NEXTHOP_UNREACHABLE;
+                break;
+            case RouteInfo.RTN_THROW:
+                nextHop = INetd.NEXTHOP_THROW;
+                break;
+            default:
+                nextHop = INetd.NEXTHOP_NONE;
+                break;
+        }
+
+        final RouteInfoParcel rip = new RouteInfoParcel();
+        rip.ifName = route.getInterface();
+        rip.destination = route.getDestination().toString();
+        rip.nextHop = nextHop;
+        rip.mtu = route.getMtu();
+
+        return rip;
+    }
+
     /**
      * Have netd update routes from oldLp to newLp.
      * @return true if routes changed between oldLp and newLp
      */
     private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
-        // Compare the route diff to determine which routes should be added and removed.
-        CompareResult<RouteInfo> routeDiff = new CompareResult<>(
-                oldLp != null ? oldLp.getAllRoutes() : null,
-                newLp != null ? newLp.getAllRoutes() : null);
+        // compare the route diff to determine which routes have been updated
+        final CompareOrUpdateResult<RouteInfo.RouteKey, RouteInfo> routeDiff =
+                new CompareOrUpdateResult<>(
+                        oldLp != null ? oldLp.getAllRoutes() : null,
+                        newLp != null ? newLp.getAllRoutes() : null,
+                        (r) -> r.getRouteKey());
 
         // add routes before removing old in case it helps with continuous connectivity
 
@@ -5993,10 +6099,10 @@
             if (route.hasGateway()) continue;
             if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNMS.addRoute(netId, route);
+                mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
             } catch (Exception e) {
                 if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
-                    loge("Exception in addRoute for non-gateway: " + e);
+                    loge("Exception in networkAddRouteParcel for non-gateway: " + e);
                 }
             }
         }
@@ -6004,10 +6110,10 @@
             if (!route.hasGateway()) continue;
             if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNMS.addRoute(netId, route);
+                mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
             } catch (Exception e) {
                 if ((route.getGateway() instanceof Inet4Address) || VDBG) {
-                    loge("Exception in addRoute for gateway: " + e);
+                    loge("Exception in networkAddRouteParcel for gateway: " + e);
                 }
             }
         }
@@ -6015,12 +6121,22 @@
         for (RouteInfo route : routeDiff.removed) {
             if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
             try {
-                mNMS.removeRoute(netId, route);
+                mNetd.networkRemoveRouteParcel(netId, convertRouteInfo(route));
             } catch (Exception e) {
-                loge("Exception in removeRoute: " + e);
+                loge("Exception in networkRemoveRouteParcel: " + e);
             }
         }
-        return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty();
+
+        for (RouteInfo route : routeDiff.updated) {
+            if (VDBG || DDBG) log("Updating Route [" + route + "] from network " + netId);
+            try {
+                mNetd.networkUpdateRouteParcel(netId, convertRouteInfo(route));
+            } catch (Exception e) {
+                loge("Exception in networkUpdateRouteParcel: " + e);
+            }
+        }
+        return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty()
+                || !routeDiff.updated.isEmpty();
     }
 
     private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
@@ -6036,7 +6152,13 @@
             log("Setting DNS servers for network " + netId + " to " + dnses);
         }
         try {
-            mDnsManager.setDnsConfigurationForNetwork(netId, newLp, isDefaultNetwork);
+            mDnsManager.noteDnsServersForNetwork(netId, newLp);
+            // TODO: netd should listen on [::1]:53 and proxy queries to the current
+            // default network, and we should just set net.dns1 to ::1, not least
+            // because applications attempting to use net.dns resolvers will bypass
+            // the privacy protections of things like DNS-over-TLS.
+            if (isDefaultNetwork) mDnsManager.setDefaultDnsSystemProperties(newLp.getDnsServers());
+            mDnsManager.flushVmDnsCache();
         } catch (Exception e) {
             loge("Exception in setDnsConfigurationForNetwork: " + e);
         }
@@ -6234,6 +6356,10 @@
             // bubble those changes through.
             updateAllVpnsCapabilities();
         }
+
+        if (!newNc.equalsTransportTypes(prevNc)) {
+            mDnsManager.updateTransportsForNetwork(nai.network.netId, newNc.getTransportTypes());
+        }
     }
 
     /**
@@ -6261,7 +6387,8 @@
                 && !nai.networkAgentConfig.allowBypass
                 && nc.getOwnerUid() != Process.SYSTEM_UID
                 && lp.getInterfaceName() != null
-                && (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute());
+                && (lp.hasIPv4DefaultRoute() || lp.hasIpv4UnreachableDefaultRoute())
+                && (lp.hasIPv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute());
     }
 
     private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
@@ -6325,13 +6452,13 @@
             // Ignore updates for disconnected networks
             return;
         }
-        // newLp is already a defensive copy.
-        newLp.ensureDirectlyConnectedRoutes();
         if (VDBG || DDBG) {
             log("Update of LinkProperties for " + nai.toShortString()
                     + "; created=" + nai.created
                     + "; everConnected=" + nai.everConnected);
         }
+        // TODO: eliminate this defensive copy after confirming that updateLinkProperties does not
+        // modify its oldLp parameter.
         updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
     }
 
@@ -7005,6 +7132,14 @@
             networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
 
             if (!createNativeNetwork(networkAgent)) return;
+            if (networkAgent.isVPN()) {
+                // Initialize the VPN capabilities to their starting values according to the
+                // underlying networks. This will avoid a spurious callback to
+                // onCapabilitiesUpdated being sent in updateAllVpnCapabilities below as
+                // the VPN would switch from its default, blank capabilities to those
+                // that reflect the capabilities of its underlying networks.
+                updateAllVpnsCapabilities();
+            }
             networkAgent.created = true;
         }
 
@@ -7031,7 +7166,9 @@
                 networkAgent.networkMonitor().setAcceptPartialConnectivity();
             }
             networkAgent.networkMonitor().notifyNetworkConnected(
-                    networkAgent.linkProperties, networkAgent.networkCapabilities);
+                    new LinkProperties(networkAgent.linkProperties,
+                            true /* parcelSensitiveFields */),
+                    networkAgent.networkCapabilities);
             scheduleUnvalidatedPrompt(networkAgent);
 
             // Whether a particular NetworkRequest listen should cause signal strength thresholds to
@@ -7783,11 +7920,12 @@
         ensureRunningOnConnectivityServiceThread();
 
         final IConnectivityDiagnosticsCallback cb = cbInfo.mCb;
+        final IBinder iCb = cb.asBinder();
         final NetworkRequestInfo nri = cbInfo.mRequestInfo;
 
         // This means that the client registered the same callback multiple times. Do
         // not override the previous entry, and exit silently.
-        if (mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
+        if (mConnectivityDiagnosticsCallbacks.containsKey(iCb)) {
             if (VDBG) log("Diagnostics callback is already registered");
 
             // Decrement the reference count for this NetworkRequestInfo. The reference count is
@@ -7797,40 +7935,75 @@
             return;
         }
 
-        mConnectivityDiagnosticsCallbacks.put(cb, cbInfo);
+        mConnectivityDiagnosticsCallbacks.put(iCb, cbInfo);
 
         try {
-            cb.asBinder().linkToDeath(cbInfo, 0);
+            iCb.linkToDeath(cbInfo, 0);
         } catch (RemoteException e) {
             cbInfo.binderDied();
+            return;
+        }
+
+        // Once registered, provide ConnectivityReports for matching Networks
+        final List<NetworkAgentInfo> matchingNetworks = new ArrayList<>();
+        synchronized (mNetworkForNetId) {
+            for (int i = 0; i < mNetworkForNetId.size(); i++) {
+                final NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
+                if (nai.satisfies(nri.request)) {
+                    matchingNetworks.add(nai);
+                }
+            }
+        }
+        for (final NetworkAgentInfo nai : matchingNetworks) {
+            final ConnectivityReport report = nai.getConnectivityReport();
+            if (report == null) {
+                continue;
+            }
+            if (!checkConnectivityDiagnosticsPermissions(
+                    nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
+                continue;
+            }
+
+            try {
+                cb.onConnectivityReportAvailable(report);
+            } catch (RemoteException e) {
+                // Exception while sending the ConnectivityReport. Move on to the next network.
+            }
         }
     }
 
     private void handleUnregisterConnectivityDiagnosticsCallback(
             @NonNull IConnectivityDiagnosticsCallback cb, int uid) {
         ensureRunningOnConnectivityServiceThread();
+        final IBinder iCb = cb.asBinder();
 
-        if (!mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
+        final ConnectivityDiagnosticsCallbackInfo cbInfo =
+                mConnectivityDiagnosticsCallbacks.remove(iCb);
+        if (cbInfo == null) {
             if (VDBG) log("Removing diagnostics callback that is not currently registered");
             return;
         }
 
-        final NetworkRequestInfo nri = mConnectivityDiagnosticsCallbacks.get(cb).mRequestInfo;
+        final NetworkRequestInfo nri = cbInfo.mRequestInfo;
 
         if (uid != nri.mUid) {
             if (VDBG) loge("Different uid than registrant attempting to unregister cb");
             return;
         }
 
-        cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
+        // Decrement the reference count for this NetworkRequestInfo. The reference count is
+        // incremented when the NetworkRequestInfo is created as part of
+        // enforceRequestCountLimit().
+        decrementNetworkRequestPerUidCount(nri);
+
+        iCb.unlinkToDeath(cbInfo, 0);
     }
 
     private void handleNetworkTestedWithExtras(
             @NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
         final NetworkAgentInfo nai = reportEvent.mNai;
         final NetworkCapabilities networkCapabilities =
-                new NetworkCapabilities(nai.networkCapabilities);
-        clearNetworkCapabilitiesUids(networkCapabilities);
+                getNetworkCapabilitiesWithoutUids(nai.networkCapabilities);
         final ConnectivityReport report =
                 new ConnectivityReport(
                         reportEvent.mNai.network,
@@ -7838,11 +8011,12 @@
                         nai.linkProperties,
                         networkCapabilities,
                         extras);
+        nai.setConnectivityReport(report);
         final List<IConnectivityDiagnosticsCallback> results =
                 getMatchingPermissionedCallbacks(nai);
         for (final IConnectivityDiagnosticsCallback cb : results) {
             try {
-                cb.onConnectivityReport(report);
+                cb.onConnectivityReportAvailable(report);
             } catch (RemoteException ex) {
                 loge("Error invoking onConnectivityReport", ex);
             }
@@ -7853,8 +8027,7 @@
             @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
             @NonNull PersistableBundle extras) {
         final NetworkCapabilities networkCapabilities =
-                new NetworkCapabilities(nai.networkCapabilities);
-        clearNetworkCapabilitiesUids(networkCapabilities);
+                getNetworkCapabilitiesWithoutUids(nai.networkCapabilities);
         final DataStallReport report =
                 new DataStallReport(
                         nai.network,
@@ -7887,23 +8060,25 @@
         }
     }
 
-    private void clearNetworkCapabilitiesUids(@NonNull NetworkCapabilities nc) {
-        nc.setUids(null);
-        nc.setAdministratorUids(Collections.EMPTY_LIST);
-        nc.setOwnerUid(Process.INVALID_UID);
+    private NetworkCapabilities getNetworkCapabilitiesWithoutUids(@NonNull NetworkCapabilities nc) {
+        final NetworkCapabilities sanitized = new NetworkCapabilities(nc);
+        sanitized.setUids(null);
+        sanitized.setAdministratorUids(new int[0]);
+        sanitized.setOwnerUid(Process.INVALID_UID);
+        return sanitized;
     }
 
     private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
             @NonNull NetworkAgentInfo nai) {
         final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
-        for (Entry<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo> entry :
+        for (Entry<IBinder, ConnectivityDiagnosticsCallbackInfo> entry :
                 mConnectivityDiagnosticsCallbacks.entrySet()) {
             final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
             final NetworkRequestInfo nri = cbInfo.mRequestInfo;
             if (nai.satisfies(nri.request)) {
                 if (checkConnectivityDiagnosticsPermissions(
                         nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
-                    results.add(entry.getKey());
+                    results.add(entry.getValue().mCb);
                 }
             }
         }
@@ -7917,23 +8092,30 @@
             return true;
         }
 
-        if (!mLocationPermissionChecker.checkLocationPermission(
-                callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+        // LocationPermissionChecker#checkLocationPermission can throw SecurityException if the uid
+        // and package name don't match. Throwing on the CS thread is not acceptable, so wrap the
+        // call in a try-catch.
+        try {
+            if (!mLocationPermissionChecker.checkLocationPermission(
+                    callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+                return false;
+            }
+        } catch (SecurityException e) {
             return false;
         }
 
+        final Network[] underlyingNetworks;
         synchronized (mVpns) {
-            if (getVpnIfOwner(callbackUid) != null) {
-                return true;
-            }
+            final Vpn vpn = getVpnIfOwner(callbackUid);
+            underlyingNetworks = (vpn == null) ? null : vpn.getUnderlyingNetworks();
+        }
+        if (underlyingNetworks != null) {
+            if (Arrays.asList(underlyingNetworks).contains(nai.network)) return true;
         }
 
         // Administrator UIDs also contains the Owner UID
-        if (nai.networkCapabilities.getAdministratorUids().contains(callbackUid)) {
-            return true;
-        }
-
-        return false;
+        final int[] administratorUids = nai.networkCapabilities.getAdministratorUids();
+        return ArrayUtils.contains(administratorUids, callbackUid);
     }
 
     @Override
@@ -7984,4 +8166,36 @@
                         0,
                         callback));
     }
+
+    @Override
+    public void simulateDataStall(int detectionMethod, long timestampMillis,
+            @NonNull Network network, @NonNull PersistableBundle extras) {
+        enforceAnyPermissionOf(android.Manifest.permission.MANAGE_TEST_NETWORKS,
+                android.Manifest.permission.NETWORK_STACK);
+        final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
+        if (!nc.hasTransport(TRANSPORT_TEST)) {
+            throw new SecurityException("Data Stall simluation is only possible for test networks");
+        }
+
+        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai == null || nai.creatorUid != Binder.getCallingUid()) {
+            throw new SecurityException("Data Stall simulation is only possible for network "
+                + "creators");
+        }
+
+        final DataStallReportParcelable p = new DataStallReportParcelable();
+        p.timestampMillis = timestampMillis;
+        p.detectionMethod = detectionMethod;
+
+        if (hasDataStallDetectionMethod(p, DETECTION_METHOD_DNS_EVENTS)) {
+            p.dnsConsecutiveTimeouts = extras.getInt(KEY_DNS_CONSECUTIVE_TIMEOUTS);
+        }
+        if (hasDataStallDetectionMethod(p, DETECTION_METHOD_TCP_METRICS)) {
+            p.tcpPacketFailRate = extras.getInt(KEY_TCP_PACKET_FAIL_RATE);
+            p.tcpMetricsCollectionPeriodMillis = extras.getInt(
+                    KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS);
+        }
+
+        notifyDataStallSuspected(p, network.netId);
+    }
 }
diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/services/core/java/com/android/server/ExplicitHealthCheckController.java
index f7c4aac..77059d9 100644
--- a/services/core/java/com/android/server/ExplicitHealthCheckController.java
+++ b/services/core/java/com/android/server/ExplicitHealthCheckController.java
@@ -47,6 +47,7 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.Consumer;
 
@@ -113,9 +114,9 @@
                 Slog.wtf(TAG, "Resetting health check controller callbacks");
             }
 
-            mPassedConsumer = Preconditions.checkNotNull(passedConsumer);
-            mSupportedConsumer = Preconditions.checkNotNull(supportedConsumer);
-            mNotifySyncRunnable = Preconditions.checkNotNull(notifySyncRunnable);
+            mPassedConsumer = Objects.requireNonNull(passedConsumer);
+            mSupportedConsumer = Objects.requireNonNull(supportedConsumer);
+            mNotifySyncRunnable = Objects.requireNonNull(notifySyncRunnable);
         }
     }
 
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 98ac4cb..6402e07 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -25,8 +25,6 @@
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_DGRAM;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -48,6 +46,7 @@
 import android.net.util.NetdService;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.INetworkManagementService;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -76,6 +75,7 @@
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A service to manage multiple clients that want to access the IpSec API. The service is
@@ -115,6 +115,9 @@
     /* Binder context for this service */
     private final Context mContext;
 
+    /* NetworkManager instance */
+    private final INetworkManagementService mNetworkManager;
+
     /**
      * The next non-repeating global ID for tracking resources between users, this service, and
      * kernel data structures. Accessing this variable is not thread safe, so it is only read or
@@ -360,10 +363,14 @@
     @VisibleForTesting
     static final class UserRecord {
         /* Maximum number of each type of resource that a single UID may possess */
-        public static final int MAX_NUM_TUNNEL_INTERFACES = 2;
-        public static final int MAX_NUM_ENCAP_SOCKETS = 2;
-        public static final int MAX_NUM_TRANSFORMS = 4;
-        public static final int MAX_NUM_SPIS = 8;
+
+        // Up to 4 active VPNs/IWLAN with potential soft handover.
+        public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
+        public static final int MAX_NUM_ENCAP_SOCKETS = 16;
+
+        // SPIs and Transforms are both cheap, and are 1:1 correlated.
+        public static final int MAX_NUM_TRANSFORMS = 64;
+        public static final int MAX_NUM_SPIS = 64;
 
         /**
          * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
@@ -566,7 +573,7 @@
         }
 
         void put(int key, RefcountedResource<T> obj) {
-            checkNotNull(obj, "Null resources cannot be added");
+            Objects.requireNonNull(obj, "Null resources cannot be added");
             mArray.put(key, obj);
         }
 
@@ -989,12 +996,13 @@
      *
      * @param context Binder context for this service
      */
-    private IpSecService(Context context) {
-        this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
+    private IpSecService(Context context, INetworkManagementService networkManager) {
+        this(context, networkManager, IpSecServiceConfiguration.GETSRVINSTANCE);
     }
 
-    static IpSecService create(Context context) throws InterruptedException {
-        final IpSecService service = new IpSecService(context);
+    static IpSecService create(Context context, INetworkManagementService networkManager)
+            throws InterruptedException {
+        final IpSecService service = new IpSecService(context, networkManager);
         service.connectNativeNetdService();
         return service;
     }
@@ -1008,9 +1016,11 @@
 
     /** @hide */
     @VisibleForTesting
-    public IpSecService(Context context, IpSecServiceConfiguration config) {
+    public IpSecService(Context context, INetworkManagementService networkManager,
+            IpSecServiceConfiguration config) {
         this(
                 context,
+                networkManager,
                 config,
                 (fd, uid) -> {
                     try {
@@ -1024,9 +1034,10 @@
 
     /** @hide */
     @VisibleForTesting
-    public IpSecService(
-            Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
+    public IpSecService(Context context, INetworkManagementService networkManager,
+            IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
         mContext = context;
+        mNetworkManager = Objects.requireNonNull(networkManager);
         mSrvConfig = config;
         mUidFdTagger = uidFdTagger;
     }
@@ -1101,7 +1112,7 @@
         if (requestedSpi > 0 && requestedSpi < 256) {
             throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
         }
-        checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
+        Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
 
         int callingUid = Binder.getCallingUid();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
@@ -1218,7 +1229,7 @@
             throw new IllegalArgumentException(
                     "Specified port number must be a valid non-reserved UDP port");
         }
-        checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
+        Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
 
         int callingUid = Binder.getCallingUid();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
@@ -1278,8 +1289,8 @@
             String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
             String callingPackage) {
         enforceTunnelFeatureAndPermissions(callingPackage);
-        checkNotNull(binder, "Null Binder passed to createTunnelInterface");
-        checkNotNull(underlyingNetwork, "No underlying network was specified");
+        Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
+        Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
         checkInetAddress(localAddr);
         checkInetAddress(remoteAddr);
 
@@ -1305,6 +1316,10 @@
             final INetd netd = mSrvConfig.getNetdInstance();
             netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
 
+            Binder.withCleanCallingIdentity(() -> {
+                mNetworkManager.setInterfaceUp(intfName);
+            });
+
             for (int selAddrFamily : ADDRESS_FAMILIES) {
                 // Always send down correct local/remote addresses for template.
                 netd.ipSecAddSecurityPolicy(
@@ -1556,7 +1571,7 @@
                     "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
         }
 
-        checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
+        Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
 
         // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
         // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
@@ -1625,12 +1640,12 @@
     @Override
     public synchronized IpSecTransformResponse createTransform(
             IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
-        checkNotNull(c);
+        Objects.requireNonNull(c);
         if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
             enforceTunnelFeatureAndPermissions(callingPackage);
         }
         checkIpSecConfig(c);
-        checkNotNull(binder, "Null Binder passed to createTransform");
+        Objects.requireNonNull(binder, "Null Binder passed to createTransform");
         final int resourceId = mNextResourceId++;
 
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
@@ -1761,7 +1776,7 @@
             socketRecord =
                     userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId());
         }
-        SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId());
+        SpiRecord spiRecord = transformInfo.getSpiRecord();
 
         int mark =
                 (direction == IpSecManager.DIRECTION_OUT)
@@ -1794,7 +1809,7 @@
 
                 // Set outbound SPI only. We want inbound to use any valid SA (old, new) on rekeys,
                 // but want to guarantee outbound packets are sent over the new SA.
-                spi = transformInfo.getSpiRecord().getSpi();
+                spi = spiRecord.getSpi();
             }
 
             // Always update the policy with the relevant XFRM_IF_ID
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 90e4670..f4b769f 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -25,7 +25,6 @@
 import static android.os.PowerManager.locationPowerSaveModeToString;
 import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
 
 import android.Manifest;
@@ -133,6 +132,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.NoSuchElementException;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -986,7 +986,7 @@
 
         @GuardedBy("mLock")
         public void attachLocked(AbstractLocationProvider provider) {
-            checkNotNull(provider);
+            Objects.requireNonNull(provider);
             checkState(mProvider == null);
 
             if (D) {
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index ad02aad..eac767f 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -46,6 +46,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.LinkedList;
+import java.util.Objects;
 
 /**
  * Generic connector class for interfacing with a native daemon which uses the
@@ -126,7 +127,7 @@
      */
     public void setWarnIfHeld(Object warnIfHeld) {
         Preconditions.checkState(mWarnIfHeld == null);
-        mWarnIfHeld = Preconditions.checkNotNull(warnIfHeld);
+        mWarnIfHeld = Objects.requireNonNull(warnIfHeld);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7d6ae21..1bb3c3a 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -111,6 +111,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * @hide
@@ -458,7 +459,7 @@
     @Override
     public void registerTetheringStatsProvider(ITetheringStatsProvider provider, String name) {
         NetworkStack.checkNetworkStackPermission(mContext);
-        Preconditions.checkNotNull(provider);
+        Objects.requireNonNull(provider);
         synchronized(mTetheringStatsProviders) {
             mTetheringStatsProviders.put(provider, name);
         }
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 76e0c13..8099f8f 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
 # Connectivity / Networking
-per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java = codewiz@google.com, ek@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
+per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java = codewiz@google.com, ek@google.com, jchalard@google.com, junyulai@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
 
 # Vibrator / Threads
 per-file VibratorService.java, DisplayThread.java = michaelwr@google.com
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 39ad354..ddbc79a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1934,7 +1934,7 @@
     public void setVolumeNickname(String fsUuid, String nickname) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
-        Preconditions.checkNotNull(fsUuid);
+        Objects.requireNonNull(fsUuid);
         synchronized (mLock) {
             final VolumeRecord rec = mRecords.get(fsUuid);
             rec.nickname = nickname;
@@ -1947,7 +1947,7 @@
     public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
-        Preconditions.checkNotNull(fsUuid);
+        Objects.requireNonNull(fsUuid);
         synchronized (mLock) {
             final VolumeRecord rec = mRecords.get(fsUuid);
             rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
@@ -1960,7 +1960,7 @@
     public void forgetVolume(String fsUuid) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
-        Preconditions.checkNotNull(fsUuid);
+        Objects.requireNonNull(fsUuid);
 
         synchronized (mLock) {
             final VolumeRecord rec = mRecords.remove(fsUuid);
@@ -2361,7 +2361,7 @@
 
     @Override
     public String getMountedObbPath(String rawPath) {
-        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+        Objects.requireNonNull(rawPath, "rawPath cannot be null");
 
         warnOnNotMounted();
 
@@ -2379,7 +2379,7 @@
 
     @Override
     public boolean isObbMounted(String rawPath) {
-        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+        Objects.requireNonNull(rawPath, "rawPath cannot be null");
         synchronized (mObbMounts) {
             return mObbPathToStateMap.containsKey(rawPath);
         }
@@ -2388,10 +2388,10 @@
     @Override
     public void mountObb(String rawPath, String canonicalPath, String key,
             IObbActionListener token, int nonce, ObbInfo obbInfo) {
-        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
-        Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
-        Preconditions.checkNotNull(token, "token cannot be null");
-        Preconditions.checkNotNull(obbInfo, "obbIfno cannot be null");
+        Objects.requireNonNull(rawPath, "rawPath cannot be null");
+        Objects.requireNonNull(canonicalPath, "canonicalPath cannot be null");
+        Objects.requireNonNull(token, "token cannot be null");
+        Objects.requireNonNull(obbInfo, "obbIfno cannot be null");
 
         final int callingUid = Binder.getCallingUid();
         final ObbState obbState = new ObbState(rawPath, canonicalPath,
@@ -2405,7 +2405,7 @@
 
     @Override
     public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
-        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+        Objects.requireNonNull(rawPath, "rawPath cannot be null");
 
         final ObbState existingState;
         synchronized (mObbMounts) {
@@ -2648,13 +2648,6 @@
      */
     @Override
     public boolean supportsCheckpoint() throws RemoteException {
-        // Only the root, system_server and shell processes are permitted to start checkpoints
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID
-                && callingUid != Process.SHELL_UID) {
-            throw new SecurityException("no permission to start filesystem checkpoint");
-        }
-
         return mVold.supportsCheckpoint();
     }
 
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index ff6a537..ba82938 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -26,6 +26,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f45d54d..d012bd7 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -27,6 +27,7 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
+import android.app.compat.CompatChanges;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -39,12 +40,15 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.telephony.Annotation;
 import android.telephony.Annotation.DataFailureCause;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SrvccState;
+import android.telephony.BarringInfo;
 import android.telephony.CallAttributes;
 import android.telephony.CallQuality;
 import android.telephony.CellIdentity;
@@ -59,7 +63,6 @@
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.DataFailCause;
 import android.telephony.DisconnectCause;
-import android.telephony.DisplayInfo;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneCapability;
 import android.telephony.PhoneStateListener;
@@ -71,6 +74,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 import android.telephony.emergency.EmergencyNumber;
@@ -167,19 +171,48 @@
 
         @Override
         public String toString() {
-            return "{callingPackage=" + callingPackage + " binder=" + binder
-                    + " callback=" + callback
+            return "{callingPackage=" + pii(callingPackage) + " callerUid=" + callerUid + " binder="
+                    + binder + " callback=" + callback
                     + " onSubscriptionsChangedListenererCallback="
                     + onSubscriptionsChangedListenerCallback
                     + " onOpportunisticSubscriptionsChangedListenererCallback="
-                    + onOpportunisticSubscriptionsChangedListenerCallback
-                    + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
-                    + " events=" + Integer.toHexString(events) + "}";
+                    + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId
+                    + " phoneId=" + phoneId + " events=" + Integer.toHexString(events) + "}";
+        }
+    }
+
+    /**
+     * Wrapper class to facilitate testing -- encapsulates bits of configuration that are
+     * normally fetched from static methods with many dependencies.
+     */
+    public static class ConfigurationProvider {
+        /**
+         * @return The per-pid registration limit for PhoneStateListeners, as set from DeviceConfig
+         * @noinspection ConstantConditions
+         */
+        public int getRegistrationLimit() {
+            return Binder.withCleanCallingIdentity(() ->
+                    DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
+                            PhoneStateListener.FLAG_PER_PID_REGISTRATION_LIMIT,
+                            PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT));
+        }
+
+        /**
+         * @param uid uid to check
+         * @return Whether enforcement of the per-pid registation limit for PhoneStateListeners is
+         *         enabled in PlatformCompat for the given uid.
+         * @noinspection ConstantConditions
+         */
+        public boolean isRegistrationLimitEnabledInPlatformCompat(int uid) {
+            return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+                    PhoneStateListener.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid));
         }
     }
 
     private final Context mContext;
 
+    private ConfigurationProvider mConfigurationProvider;
+
     // access should be inside synchronized (mRecords) for these two fields
     private final ArrayList<IBinder> mRemoveList = new ArrayList<IBinder>();
     private final ArrayList<Record> mRecords = new ArrayList<Record>();
@@ -206,7 +239,7 @@
 
     private boolean[] mUserMobileDataState;
 
-    private DisplayInfo[] mDisplayInfos;
+    private TelephonyDisplayInfo[] mTelephonyDisplayInfos;
 
     private SignalStrength[] mSignalStrength;
 
@@ -258,6 +291,8 @@
 
     private int[] mCallPreciseDisconnectCause;
 
+    private List<BarringInfo> mBarringInfo = null;
+
     private boolean mCarrierNetworkChangeState = false;
 
     private PhoneCapability mPhoneCapability = null;
@@ -444,13 +479,14 @@
         mCallAttributes = copyOf(mCallAttributes, mNumPhones);
         mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones);
         mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones);
-        mDisplayInfos = copyOf(mDisplayInfos, mNumPhones);
+        mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
 
         // ds -> ss switch.
         if (mNumPhones < oldNumPhones) {
             cutListToSize(mCellInfo, mNumPhones);
             cutListToSize(mImsReasonInfo, mNumPhones);
             cutListToSize(mPreciseDataConnectionStates, mNumPhones);
+            cutListToSize(mBarringInfo, mNumPhones);
             return;
         }
 
@@ -482,7 +518,8 @@
             mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>());
-            mDisplayInfos[i] = null;
+            mBarringInfo.add(i, new BarringInfo());
+            mTelephonyDisplayInfos[i] = null;
         }
     }
 
@@ -502,10 +539,11 @@
     // handler before they get to app code.
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public TelephonyRegistry(Context context) {
+    public TelephonyRegistry(Context context, ConfigurationProvider configurationProvider) {
         CellLocation  location = CellLocation.getEmpty();
 
         mContext = context;
+        mConfigurationProvider = configurationProvider;
         mBatteryStats = BatteryStatsService.getService();
 
         int numPhones = getTelephonyManager().getActiveModemCount();
@@ -540,7 +578,8 @@
         mEmergencyNumberList = new HashMap<>();
         mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
         mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
-        mDisplayInfos = new DisplayInfo[numPhones];
+        mBarringInfo = new ArrayList<>();
+        mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -568,7 +607,8 @@
             mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>());
-            mDisplayInfos[i] = null;
+            mBarringInfo.add(i, new BarringInfo());
+            mTelephonyDisplayInfos[i] = null;
         }
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -591,15 +631,15 @@
         int callerUserId = UserHandle.getCallingUserId();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         if (VDBG) {
-            log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
-                + " callerUserId="  + callerUserId + " callback=" + callback
-                + " callback.asBinder=" + callback.asBinder());
+            log("listen oscl: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
+                    + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId
+                    + " callback=" + callback + " callback.asBinder=" + callback.asBinder());
         }
 
         synchronized (mRecords) {
             // register
             IBinder b = callback.asBinder();
-            Record r = add(b);
+            Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), false);
 
             if (r == null) {
                 return;
@@ -645,15 +685,15 @@
         int callerUserId = UserHandle.getCallingUserId();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         if (VDBG) {
-            log("listen ooscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
-                    + " callerUserId="  + callerUserId + " callback=" + callback
-                    + " callback.asBinder=" + callback.asBinder());
+            log("listen ooscl: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
+                    + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId
+                    + " callback=" + callback + " callback.asBinder=" + callback.asBinder());
         }
 
         synchronized (mRecords) {
             // register
             IBinder b = callback.asBinder();
-            Record r = add(b);
+            Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), false);
 
             if (r == null) {
                 return;
@@ -762,9 +802,9 @@
             IPhoneStateListener callback, int events, boolean notifyNow, int subId) {
         int callerUserId = UserHandle.getCallingUserId();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
-        String str = "listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
-                + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
-                + UserHandle.myUserId() + " callerUserId=" + callerUserId;
+        String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
+                + " events=0x" + Integer.toHexString(events) + " notifyNow=" + notifyNow + " subId="
+                + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId;
         mListenLog.log(str);
         if (VDBG) {
             log(str);
@@ -783,7 +823,11 @@
             synchronized (mRecords) {
                 // register
                 IBinder b = callback.asBinder();
-                Record r = add(b);
+                boolean doesLimitApply =
+                        Binder.getCallingUid() != Process.SYSTEM_UID
+                        && Binder.getCallingUid() != Process.PHONE_UID
+                        && Binder.getCallingUid() != Process.myUid();
+                Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply);
 
                 if (r == null) {
                     return;
@@ -981,8 +1025,8 @@
                     }
                     if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
                         try {
-                            if (mDisplayInfos[phoneId] != null) {
-                                r.callback.onDisplayInfoChanged(mDisplayInfos[phoneId]);
+                            if (mTelephonyDisplayInfos[phoneId] != null) {
+                                r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
                             }
                         } catch (RemoteException ex) {
                             remove(r.binder);
@@ -1031,6 +1075,19 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_BARRING_INFO) != 0) {
+                        BarringInfo barringInfo = mBarringInfo.get(phoneId);
+                        BarringInfo biNoLocation = barringInfo != null
+                                ? barringInfo.createLocationInfoSanitizedCopy() : null;
+                        if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
+                        try {
+                            r.callback.onBarringInfoChanged(
+                                    checkFineLocationAccess(r, Build.VERSION_CODES.R)
+                                            ? barringInfo : biNoLocation);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                 }
             }
         } else {
@@ -1065,18 +1122,44 @@
         return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : "";
     }
 
-    private Record add(IBinder binder) {
+    private Record add(IBinder binder, int callingUid, int callingPid, boolean doesLimitApply) {
         Record r;
 
         synchronized (mRecords) {
             final int N = mRecords.size();
+            // While iterating through the records, keep track of how many we have from this pid.
+            int numRecordsForPid = 0;
             for (int i = 0; i < N; i++) {
                 r = mRecords.get(i);
                 if (binder == r.binder) {
                     // Already existed.
                     return r;
                 }
+                if (r.callerPid == callingPid) {
+                    numRecordsForPid++;
+                }
             }
+            // If we've exceeded the limit for registrations, log an error and quit.
+            int registrationLimit = mConfigurationProvider.getRegistrationLimit();
+
+            if (doesLimitApply
+                    && registrationLimit >= 1
+                    && numRecordsForPid >= registrationLimit) {
+                String errorMsg = "Pid " + callingPid + " has exceeded the number of permissible"
+                        + "registered listeners. Ignoring request to add.";
+                loge(errorMsg);
+                if (mConfigurationProvider
+                        .isRegistrationLimitEnabledInPlatformCompat(callingUid)) {
+                    throw new IllegalStateException(errorMsg);
+                }
+            } else if (doesLimitApply && numRecordsForPid
+                    >= PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT / 2) {
+                // Log the warning independently of the dynamically set limit -- apps shouldn't be
+                // doing this regardless of whether we're throwing them an exception for it.
+                Rlog.w(TAG, "Pid " + callingPid + " has exceeded half the number of permissible"
+                        + "registered listeners. Now at " + numRecordsForPid);
+            }
+
             r = new Record();
             r.binder = binder;
             r.deathRecipient = new TelephonyRegistryDeathRecipient(binder);
@@ -1503,32 +1586,30 @@
      *
      * @param phoneId Phone id
      * @param subId Subscription id
-     * @param displayInfo Display network info
+     * @param telephonyDisplayInfo Display network info
      *
-     * @see PhoneStateListener#onDisplayInfoChanged(DisplayInfo)
+     * @see PhoneStateListener#onDisplayInfoChanged(TelephonyDisplayInfo)
      */
     public void notifyDisplayInfoChanged(int phoneId, int subId,
-                                         @NonNull DisplayInfo displayInfo) {
+                                         @NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
         if (!checkNotifyPermission("notifyDisplayInfoChanged()")) {
             return;
         }
         if (VDBG) {
             log("notifyDisplayInfoChanged: PhoneId=" + phoneId
-                    + " subId=" + subId + " displayInfo=" + displayInfo);
+                    + " subId=" + subId + " telephonyDisplayInfo=" + telephonyDisplayInfo);
         }
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
-                if (mDisplayInfos[phoneId] != null) {
-                    mDisplayInfos[phoneId] = displayInfo;
-                    for (Record r : mRecords) {
-                        if (r.matchPhoneStateListenerEvent(
-                                PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
-                                && idMatch(r.subId, subId, phoneId)) {
-                            try {
-                                r.callback.onDisplayInfoChanged(displayInfo);
-                            } catch (RemoteException ex) {
-                                mRemoveList.add(r.binder);
-                            }
+                mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo;
+                for (Record r : mRecords) {
+                    if (r.matchPhoneStateListenerEvent(
+                            PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
+                            && idMatch(r.subId, subId, phoneId)) {
+                        try {
+                            r.callback.onDisplayInfoChanged(telephonyDisplayInfo);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
                         }
                     }
                 }
@@ -2207,6 +2288,52 @@
         }
     }
 
+    /**
+     * Send a notification of changes to barring status to PhoneStateListener registrants.
+     *
+     * @param phoneId the phoneId
+     * @param subId the subId
+     * @param barringInfo a structure containing the complete updated barring info.
+     */
+    public void notifyBarringInfoChanged(int phoneId, int subId, @NonNull BarringInfo barringInfo) {
+        if (!checkNotifyPermission("notifyBarringInfo()")) {
+            return;
+        }
+        if (barringInfo == null) {
+            log("Received null BarringInfo for subId=" + subId + ", phoneId=" + phoneId);
+            mBarringInfo.set(phoneId, new BarringInfo());
+            return;
+        }
+
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                mBarringInfo.set(phoneId, barringInfo);
+                // Barring info is non-null
+                BarringInfo biNoLocation = barringInfo.createLocationInfoSanitizedCopy();
+                if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
+                for (Record r : mRecords) {
+                    if (r.matchPhoneStateListenerEvent(
+                            PhoneStateListener.LISTEN_BARRING_INFO)
+                            && idMatch(r.subId, subId, phoneId)) {
+                        try {
+                            if (DBG_LOC) {
+                                log("notifyBarringInfo: mBarringInfo="
+                                        + barringInfo + " r=" + r);
+                            }
+                            r.callback.onBarringInfoChanged(
+                                    checkFineLocationAccess(r, Build.VERSION_CODES.R)
+                                        ? barringInfo : biNoLocation);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
+                        }
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -2247,6 +2374,7 @@
                 pw.println("mPreciseDataConnectionStates=" + mPreciseDataConnectionStates.get(i));
                 pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
                 pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
+                pw.println("mBarringInfo=" + mBarringInfo.get(i));
                 pw.decreaseIndent();
             }
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
@@ -2701,10 +2829,10 @@
             try {
                 if (VDBG) {
                     log("checkPossibleMissNotify: onDisplayInfoChanged phoneId="
-                            + phoneId + " dpi=" + mDisplayInfos[phoneId]);
+                            + phoneId + " dpi=" + mTelephonyDisplayInfos[phoneId]);
                 }
-                if (mDisplayInfos[phoneId] != null) {
-                    r.callback.onDisplayInfoChanged(mDisplayInfos[phoneId]);
+                if (mTelephonyDisplayInfos[phoneId] != null) {
+                    r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
                 }
             } catch (RemoteException ex) {
                 mRemoveList.add(r.binder);
@@ -2870,4 +2998,14 @@
         if (info == null) return INVALID_SIM_SLOT_INDEX;
         return info.getSimSlotIndex();
     }
+
+    /**
+     * On certain build types, we should redact information by default. UID information will be
+     * preserved in the same log line, so no debugging capability is lost in full bug reports.
+     * However, privacy-constrained bug report types (e.g. connectivity) cannot display raw
+     * package names on user builds as it's considered an information leak.
+     */
+    private static String pii(String packageName) {
+        return Build.IS_DEBUGGABLE ? packageName : "***";
+    }
 }
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 95ac900..d6bd5a1 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -16,7 +16,8 @@
 
 package com.android.server;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
+import static android.net.TestNetworkManager.TEST_TUN_PREFIX;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -55,14 +56,13 @@
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /** @hide */
 class TestNetworkService extends ITestNetworkManager.Stub {
     @NonNull private static final String TAG = TestNetworkService.class.getSimpleName();
     @NonNull private static final String TEST_NETWORK_TYPE = "TEST_NETWORK";
-    @NonNull private static final String TEST_TUN_PREFIX = "testtun";
-    @NonNull private static final String TEST_TAP_PREFIX = "testtap";
     @NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger();
 
     @NonNull private final Context mContext;
@@ -82,9 +82,9 @@
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
 
-        mContext = checkNotNull(context, "missing Context");
-        mNMS = checkNotNull(netManager, "missing INetworkManagementService");
-        mNetd = checkNotNull(NetdService.getInstance(), "could not get netd instance");
+        mContext = Objects.requireNonNull(context, "missing Context");
+        mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
+        mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance");
     }
 
     /**
@@ -96,7 +96,7 @@
     private TestNetworkInterface createInterface(boolean isTun, LinkAddress[] linkAddrs) {
         enforceTestNetworkPermissions(mContext);
 
-        checkNotNull(linkAddrs, "missing linkAddrs");
+        Objects.requireNonNull(linkAddrs, "missing linkAddrs");
 
         String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
         String iface = ifacePrefix + sTestTunIndex.getAndIncrement();
@@ -231,10 +231,11 @@
             @Nullable LinkProperties lp,
             boolean isMetered,
             int callingUid,
+            @NonNull int[] administratorUids,
             @NonNull IBinder binder)
             throws RemoteException, SocketException {
-        checkNotNull(looper, "missing Looper");
-        checkNotNull(context, "missing Context");
+        Objects.requireNonNull(looper, "missing Looper");
+        Objects.requireNonNull(context, "missing Context");
         // iface and binder validity checked by caller
 
         // Build network info with special testing type
@@ -249,6 +250,7 @@
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
+        nc.setAdministratorUids(administratorUids);
         if (!isMetered) {
             nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         }
@@ -267,7 +269,7 @@
         // Find the currently assigned addresses, and add them to LinkProperties
         boolean allowIPv4 = false, allowIPv6 = false;
         NetworkInterface netIntf = NetworkInterface.getByName(iface);
-        checkNotNull(netIntf, "No such network interface found: " + netIntf);
+        Objects.requireNonNull(netIntf, "No such network interface found: " + netIntf);
 
         for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) {
             lp.addLinkAddress(
@@ -302,11 +304,12 @@
             @NonNull String iface,
             @Nullable LinkProperties lp,
             boolean isMetered,
+            @NonNull int[] administratorUids,
             @NonNull IBinder binder) {
         enforceTestNetworkPermissions(mContext);
 
-        checkNotNull(iface, "missing Iface");
-        checkNotNull(binder, "missing IBinder");
+        Objects.requireNonNull(iface, "missing Iface");
+        Objects.requireNonNull(binder, "missing IBinder");
 
         if (!(iface.startsWith(INetd.IPSEC_INTERFACE_PREFIX)
                 || iface.startsWith(TEST_TUN_PREFIX))) {
@@ -314,38 +317,34 @@
                     "Cannot create network for non ipsec, non-testtun interface");
         }
 
-        // Setup needs to be done with NETWORK_STACK privileges.
-        int callingUid = Binder.getCallingUid();
-        Binder.withCleanCallingIdentity(
-                () -> {
-                    try {
-                        mNMS.setInterfaceUp(iface);
+        try {
+            // This requires NETWORK_STACK privileges.
+            Binder.withCleanCallingIdentity(() -> mNMS.setInterfaceUp(iface));
 
-                        // Synchronize all accesses to mTestNetworkTracker to prevent the case
-                        // where:
-                        // 1. TestNetworkAgent successfully binds to death of binder
-                        // 2. Before it is added to the mTestNetworkTracker, binder dies,
-                        // binderDied() is called (on a different thread)
-                        // 3. This thread is pre-empted, put() is called after remove()
-                        synchronized (mTestNetworkTracker) {
-                            TestNetworkAgent agent =
-                                    registerTestNetworkAgent(
-                                            mHandler.getLooper(),
-                                            mContext,
-                                            iface,
-                                            lp,
-                                            isMetered,
-                                            callingUid,
-                                            binder);
+            // Synchronize all accesses to mTestNetworkTracker to prevent the case where:
+            // 1. TestNetworkAgent successfully binds to death of binder
+            // 2. Before it is added to the mTestNetworkTracker, binder dies, binderDied() is called
+            // (on a different thread)
+            // 3. This thread is pre-empted, put() is called after remove()
+            synchronized (mTestNetworkTracker) {
+                TestNetworkAgent agent =
+                        registerTestNetworkAgent(
+                                mHandler.getLooper(),
+                                mContext,
+                                iface,
+                                lp,
+                                isMetered,
+                                Binder.getCallingUid(),
+                                administratorUids,
+                                binder);
 
-                            mTestNetworkTracker.put(agent.getNetwork().netId, agent);
-                        }
-                    } catch (SocketException e) {
-                        throw new UncheckedIOException(e);
-                    } catch (RemoteException e) {
-                        throw e.rethrowFromSystemServer();
-                    }
-                });
+                mTestNetworkTracker.put(agent.getNetwork().netId, agent);
+            }
+        } catch (SocketException e) {
+            throw new UncheckedIOException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /** Teardown a test network */
diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java
index 74f113f..2403b63 100644
--- a/services/core/java/com/android/server/UserspaceRebootLogger.java
+++ b/services/core/java/com/android/server/UserspaceRebootLogger.java
@@ -24,8 +24,10 @@
 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.PowerManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.util.FrameworkStatsLog;
@@ -45,15 +47,22 @@
             "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 static final String LAST_BOOT_REASON_PROPERTY = "sys.boot.reason.last";
 
     private UserspaceRebootLogger() {}
 
     /**
      * Modifies internal state to note that {@code UserspaceRebootReported} atom needs to be
      * logged on the next successful boot.
+     *
+     * <p>This call should only be made on devices supporting userspace reboot.
      */
     public static void noteUserspaceRebootWasRequested() {
+        if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+            Slog.wtf(TAG, "Userspace reboot is not supported.");
+            return;
+        }
+
         SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "1");
         SystemProperties.set(USERSPACE_REBOOT_LAST_STARTED_PROPERTY,
                 String.valueOf(SystemClock.elapsedRealtime()));
@@ -63,16 +72,30 @@
      * Updates internal state on boot after successful userspace reboot.
      *
      * <p>Should be called right before framework sets {@code sys.boot_completed} property.
+     *
+     * <p>This call should only be made on devices supporting userspace reboot.
      */
     public static void noteUserspaceRebootSuccess() {
+        if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+            Slog.wtf(TAG, "Userspace reboot is not supported.");
+            return;
+        }
+
         SystemProperties.set(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY,
                 String.valueOf(SystemClock.elapsedRealtime()));
     }
 
     /**
      * Returns {@code true} if {@code UserspaceRebootReported} atom should be logged.
+     *
+     * <p>This call should only be made on devices supporting userspace reboot.
      */
     public static boolean shouldLogUserspaceRebootEvent() {
+        if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+            Slog.wtf(TAG, "Userspace reboot is not supported.");
+            return false;
+        }
+
         return SystemProperties.getBoolean(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, false);
     }
 
@@ -82,8 +105,15 @@
      * <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.
+     *
+     * <p>This call should only be made on devices supporting userspace reboot.
      */
     public static void logEventAsync(boolean userUnlocked, Executor executor) {
+        if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+            Slog.wtf(TAG, "Userspace reboot is not supported.");
+            return;
+        }
+
         final int outcome = computeOutcome();
         final long durationMillis;
         if (outcome == USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS) {
@@ -111,26 +141,28 @@
         if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) {
             return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
         }
-        String reason = SystemProperties.get(BOOT_REASON_PROPERTY, "");
+        String reason = TextUtils.emptyIfNull(SystemProperties.get(LAST_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;
+        if (reason.startsWith("userspace_failed,watchdog_fork")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
         }
+        if (reason.startsWith("userspace_failed,shutdown_aborted")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
+        }
+        if (reason.startsWith("mount_userdata_failed")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+        }
+        if (reason.startsWith("userspace_failed,init_user0")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+        }
+        if (reason.startsWith("userspace_failed,enablefilecrypto")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+        }
+        if (reason.startsWith("userspace_failed,watchdog_triggered")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
+        }
+        return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
     }
 }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 454941c..5f9d1d8 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -115,6 +115,7 @@
             "android.hardware.media.c2@1.0::IComponentStore",
             "android.hardware.media.omx@1.0::IOmx",
             "android.hardware.media.omx@1.0::IOmxStore",
+            "android.hardware.neuralnetworks@1.0::IDevice",
             "android.hardware.power.stats@1.0::IPowerStats",
             "android.hardware.sensors@1.0::ISensors",
             "android.hardware.vr@1.0::IVr",
diff --git a/services/core/java/com/android/server/accounts/TokenCache.java b/services/core/java/com/android/server/accounts/TokenCache.java
index 2af2f38..2aa9776 100644
--- a/services/core/java/com/android/server/accounts/TokenCache.java
+++ b/services/core/java/com/android/server/accounts/TokenCache.java
@@ -148,7 +148,7 @@
                 accountEvictor = new Evictor();
             }
             accountEvictor.add(k);
-            mAccountEvictors.put(k.account, tokenEvictor);
+            mAccountEvictors.put(k.account, accountEvictor);
 
             // Only cache the token once we can remove it directly or by account.
             put(k, v);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0ba4173..0ba6a55 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -842,6 +842,15 @@
         }
     }
 
+    void killMisbehavingService(ServiceRecord r,
+            int appUid, int appPid, String localPackageName) {
+        synchronized (mAm) {
+            stopServiceLocked(r);
+            mAm.crashApplication(appUid, appPid, localPackageName, -1,
+                    "Bad notification for startForeground", true /*force*/);
+        }
+    }
+
     IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
         ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, callingPackage,
                 Binder.getCallingPid(), Binder.getCallingUid(),
@@ -3946,7 +3955,7 @@
     void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
         mAm.crashApplication(app.uid, app.pid, app.info.packageName, app.userId,
                 "Context.startForegroundService() did not then call Service.startForeground(): "
-                    + serviceRecord);
+                    + serviceRecord, false /*force*/);
     }
 
     void scheduleServiceTimeoutLocked(ProcessRecord proc) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9678abb..166c4f3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -456,6 +456,8 @@
 
     static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
 
+    static final String SYSTEM_USER_HOME_NEEDED = "ro.system_user_home_needed";
+
     public static final String ANR_TRACE_DIR = "/data/anr";
 
     // Maximum number of receivers an app can register.
@@ -3595,7 +3597,7 @@
 
     @Override
     public void crashApplication(int uid, int initialPid, String packageName, int userId,
-            String message) {
+            String message, boolean force) {
         if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: crashApplication() from pid="
@@ -3607,7 +3609,8 @@
         }
 
         synchronized(this) {
-            mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId, message);
+            mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId,
+                    message, force);
         }
     }
 
@@ -4786,7 +4789,7 @@
     }
 
     @GuardedBy("this")
-    private final boolean attachApplicationLocked(IApplicationThread thread,
+    private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
             int pid, int callingUid, long startSeq) {
 
         // Find the application record that is being attached...  either via
@@ -5209,6 +5212,9 @@
 
     @Override
     public final void attachApplication(IApplicationThread thread, long startSeq) {
+        if (thread == null) {
+            throw new SecurityException("Invalid application interface");
+        }
         synchronized (this) {
             int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
@@ -9111,7 +9117,8 @@
             // to handle home activity in this case.
             if (UserManager.isSplitSystemUser() &&
                     Settings.Secure.getInt(mContext.getContentResolver(),
-                         Settings.Secure.USER_SETUP_COMPLETE, 0) != 0) {
+                         Settings.Secure.USER_SETUP_COMPLETE, 0) != 0
+                    || SystemProperties.getBoolean(SYSTEM_USER_HOME_NEEDED, false)) {
                 ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
                 try {
                     AppGlobals.getPackageManager().setComponentEnabledSetting(cName,
@@ -12850,7 +12857,9 @@
                         pw.print(" unmapped + ");
                         pw.print(stringifyKBSize(ionPool));
                         pw.println(" pools)");
-                kernelUsed += ionUnmapped;
+                // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
+                // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
+                kernelUsed += ionHeap;
             }
             final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
                     - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
@@ -13587,7 +13596,9 @@
             memInfoBuilder.append("       ION: ");
             memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
             memInfoBuilder.append("\n");
-            kernelUsed += ionUnmapped;
+            // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being
+            // set on ION VMAs, therefore consider the entire ION heap as used kernel memory
+            kernelUsed += ionHeap;
         }
         memInfoBuilder.append("  Used RAM: ");
         memInfoBuilder.append(stringifyKBSize(
@@ -16066,6 +16077,22 @@
     }
 
     @Override
+    public boolean updateMccMncConfiguration(String mcc, String mnc) {
+        int mccInt, mncInt;
+        try {
+            mccInt = Integer.parseInt(mcc);
+            mncInt = Integer.parseInt(mnc);
+        } catch (NumberFormatException | StringIndexOutOfBoundsException ex) {
+            Slog.e(TAG, "Error parsing mcc: " + mcc + " mnc: " + mnc + ". ex=" + ex);
+            return false;
+        }
+        Configuration config = new Configuration();
+        config.mcc = mccInt;
+        config.mnc = mncInt == 0 ? Configuration.MNC_ZERO : mncInt;
+        return mActivityTaskManager.updateConfiguration(config);
+    }
+
+    @Override
     public int getLaunchedFromUid(IBinder activityToken) {
         return mActivityTaskManager.getLaunchedFromUid(activityToken);
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 5078b8a..5ab18d9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1058,7 +1058,7 @@
         } catch (NumberFormatException e) {
             packageName = arg;
         }
-        mInterface.crashApplication(-1, pid, packageName, userId, "shell-induced crash");
+        mInterface.crashApplication(-1, pid, packageName, userId, "shell-induced crash", false);
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index a4c6950..bbd2d34 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -314,20 +314,24 @@
     }
 
     void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) {
-        app.setCrashing(false);
-        app.crashingReport = null;
-        app.setNotResponding(false);
-        app.notRespondingReport = null;
         if (app.anrDialog == fromDialog) {
             app.anrDialog = null;
         }
         if (app.waitDialog == fromDialog) {
             app.waitDialog = null;
         }
+        killAppImmediateLocked(app, "user-terminated", "user request after error");
+    }
+
+    private void killAppImmediateLocked(ProcessRecord app, String reason, String killReason) {
+        app.setCrashing(false);
+        app.crashingReport = null;
+        app.setNotResponding(false);
+        app.notRespondingReport = null;
         if (app.pid > 0 && app.pid != MY_PID) {
-            handleAppCrashLocked(app, "user-terminated" /*reason*/,
+            handleAppCrashLocked(app, reason,
                     null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
-            app.kill("user request after error", true);
+            app.kill(killReason, true);
         }
     }
 
@@ -341,7 +345,7 @@
      * @param message
      */
     void scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId,
-            String message) {
+            String message, boolean force) {
         ProcessRecord proc = null;
 
         // Figure out which process to kill.  We don't trust that initialPid
@@ -374,6 +378,14 @@
         }
 
         proc.scheduleCrash(message);
+        if (force) {
+            // If the app is responsive, the scheduled crash will happen as expected
+            // and then the delayed summary kill will be a no-op.
+            final ProcessRecord p = proc;
+            mService.mHandler.postDelayed(
+                    () -> killAppImmediateLocked(p, "forced", "killed for invalid state"),
+                    5000L);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index d935162..5b5d8a0 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -53,6 +53,7 @@
 import android.app.AppProtoEnums;
 import android.app.IApplicationThread;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledAfter;
 import android.content.ComponentName;
 import android.content.Context;
@@ -95,6 +96,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ProcessMap;
 import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.RuntimeInit;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.MemInfoReader;
@@ -106,7 +108,6 @@
 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;
@@ -290,9 +291,17 @@
      * Pointers</a>
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
 
+    /**
+     * Enable memory tag checks in non-system apps. This flag will only have an effect on
+     * hardware supporting the ARM Memory Tagging Extension (MTE).
+     */
+    @ChangeId
+    @Disabled
+    private static final long NATIVE_MEMORY_TAGGING = 135772972; // This is a bug id.
+
     ActivityManagerService mService = null;
 
     // To kill process groups asynchronously
@@ -1665,8 +1674,20 @@
                 runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
             }
 
-            if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
-                runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+            if (Zygote.nativeSupportsMemoryTagging()) {
+                // System apps are generally more privileged than regular apps, and don't have the
+                // same app compat concerns as regular apps, so we enable async tag checks for all
+                // of their processes.
+                if ((app.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                        || mPlatformCompat.isChangeEnabled(NATIVE_MEMORY_TAGGING, app.info)) {
+                    runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+                }
+            } else if (Zygote.nativeSupportsTaggedPointers()) {
+                // Enable heap pointer tagging if supported by the kernel, unless disabled by the
+                // target sdk level or compat feature.
+                if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+                    runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+                }
             }
 
             String invokeWith = null;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index dee8e3b..c408695 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -798,6 +798,7 @@
             final String localPackageName = packageName;
             final int localForegroundId = foregroundId;
             final Notification _foregroundNoti = foregroundNoti;
+            final ServiceRecord record = this;
             ams.mHandler.post(new Runnable() {
                 public void run() {
                     NotificationManagerInternal nm = LocalServices.getService(
@@ -896,10 +897,8 @@
                         Slog.w(TAG, "Error showing notification for service", e);
                         // If it gave us a garbage notification, it doesn't
                         // get to be foreground.
-                        ams.setServiceForeground(instanceName, ServiceRecord.this,
-                                0, null, 0, 0);
-                        ams.crashApplication(appUid, appPid, localPackageName, -1,
-                                "Bad notification for startForeground: " + e);
+                        ams.mServices.killMisbehavingService(record,
+                                appUid, appPid, localPackageName);
                     }
                 }
             });
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index d45bc72a..06f11a8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1789,7 +1789,6 @@
 
     private int checkOperationImpl(int code, int uid, String packageName,
                 boolean raw) {
-        verifyIncomingUid(uid);
         verifyIncomingOp(code);
         String resolvedPackageName = resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 068d6fc..d6315ff 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -672,7 +672,7 @@
             AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = defaultCallVolume;
         } else {
             AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] =
-                    (maxCallVolume * 3) / 4;
+                    (MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] * 3) / 4;
         }
 
         int maxMusicVolume = SystemProperties.getInt("ro.config.media_vol_steps", -1);
@@ -1230,10 +1230,10 @@
     private void updateDefaultVolumes() {
         for (int stream = 0; stream < mStreamStates.length; stream++) {
             if (stream != mStreamVolumeAlias[stream]) {
-                AudioSystem.DEFAULT_STREAM_VOLUME[stream] = rescaleIndex(
-                        AudioSystem.DEFAULT_STREAM_VOLUME[mStreamVolumeAlias[stream]],
+                AudioSystem.DEFAULT_STREAM_VOLUME[stream] = (rescaleIndex(
+                        AudioSystem.DEFAULT_STREAM_VOLUME[mStreamVolumeAlias[stream]] * 10,
                         mStreamVolumeAlias[stream],
-                        stream);
+                        stream) + 5) / 10;
             }
         }
     }
@@ -2135,6 +2135,13 @@
 
         // For legacy reason, propagate to all streams associated to this volume group
         for (final int groupedStream : vgs.getLegacyStreamTypes()) {
+            try {
+                ensureValidStreamType(groupedStream);
+            } catch (IllegalArgumentException e) {
+                Log.d(TAG, "volume group " + volumeGroup + " has internal streams (" + groupedStream
+                        + "), do not change associated stream volume");
+                continue;
+            }
             setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage,
                             Binder.getCallingUid());
         }
@@ -3350,7 +3357,15 @@
                 hdlr = h;
                 // Remove from client list so that it is re-inserted at top of list
                 iter.remove();
-                hdlr.getBinder().unlinkToDeath(hdlr, 0);
+                try {
+                    hdlr.getBinder().unlinkToDeath(hdlr, 0);
+                    if (cb != hdlr.getBinder()){
+                        hdlr = null;
+                    }
+                } catch (NoSuchElementException e) {
+                    hdlr = null;
+                    Log.w(TAG, "link does not exist ...");
+                }
                 break;
             }
         }
@@ -4318,6 +4333,7 @@
     public void setWiredDeviceConnectionState(int type,
             @ConnectionState int state, String address, String name,
             String caller) {
+        enforceModifyAudioRoutingPermission();
         if (state != CONNECTION_STATE_CONNECTED
                 && state != CONNECTION_STATE_DISCONNECTED) {
             throw new IllegalArgumentException("Invalid state " + state);
@@ -4465,7 +4481,9 @@
             } catch (IllegalArgumentException e) {
                 // Volume Groups without attributes are not controllable through set/get volume
                 // using attributes. Do not append them.
-                Log.d(TAG, "volume group " + avg.name() + " for internal policy needs");
+                if (DEBUG_VOL) {
+                    Log.v(TAG, "volume group " + avg.name() + " for internal policy needs");
+                }
                 continue;
             }
             sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
@@ -4486,7 +4504,9 @@
     }
 
     private void readVolumeGroupsSettings() {
-        Log.v(TAG, "readVolumeGroupsSettings");
+        if (DEBUG_VOL) {
+            Log.v(TAG, "readVolumeGroupsSettings.");
+        }
         for (int i = 0; i < sVolumeGroupStates.size(); i++) {
             final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
             vgs.readSettings();
@@ -4496,7 +4516,9 @@
 
     // Called upon crash of AudioServer
     private void restoreVolumeGroups() {
-        Log.v(TAG, "restoreVolumeGroups");
+        if (DEBUG_VOL) {
+            Log.v(TAG, "restoreVolumeGroups");
+        }
         for (int i = 0; i < sVolumeGroupStates.size(); i++) {
             final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
             vgs.applyAllVolumes();
@@ -4532,7 +4554,9 @@
 
         private VolumeGroupState(AudioVolumeGroup avg) {
             mAudioVolumeGroup = avg;
-            Log.v(TAG, "VolumeGroupState for " + avg.toString());
+            if (DEBUG_VOL) {
+                Log.v(TAG, "VolumeGroupState for " + avg.toString());
+            }
             for (final AudioAttributes aa : avg.getAudioAttributes()) {
                 if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
                     mAudioAttributes = aa;
@@ -4627,11 +4651,19 @@
             return mIndexMin;
         }
 
+        private boolean isValidLegacyStreamType() {
+            return (mLegacyStreamType != AudioSystem.STREAM_DEFAULT)
+                    && (mLegacyStreamType < mStreamStates.length);
+        }
+
         public void applyAllVolumes() {
             synchronized (VolumeGroupState.class) {
-                if (mLegacyStreamType != AudioSystem.STREAM_DEFAULT) {
-                    // No-op to avoid regression with stream based volume management
-                    return;
+                int deviceForStream = AudioSystem.DEVICE_NONE;
+                int volumeIndexForStream = 0;
+                if (isValidLegacyStreamType()) {
+                    // Prevent to apply settings twice when group is associated to public stream
+                    deviceForStream = getDeviceForStream(mLegacyStreamType);
+                    volumeIndexForStream = getStreamVolume(mLegacyStreamType);
                 }
                 // apply device specific volumes first
                 int index;
@@ -4639,16 +4671,30 @@
                     final int device = mIndexMap.keyAt(i);
                     if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                         index = mIndexMap.valueAt(i);
-                        Log.v(TAG, "applyAllVolumes: restore index " + index + " for group "
-                                + mAudioVolumeGroup.name() + " and device "
-                                + AudioSystem.getOutputDeviceName(device));
+                        if (device == deviceForStream && volumeIndexForStream == index) {
+                            continue;
+                        }
+                        if (DEBUG_VOL) {
+                            Log.v(TAG, "applyAllVolumes: restore index " + index + " for group "
+                                    + mAudioVolumeGroup.name() + " and device "
+                                    + AudioSystem.getOutputDeviceName(device));
+                        }
                         setVolumeIndexInt(index, device, 0 /*flags*/);
                     }
                 }
                 // apply default volume last: by convention , default device volume will be used
                 index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
-                Log.v(TAG, "applyAllVolumes: restore default device index " + index + " for group "
-                        + mAudioVolumeGroup.name());
+                if (DEBUG_VOL) {
+                    Log.v(TAG, "applyAllVolumes: restore default device index " + index
+                            + " for group " + mAudioVolumeGroup.name());
+                }
+                if (isValidLegacyStreamType()) {
+                    int defaultStreamIndex = (mStreamStates[mLegacyStreamType]
+                            .getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5) / 10;
+                    if (defaultStreamIndex == index) {
+                        return;
+                    }
+                }
                 setVolumeIndexInt(index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/);
             }
         }
@@ -4657,9 +4703,12 @@
             if (mUseFixedVolume) {
                 return;
             }
-            Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
-                    + mAudioVolumeGroup.name() + " and device "
-                    + AudioSystem.getOutputDeviceName(device));
+            if (DEBUG_VOL) {
+                Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
+                        + mAudioVolumeGroup.name()
+                        + ", device " + AudioSystem.getOutputDeviceName(device)
+                        + " and User=" + ActivityManager.getCurrentUser());
+            }
             boolean success = Settings.System.putIntForUser(mContentResolver,
                     getSettingNameForDevice(device),
                     getIndex(device),
@@ -4689,12 +4738,18 @@
                     index = Settings.System.getIntForUser(
                             mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
                     if (index == -1) {
-                        Log.e(TAG, "readSettings: No index stored for group "
-                                + mAudioVolumeGroup.name() + ", device " + name);
+                        if (DEBUG_VOL) {
+                            Log.e(TAG, "readSettings: No index stored for group "
+                                    + mAudioVolumeGroup.name() + ", device " + name
+                                    + ", User=" + ActivityManager.getCurrentUser());
+                        }
                         continue;
                     }
-                    Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
-                             + " for group " + mAudioVolumeGroup.name() + ", device: " + name);
+                    if (DEBUG_VOL) {
+                        Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
+                                 + " for group " + mAudioVolumeGroup.name() + ", device: " + name
+                                 + ", User=" + ActivityManager.getCurrentUser());
+                    }
                     mIndexMap.put(device, getValidIndex(index));
                 }
             }
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index c845981..3ac3771 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -625,7 +625,12 @@
                 return;
             }
         }
-        final FocusRequester fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
+        final FocusRequester fr;
+        if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
+            fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
+        } else {
+            fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
+        }
         if (fr != null) {
             fr.dispatchFocusResultFromExtPolicy(requestResult);
         }
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 7bdeb59..2e9818d 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -151,6 +151,15 @@
         return true;
     }
 
+    /**
+     * Checks whether a change has an override for a package.
+     * @param packageName name of the package
+     * @return true if there is such override
+     */
+    boolean hasOverride(String packageName) {
+        return mPackageOverrides != null && mPackageOverrides.containsKey(packageName);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder("ChangeId(")
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 33eeb8a..d3f4eb4 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -242,11 +242,13 @@
             CompatChange c = mChanges.get(changeId);
             try {
                 if (c != null) {
-                    OverrideAllowedState allowedState =
-                            mOverrideValidator.getOverrideAllowedState(changeId, packageName);
-                    allowedState.enforce(changeId, packageName);
-                    overrideExists = true;
-                    c.removePackageOverride(packageName);
+                    overrideExists = c.hasOverride(packageName);
+                    if (overrideExists) {
+                        OverrideAllowedState allowedState =
+                                mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+                        allowedState.enforce(changeId, packageName);
+                        c.removePackageOverride(packageName);
+                    }
                 }
             } catch (RemoteException e) {
                 // Should never occur, since validator is in the same process.
@@ -291,12 +293,14 @@
             for (int i = 0; i < mChanges.size(); ++i) {
                 try {
                     CompatChange change = mChanges.valueAt(i);
-                    OverrideAllowedState allowedState =
-                            mOverrideValidator.getOverrideAllowedState(change.getId(),
-                                                                       packageName);
-                    allowedState.enforce(change.getId(), packageName);
-                    if (change != null) {
-                        mChanges.valueAt(i).removePackageOverride(packageName);
+                    if (change.hasOverride(packageName)) {
+                        OverrideAllowedState allowedState =
+                                mOverrideValidator.getOverrideAllowedState(change.getId(),
+                                        packageName);
+                        allowedState.enforce(change.getId(), packageName);
+                        if (change != null) {
+                            mChanges.valueAt(i).removePackageOverride(packageName);
+                        }
                     }
                 } catch (RemoteException e) {
                     // Should never occur, since validator is in the same process.
diff --git a/services/core/java/com/android/server/compat/OWNERS b/services/core/java/com/android/server/compat/OWNERS
index 2b7cdb0..cfd0a4b 100644
--- a/services/core/java/com/android/server/compat/OWNERS
+++ b/services/core/java/com/android/server/compat/OWNERS
@@ -2,6 +2,5 @@
 platform-compat-eng+reviews@google.com
 
 andreionea@google.com
-atrost@google.com
 mathewi@google.com
 satayev@google.com
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 3e51ee7..9d1d0d7 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
 import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -28,6 +29,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Slog;
@@ -43,6 +45,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Arrays;
 
 /**
  * System server internal API for gating and reporting compatibility changes.
@@ -55,6 +58,9 @@
     private final ChangeReporter mChangeReporter;
     private final CompatConfig mCompatConfig;
 
+    private static int sMinTargetSdk = Build.VERSION_CODES.P;
+    private static int sMaxTargetSdk = Build.VERSION_CODES.Q;
+
     public PlatformCompat(Context context) {
         mContext = context;
         mChangeReporter = new ChangeReporter(
@@ -219,6 +225,12 @@
         return mCompatConfig.dumpChanges();
     }
 
+    @Override
+    public CompatibilityChangeInfo[] listUIChanges() {
+        return Arrays.stream(listAllChanges()).filter(
+                x -> isShownInUI(x)).toArray(CompatibilityChangeInfo[]::new);
+    }
+
     /**
      * Check whether the change is known to the compat config.
      *
@@ -318,6 +330,10 @@
     }
 
     private void checkCompatChangeLogPermission() throws SecurityException {
+        // Don't check for permissions within the system process
+        if (Binder.getCallingUid() == SYSTEM_UID) {
+            return;
+        }
         if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE)
                 != PERMISSION_GRANTED) {
             throw new SecurityException("Cannot log compat change usage");
@@ -325,6 +341,10 @@
     }
 
     private void checkCompatChangeReadPermission() throws SecurityException {
+        // Don't check for permissions within the system process
+        if (Binder.getCallingUid() == SYSTEM_UID) {
+            return;
+        }
         if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG)
                 != PERMISSION_GRANTED) {
             throw new SecurityException("Cannot read compat change");
@@ -332,6 +352,10 @@
     }
 
     private void checkCompatChangeOverridePermission() throws SecurityException {
+        // Don't check for permissions within the system process
+        if (Binder.getCallingUid() == SYSTEM_UID) {
+            return;
+        }
         if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
                 != PERMISSION_GRANTED) {
             throw new SecurityException("Cannot override compat change");
@@ -342,4 +366,17 @@
         checkCompatChangeReadPermission();
         checkCompatChangeLogPermission();
     }
+
+    private boolean isShownInUI(CompatibilityChangeInfo change) {
+        if (change.getLoggingOnly()) {
+            return false;
+        }
+        if (change.getEnableAfterTargetSdk() > 0) {
+            if (change.getEnableAfterTargetSdk() < sMinTargetSdk
+                    || change.getEnableAfterTargetSdk() > sMaxTargetSdk) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 5250a77..506c8e3 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -27,6 +27,7 @@
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 
+import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +35,7 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkUtils;
+import android.net.ResolverOptionsParcel;
 import android.net.ResolverParamsParcel;
 import android.net.Uri;
 import android.net.shared.PrivateDnsConfig;
@@ -237,6 +239,8 @@
     // TODO: Replace these Maps with SparseArrays.
     private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
     private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap;
+    private final Map<Integer, LinkProperties> mLinkPropertiesMap;
+    private final Map<Integer, int[]> mTransportsMap;
 
     private int mNumDnsEntries;
     private int mSampleValidity;
@@ -253,6 +257,8 @@
         mSystemProperties = sp;
         mPrivateDnsMap = new HashMap<>();
         mPrivateDnsValidationMap = new HashMap<>();
+        mLinkPropertiesMap = new HashMap<>();
+        mTransportsMap = new HashMap<>();
 
         // TODO: Create and register ContentObservers to track every setting
         // used herein, posting messages to respond to changes.
@@ -265,6 +271,8 @@
     public void removeNetwork(Network network) {
         mPrivateDnsMap.remove(network.netId);
         mPrivateDnsValidationMap.remove(network.netId);
+        mTransportsMap.remove(network.netId);
+        mLinkPropertiesMap.remove(network.netId);
     }
 
     public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
@@ -304,9 +312,35 @@
         statuses.updateStatus(update);
     }
 
-    public void setDnsConfigurationForNetwork(
-            int netId, LinkProperties lp, boolean isDefaultNetwork) {
+    /**
+     * When creating a new network or transport types are changed in a specific network,
+     * transport types are always saved to a hashMap before update dns config.
+     * When destroying network, the specific network will be removed from the hashMap.
+     * The hashMap is always accessed on the same thread.
+     */
+    public void updateTransportsForNetwork(int netId, @NonNull int[] transportTypes) {
+        mTransportsMap.put(netId, transportTypes);
+        sendDnsConfigurationForNetwork(netId);
+    }
 
+    /**
+     * When {@link LinkProperties} are changed in a specific network, they are
+     * always saved to a hashMap before update dns config.
+     * When destroying network, the specific network will be removed from the hashMap.
+     * The hashMap is always accessed on the same thread.
+     */
+    public void noteDnsServersForNetwork(int netId, @NonNull LinkProperties lp) {
+        mLinkPropertiesMap.put(netId, lp);
+        sendDnsConfigurationForNetwork(netId);
+    }
+
+    /**
+     * Send dns configuration parameters to resolver for a given network.
+     */
+    public void sendDnsConfigurationForNetwork(int netId) {
+        final LinkProperties lp = mLinkPropertiesMap.get(netId);
+        final int[] transportTypes = mTransportsMap.get(netId);
+        if (lp == null || transportTypes == null) return;
         updateParametersSettings();
         final ResolverParamsParcel paramsParcel = new ResolverParamsParcel();
 
@@ -319,15 +353,16 @@
         // networks like IMS.
         final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
                 PRIVATE_DNS_OFF);
-
         final boolean useTls = privateDnsCfg.useTls;
         final boolean strictMode = privateDnsCfg.inStrictMode();
+
         paramsParcel.netId = netId;
         paramsParcel.sampleValiditySeconds = mSampleValidity;
         paramsParcel.successThreshold = mSuccessThreshold;
         paramsParcel.minSamples = mMinSamples;
         paramsParcel.maxSamples = mMaxSamples;
-        paramsParcel.servers = NetworkUtils.makeStrings(lp.getDnsServers());
+        paramsParcel.servers =
+                NetworkUtils.makeStrings(lp.getDnsServers());
         paramsParcel.domains = getDomainStrings(lp.getDomains());
         paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : "";
         paramsParcel.tlsServers =
@@ -337,6 +372,8 @@
                               .collect(Collectors.toList()))
                 : useTls ? paramsParcel.servers  // Opportunistic
                 : new String[0];            // Off
+        paramsParcel.resolverOptions = new ResolverOptionsParcel();
+        paramsParcel.transportTypes = transportTypes;
         // Prepare to track the validation status of the DNS servers in the
         // resolver config when private DNS is in opportunistic or strict mode.
         if (useTls) {
@@ -349,7 +386,7 @@
             mPrivateDnsValidationMap.remove(netId);
         }
 
-        Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
+        Slog.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
                 + "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers),
                 Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds,
                 paramsParcel.successThreshold, paramsParcel.minSamples,
@@ -363,13 +400,6 @@
             Slog.e(TAG, "Error setting DNS configuration: " + e);
             return;
         }
-
-        // TODO: netd should listen on [::1]:53 and proxy queries to the current
-        // default network, and we should just set net.dns1 to ::1, not least
-        // because applications attempting to use net.dns resolvers will bypass
-        // the privacy protections of things like DNS-over-TLS.
-        if (isDefaultNetwork) setDefaultDnsSystemProperties(lp.getDnsServers());
-        flushVmDnsCache();
     }
 
     public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
@@ -384,7 +414,10 @@
         mNumDnsEntries = last;
     }
 
-    private void flushVmDnsCache() {
+    /**
+     * Flush DNS caches and events work before boot has completed.
+     */
+    public void flushVmDnsCache() {
         /*
          * Tell the VMs to toss their DNS caches
          */
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 5059a48..7c8fb5a 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -220,9 +220,9 @@
                     + " network=" + mNai.network
                     + " startedState=" + startedStateString(mStartedState)
                     + " "
-                    + IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort)
+                    + IpUtils.addressAndPortToString(mPacket.getSrcAddress(), mPacket.getSrcPort())
                     + "->"
-                    + IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)
+                    + IpUtils.addressAndPortToString(mPacket.getDstAddress(), mPacket.getDstPort())
                     + " interval=" + mInterval
                     + " uid=" + mUid + " pid=" + mPid + " privileged=" + mPrivileged
                     + " packetData=" + HexDump.toHexString(mPacket.getPacket())
@@ -250,7 +250,7 @@
         private int checkSourceAddress() {
             // Check that we have the source address.
             for (InetAddress address : mNai.linkProperties.getAddresses()) {
-                if (address.equals(mPacket.srcAddress)) {
+                if (address.equals(mPacket.getSrcAddress())) {
                     return SUCCESS;
                 }
             }
@@ -619,7 +619,7 @@
             packet = NattKeepalivePacketData.nattKeepalivePacket(
                     srcAddress, srcPort, dstAddress, NATT_PORT);
         } catch (InvalidPacketException e) {
-            notifyErrorCallback(cb, e.error);
+            notifyErrorCallback(cb, e.getError());
             return;
         }
         KeepaliveInfo ki = null;
@@ -662,7 +662,7 @@
             notifyErrorCallback(cb, e.error);
             return;
         } catch (InvalidPacketException e) {
-            notifyErrorCallback(cb, e.error);
+            notifyErrorCallback(cb, e.getError());
             return;
         }
         KeepaliveInfo ki = null;
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 04c792a..d548871 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -25,6 +25,7 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
 import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
@@ -220,7 +221,7 @@
             mNetworkTemplate = new NetworkTemplate(
                     NetworkTemplate.MATCH_MOBILE, subscriberId, new String[] { subscriberId },
                     null, NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
-                    NetworkStats.DEFAULT_NETWORK_NO);
+                    NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL);
             mUsageCallback = new UsageCallback() {
                 @Override
                 public void onThresholdReached(int networkType, String subscriberId) {
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 82465f8..3091a71 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -81,12 +81,23 @@
         RUNNING,      // start() called, and the stacked iface is known to be up.
     }
 
-    private IpPrefix mNat64Prefix;
+    /**
+     * NAT64 prefix currently in use. Only valid in STARTING or RUNNING states.
+     * Used, among other things, to avoid updates when switching from a prefix learned from one
+     * source (e.g., RA) to the same prefix learned from another source (e.g., RA).
+     */
+    private IpPrefix mNat64PrefixInUse;
+    /** NAT64 prefix (if any) discovered from DNS via RFC 7050. */
+    private IpPrefix mNat64PrefixFromDns;
+    /** NAT64 prefix (if any) learned from the network via RA. */
+    private IpPrefix mNat64PrefixFromRa;
     private String mBaseIface;
     private String mIface;
     private Inet6Address mIPv6Address;
     private State mState = State.IDLE;
 
+    private boolean mPrefixDiscoveryRunning;
+
     public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
             INetworkManagementService nmService) {
         mDnsResolver = dnsResolver;
@@ -100,7 +111,7 @@
      * currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to
      * enable NAT64 prefix discovery.
      *
-     * @param network the NetworkAgentInfo corresponding to the network.
+     * @param nai the NetworkAgentInfo corresponding to the network.
      * @return true if the network requires clat, false otherwise.
      */
     @VisibleForTesting
@@ -136,15 +147,6 @@
     }
 
     /**
-     * @return true if we have started prefix discovery and not yet stopped it (regardless of
-     * whether it is still running or has succeeded).
-     * A true result corresponds to internal states DISCOVERING, STARTING and RUNNING.
-     */
-    public boolean isPrefixDiscoveryStarted() {
-        return mState == State.DISCOVERING || isStarted();
-    }
-
-    /**
      * @return true if clatd has been started and has not yet stopped.
      * A true result corresponds to internal states STARTING and RUNNING.
      */
@@ -178,9 +180,10 @@
             return;
         }
 
+        mNat64PrefixInUse = selectNat64Prefix();
         String addrStr = null;
         try {
-            addrStr = mNetd.clatdStart(baseIface, mNat64Prefix.toString());
+            addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
         } catch (RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
         }
@@ -192,6 +195,12 @@
         } catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
             Slog.e(TAG, "Invalid IPv6 address " + addrStr);
         }
+        if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) {
+            stopPrefixDiscovery();
+        }
+        if (!mPrefixDiscoveryRunning) {
+            setPrefix64(mNat64PrefixInUse);
+        }
     }
 
     /**
@@ -211,10 +220,18 @@
         } catch (RemoteException | IllegalStateException e) {
             Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
         }
+        mNat64PrefixInUse = null;
         mIface = null;
         mBaseIface = null;
-        mState = State.IDLE;
-        if (requiresClat(mNetwork)) {
+
+        if (!mPrefixDiscoveryRunning) {
+            setPrefix64(null);
+        }
+
+        if (isPrefixDiscoveryNeeded()) {
+            if (!mPrefixDiscoveryRunning) {
+                startPrefixDiscovery();
+            }
             mState = State.DISCOVERING;
         } else {
             stopPrefixDiscovery();
@@ -276,10 +293,10 @@
     private void startPrefixDiscovery() {
         try {
             mDnsResolver.startPrefix64Discovery(getNetId());
-            mState = State.DISCOVERING;
         } catch (RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
         }
+        mPrefixDiscoveryRunning = true;
     }
 
     private void stopPrefixDiscovery() {
@@ -288,38 +305,100 @@
         } catch (RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
         }
+        mPrefixDiscoveryRunning = false;
+    }
+
+    private boolean isPrefixDiscoveryNeeded() {
+        // If there is no NAT64 prefix in the RA, prefix discovery is always needed. It cannot be
+        // stopped after it succeeds, because stopping it will cause netd to report that the prefix
+        // has been removed, and that will cause us to stop clatd.
+        return requiresClat(mNetwork) && mNat64PrefixFromRa == null;
+    }
+
+    private void setPrefix64(IpPrefix prefix) {
+        final String prefixString = (prefix != null) ? prefix.toString() : "";
+        try {
+            mDnsResolver.setPrefix64(getNetId(), prefixString);
+        } catch (RemoteException | ServiceSpecificException e) {
+            Slog.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to "
+                    + prefix + ": " + e);
+        }
+    }
+
+    private void maybeHandleNat64PrefixChange() {
+        final IpPrefix newPrefix = selectNat64Prefix();
+        if (!Objects.equals(mNat64PrefixInUse, newPrefix)) {
+            Slog.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to "
+                    + newPrefix);
+            stop();
+            // It's safe to call update here, even though this method is called from update, because
+            // stop() is guaranteed to have moved out of STARTING and RUNNING, which are the only
+            // states in which this method can be called.
+            update();
+        }
     }
 
     /**
      * Starts/stops NAT64 prefix discovery and clatd as necessary.
      */
     public void update() {
-        // TODO: turn this class into a proper StateMachine. // http://b/126113090
-        if (requiresClat(mNetwork)) {
-            if (!isPrefixDiscoveryStarted()) {
-                startPrefixDiscovery();
-            } else if (shouldStartClat(mNetwork)) {
-                // NAT64 prefix detected. Start clatd.
-                // TODO: support the NAT64 prefix changing after it's been discovered. There is no
-                // need to support this at the moment because it cannot happen without changes to
-                // the Dns64Configuration code in netd.
-                start();
-            } else {
-                // NAT64 prefix removed. Stop clatd and go back into DISCOVERING state.
-                stop();
-            }
-        } else {
-            // Network no longer requires clat. Stop clat and prefix discovery.
-            if (isStarted()) {
-                stop();
-            } else if (isPrefixDiscoveryStarted()) {
-                leaveStartedState();
-            }
+        // TODO: turn this class into a proper StateMachine. http://b/126113090
+        switch (mState) {
+            case IDLE:
+                if (isPrefixDiscoveryNeeded()) {
+                    startPrefixDiscovery();  // Enters DISCOVERING state.
+                    mState = State.DISCOVERING;
+                } else if (requiresClat(mNetwork)) {
+                    start();  // Enters STARTING state.
+                }
+                break;
+
+            case DISCOVERING:
+                if (shouldStartClat(mNetwork)) {
+                    // NAT64 prefix detected. Start clatd.
+                    start();  // Enters STARTING state.
+                    return;
+                }
+                if (!requiresClat(mNetwork)) {
+                    // IPv4 address added. Go back to IDLE state.
+                    stopPrefixDiscovery();
+                    mState = State.IDLE;
+                    return;
+                }
+                break;
+
+            case STARTING:
+            case RUNNING:
+                // NAT64 prefix removed, or IPv4 address added.
+                // Stop clatd and go back into DISCOVERING or idle.
+                if (!shouldStartClat(mNetwork)) {
+                    stop();
+                    break;
+                }
+                // Only necessary while clat is actually started.
+                maybeHandleNat64PrefixChange();
+                break;
         }
     }
 
-    public void setNat64Prefix(IpPrefix nat64Prefix) {
-        mNat64Prefix = nat64Prefix;
+    /**
+     * Picks a NAT64 prefix to use. Always prefers the prefix from the RA if one is received from
+     * both RA and DNS, because the prefix in the RA has better security and updatability, and will
+     * almost always be received first anyway.
+     *
+     * Any network that supports legacy hosts will support discovering the DNS64 prefix via DNS as
+     * well. If the prefix from the RA is withdrawn, fall back to that for reliability purposes.
+     */
+    private IpPrefix selectNat64Prefix() {
+        return mNat64PrefixFromRa != null ? mNat64PrefixFromRa : mNat64PrefixFromDns;
+    }
+
+    public void setNat64PrefixFromRa(IpPrefix prefix) {
+        mNat64PrefixFromRa = prefix;
+    }
+
+    public void setNat64PrefixFromDns(IpPrefix prefix) {
+        mNat64PrefixFromDns = prefix;
     }
 
     /**
@@ -328,7 +407,9 @@
      * has no idea that 464xlat is running on top of it.
      */
     public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) {
-        lp.setNat64Prefix(mNat64Prefix);
+        // This must be done even if clatd is not running, because otherwise shouldStartClat would
+        // never return true.
+        lp.setNat64Prefix(selectNat64Prefix());
 
         if (!isRunning()) {
             return;
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 23b954c..a9f62d9 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity;
 
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
 import static android.net.NetworkCapabilities.transportNamesOf;
 
 import android.annotation.NonNull;
@@ -160,10 +161,6 @@
     // Whether a captive portal was found during the last network validation attempt.
     public boolean lastCaptivePortalDetected;
 
-    // Indicates the captive portal app was opened to show a login UI to the user, but the network
-    // has not validated yet.
-    public volatile boolean captivePortalValidationPending;
-
     // Set to true when partial connectivity was detected.
     public boolean partialConnectivity;
 
@@ -171,6 +168,9 @@
     // Obtained by ConnectivityService and merged into NetworkAgent-provided information.
     public CaptivePortalData captivePortalData;
 
+    // The UID of the remote entity that created this Network.
+    public final int creatorUid;
+
     // Networks are lingered when they become unneeded as a result of their NetworkRequests being
     // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
     // network is taken down.  This usually only happens to the default network. Lingering ends with
@@ -247,6 +247,10 @@
     // How many of the satisfied requests are of type BACKGROUND_REQUEST.
     private int mNumBackgroundNetworkRequests = 0;
 
+    // The last ConnectivityReport made available for this network. This value is only null before a
+    // report is generated. Once non-null, it will never be null again.
+    @Nullable private ConnectivityReport mConnectivityReport;
+
     public final Messenger messenger;
     public final AsyncChannel asyncChannel;
 
@@ -267,7 +271,8 @@
     public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context,
             Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
-            IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) {
+            IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
+            int creatorUid) {
         this.messenger = messenger;
         asyncChannel = ac;
         network = net;
@@ -281,6 +286,7 @@
         mHandler = handler;
         networkAgentConfig = config;
         this.factorySerialNumber = factorySerialNumber;
+        this.creatorUid = creatorUid;
     }
 
     /**
@@ -625,23 +631,49 @@
         for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
     }
 
+    /**
+     * Sets the most recent ConnectivityReport for this network.
+     *
+     * <p>This should only be called from the ConnectivityService thread.
+     *
+     * @hide
+     */
+    public void setConnectivityReport(@NonNull ConnectivityReport connectivityReport) {
+        mConnectivityReport = connectivityReport;
+    }
+
+    /**
+     * Returns the most recent ConnectivityReport for this network, or null if none have been
+     * reported yet.
+     *
+     * <p>This should only be called from the ConnectivityService thread.
+     *
+     * @hide
+     */
+    @Nullable
+    public ConnectivityReport getConnectivityReport() {
+        return mConnectivityReport;
+    }
+
     // TODO: Print shorter members first and only print the boolean variable which value is true
     // to improve readability.
     public String toString() {
-        return "NetworkAgentInfo{ ni{" + networkInfo + "}  "
-                + "network{" + network + "}  nethandle{" + network.getNetworkHandle() + "}  "
-                + "lp{" + linkProperties + "}  "
-                + "nc{" + networkCapabilities + "}  Score{" + getCurrentScore() + "}  "
-                + "everValidated{" + everValidated + "}  lastValidated{" + lastValidated + "}  "
-                + "created{" + created + "} lingering{" + isLingering() + "} "
-                + "explicitlySelected{" + networkAgentConfig.explicitlySelected + "} "
-                + "acceptUnvalidated{" + networkAgentConfig.acceptUnvalidated + "} "
-                + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
-                + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
-                + "captivePortalValidationPending{" + captivePortalValidationPending + "} "
-                + "partialConnectivity{" + partialConnectivity + "} "
-                + "acceptPartialConnectivity{" + networkAgentConfig.acceptPartialConnectivity + "} "
-                + "clat{" + clatd + "} "
+        return "NetworkAgentInfo{"
+                + "network{" + network + "}  handle{" + network.getNetworkHandle() + "}  ni{"
+                + networkInfo.toShortString() + "} "
+                + "  Score{" + getCurrentScore() + "} "
+                + (isLingering() ? " lingering" : "")
+                + (everValidated ? " everValidated" : "")
+                + (lastValidated ? " lastValidated" : "")
+                + (partialConnectivity ? " partialConnectivity" : "")
+                + (everCaptivePortalDetected ? " everCaptivePortal" : "")
+                + (lastCaptivePortalDetected ? " isCaptivePortal" : "")
+                + (networkAgentConfig.explicitlySelected ? " explicitlySelected" : "")
+                + (networkAgentConfig.acceptUnvalidated ? " acceptUnvalidated" : "")
+                + (networkAgentConfig.acceptPartialConnectivity ? " acceptPartialConnectivity" : "")
+                + (clatd.isStarted() ? " clat{" + clatd + "} " : "")
+                + "  lp{" + linkProperties + "}"
+                + "  nc{" + networkCapabilities + "}"
                 + "}";
     }
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 25c761a..34b0aa2 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -51,7 +51,6 @@
         LOST_INTERNET(SystemMessage.NOTE_NETWORK_LOST_INTERNET),
         NETWORK_SWITCH(SystemMessage.NOTE_NETWORK_SWITCH),
         NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET),
-        LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN),
         PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY),
         SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN),
         PRIVATE_DNS_BROKEN(SystemMessage.NOTE_NETWORK_PRIVATE_DNS_BROKEN);
@@ -114,14 +113,10 @@
         }
     }
 
-    private static int getIcon(int transportType, NotificationType notifyType) {
-        if (transportType != TRANSPORT_WIFI) {
-            return R.drawable.stat_notify_rssi_in_range;
-        }
-
-        return notifyType == NotificationType.LOGGED_IN
-            ? R.drawable.ic_wifi_signal_4
-            : R.drawable.stat_notify_wifi_in_range;  // TODO: Distinguish ! from ?.
+    private static int getIcon(int transportType) {
+        return (transportType == TRANSPORT_WIFI)
+                ? R.drawable.stat_notify_wifi_in_range :  // TODO: Distinguish ! from ?.
+                R.drawable.stat_notify_rssi_in_range;
     }
 
     /**
@@ -155,7 +150,7 @@
         if (nai != null) {
             transportType = approximateTransportType(nai);
             final String extraInfo = nai.networkInfo.getExtraInfo();
-            name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSSID() : extraInfo;
+            name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSsid() : extraInfo;
             // Only notify for Internet-capable networks.
             if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return;
         } else {
@@ -185,17 +180,17 @@
         Resources r = mContext.getResources();
         final CharSequence title;
         final CharSequence details;
-        int icon = getIcon(transportType, notifyType);
+        int icon = getIcon(transportType);
         if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet,
-                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
             details = r.getString(R.string.wifi_no_internet_detailed);
         } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
             if (transportType == TRANSPORT_CELLULAR) {
                 title = r.getString(R.string.mobile_no_internet);
             } else if (transportType == TRANSPORT_WIFI) {
                 title = r.getString(R.string.wifi_no_internet,
-                        WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                        WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
             } else {
                 title = r.getString(R.string.other_networks_no_internet);
             }
@@ -203,19 +198,19 @@
         } else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
                 && transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.network_partial_connectivity,
-                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
             details = r.getString(R.string.network_partial_connectivity_detailed);
         } else if (notifyType == NotificationType.LOST_INTERNET &&
                 transportType == TRANSPORT_WIFI) {
             title = r.getString(R.string.wifi_no_internet,
-                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                    WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
             details = r.getString(R.string.wifi_no_internet_detailed);
         } else if (notifyType == NotificationType.SIGN_IN) {
             switch (transportType) {
                 case TRANSPORT_WIFI:
                     title = r.getString(R.string.wifi_available_sign_in, 0);
                     details = r.getString(R.string.network_available_sign_in_detailed,
-                            WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID()));
+                            WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()));
                     break;
                 case TRANSPORT_CELLULAR:
                     title = r.getString(R.string.network_available_sign_in, 0);
@@ -235,9 +230,6 @@
                     details = r.getString(R.string.network_available_sign_in_detailed, name);
                     break;
             }
-        } else if (notifyType == NotificationType.LOGGED_IN) {
-            title = WifiInfo.sanitizeSsid(nai.networkCapabilities.getSSID());
-            details = r.getString(R.string.captive_portal_logged_in_detailed);
         } else if (notifyType == NotificationType.NETWORK_SWITCH) {
             String fromTransport = getTransportName(transportType);
             String toTransport = getTransportName(approximateTransportType(switchToNai));
@@ -379,7 +371,6 @@
             case NETWORK_SWITCH:
                 return 2;
             case LOST_INTERNET:
-            case LOGGED_IN:
                 return 1;
             default:
                 return 0;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1fb34b3..730da28 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -21,6 +21,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.RouteInfo.RTN_THROW;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
 
@@ -64,6 +65,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkProvider;
+import android.net.NetworkRequest;
 import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.VpnManager;
@@ -317,8 +319,7 @@
      *
      * @param defaultNetwork underlying network for VPNs following platform's default
      */
-    public synchronized NetworkCapabilities updateCapabilities(
-            @Nullable Network defaultNetwork) {
+    public synchronized NetworkCapabilities updateCapabilities(@Nullable Network defaultNetwork) {
         if (mConfig == null) {
             // VPN is not running.
             return null;
@@ -350,11 +351,10 @@
         int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
-        // VPN's meteredness is OR'd with isAlwaysMetered and meteredness of its underlying
-        // networks.
-        boolean metered = isAlwaysMetered;
-        boolean roaming = false;
-        boolean congested = false;
+        boolean metered = isAlwaysMetered; // metered if any underlying is metered, or alwaysMetered
+        boolean roaming = false; // roaming if any underlying is roaming
+        boolean congested = false; // congested if any underlying is congested
+        boolean suspended = true; // suspended if all underlying are suspended
 
         boolean hadUnderlyingNetworks = false;
         if (null != underlyingNetworks) {
@@ -367,15 +367,24 @@
                     transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
                 }
 
-                // When we have multiple networks, we have to assume the
-                // worst-case link speed and restrictions.
+                // Merge capabilities of this underlying network. For bandwidth, assume the
+                // worst case.
                 downKbps = NetworkCapabilities.minBandwidth(downKbps,
                         underlyingCaps.getLinkDownstreamBandwidthKbps());
                 upKbps = NetworkCapabilities.minBandwidth(upKbps,
                         underlyingCaps.getLinkUpstreamBandwidthKbps());
+                // If this underlying network is metered, the VPN is metered (it may cost money
+                // to send packets on this network).
                 metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
+                // If this underlying network is roaming, the VPN is roaming (the billing structure
+                // is different than the usual, local one).
                 roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+                // If this underlying network is congested, the VPN is congested (the current
+                // condition of the network affects the performance of this network).
                 congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
+                // If this network is not suspended, the VPN is not suspended (the VPN
+                // is able to transfer some data).
+                suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
             }
         }
         if (!hadUnderlyingNetworks) {
@@ -383,6 +392,7 @@
             metered = true;
             roaming = false;
             congested = false;
+            suspended = false;
         }
 
         caps.setTransportTypes(transportTypes);
@@ -391,6 +401,7 @@
         caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
         caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
         caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
+        caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
     }
 
     /**
@@ -1955,6 +1966,7 @@
                 profile.ipsecCaCert = caCert;
 
                 // Start VPN profile
+                profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS);
                 startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
                 return;
             case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
@@ -1963,6 +1975,7 @@
                         Ikev2VpnProfile.encodeForIpsecSecret(profile.ipsecSecret.getBytes());
 
                 // Start VPN profile
+                profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS);
                 startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
                 return;
             case VpnProfile.TYPE_L2TP_IPSEC_PSK:
@@ -2213,12 +2226,27 @@
 
         @Override
         public void run() {
-            // Explicitly use only the network that ConnectivityService thinks is the "best." In
-            // other words, only ever use the currently selected default network. This does mean
-            // that in both onLost() and onConnected(), any old sessions MUST be torn down. This
-            // does NOT include VPNs.
+            // Unless the profile is restricted to test networks, explicitly use only the network
+            // that ConnectivityService thinks is the "best." In other words, only ever use the
+            // currently selected default network. This does mean that in both onLost() and
+            // onConnected(), any old sessions MUST be torn down. This does NOT include VPNs.
+            //
+            // When restricted to test networks, select any network with TRANSPORT_TEST. Since the
+            // creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS,
+            // this is considered safe.
             final ConnectivityManager cm = ConnectivityManager.from(mContext);
-            cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback);
+            final NetworkRequest req;
+
+            if (mProfile.isRestrictedToTestNetworks()) {
+                req = new NetworkRequest.Builder()
+                        .clearCapabilities()
+                        .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+                        .build();
+            } else {
+                req = cm.getDefaultRequest();
+            }
+
+            cm.requestNetwork(req, mNetworkCallback);
         }
 
         private boolean isActiveNetwork(@Nullable Network network) {
@@ -2248,12 +2276,16 @@
                 final String interfaceName = mTunnelIface.getInterfaceName();
                 final int maxMtu = mProfile.getMaxMtu();
                 final List<LinkAddress> internalAddresses = childConfig.getInternalAddresses();
+                final List<String> dnsAddrStrings = new ArrayList<>();
 
                 final Collection<RouteInfo> newRoutes = VpnIkev2Utils.getRoutesFromTrafficSelectors(
                         childConfig.getOutboundTrafficSelectors());
                 for (final LinkAddress address : internalAddresses) {
                     mTunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
                 }
+                for (InetAddress addr : childConfig.getInternalDnsServers()) {
+                    dnsAddrStrings.add(addr.getHostAddress());
+                }
 
                 final NetworkAgent networkAgent;
                 final LinkProperties lp;
@@ -2269,7 +2301,9 @@
                     mConfig.routes.clear();
                     mConfig.routes.addAll(newRoutes);
 
-                    // TODO: Add DNS servers from negotiation
+                    if (mConfig.dnsServers == null) mConfig.dnsServers = new ArrayList<>();
+                    mConfig.dnsServers.clear();
+                    mConfig.dnsServers.addAll(dnsAddrStrings);
 
                     networkAgent = mNetworkAgent;
 
@@ -2353,7 +2387,7 @@
                     final IkeSessionParams ikeSessionParams =
                             VpnIkev2Utils.buildIkeSessionParams(mContext, mProfile, network);
                     final ChildSessionParams childSessionParams =
-                            VpnIkev2Utils.buildChildSessionParams();
+                            VpnIkev2Utils.buildChildSessionParams(mProfile.getAllowedAlgorithms());
 
                     // TODO: Remove the need for adding two unused addresses with
                     // IPsec tunnels.
@@ -2563,7 +2597,7 @@
         public void exitIfOuterInterfaceIs(String interfaze) {
             if (interfaze.equals(mOuterInterface)) {
                 Log.i(TAG, "Legacy VPN is going down with " + interfaze);
-                exit();
+                exitVpnRunner();
             }
         }
 
@@ -2572,6 +2606,10 @@
         public void exitVpnRunner() {
             // We assume that everything is reset after stopping the daemons.
             interrupt();
+
+            // Always disconnect. This may be called again in cleanupVpnStateLocked() if
+            // exitVpnRunner() was called from exit(), but it will be a no-op.
+            agentDisconnect();
             try {
                 mContext.unregisterReceiver(mBroadcastReceiver);
             } catch (IllegalArgumentException e) {}
@@ -2794,7 +2832,7 @@
             } catch (Exception e) {
                 Log.i(TAG, "Aborting", e);
                 updateState(DetailedState.FAILED, e.getMessage());
-                exit();
+                exitVpnRunner();
             }
         }
 
@@ -2846,6 +2884,11 @@
         verifyCallingUidAndPackage(packageName);
         enforceNotRestrictedUser();
 
+        if (profile.isRestrictedToTestNetworks) {
+            mContext.enforceCallingPermission(Manifest.permission.MANAGE_TEST_NETWORKS,
+                    "Test-mode profiles require the MANAGE_TEST_NETWORKS permission");
+        }
+
         final byte[] encodedProfile = profile.encode();
         if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
             throw new IllegalArgumentException("Profile too big");
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index 3da304c..103f659 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -17,14 +17,12 @@
 package com.android.server.connectivity;
 
 import static android.net.ConnectivityManager.NetworkCallback;
-import static android.net.ipsec.ike.SaProposal.DH_GROUP_1024_BIT_MODP;
 import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
 import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
-import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96;
 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
 import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
@@ -39,6 +37,7 @@
 import android.net.Ikev2VpnProfile;
 import android.net.InetAddresses;
 import android.net.IpPrefix;
+import android.net.IpSecAlgorithm;
 import android.net.IpSecTransform;
 import android.net.Network;
 import android.net.RouteInfo;
@@ -83,6 +82,14 @@
  * @hide
  */
 public class VpnIkev2Utils {
+    private static final String TAG = VpnIkev2Utils.class.getSimpleName();
+
+    // TODO: Use IKE library exposed constants when @SystemApi is updated.
+    /** IANA-defined 3072 group for use in IKEv2 */
+    private static final int DH_GROUP_3072_BIT_MODP = 15;
+    /** IANA-defined 4096 group for use in IKEv2 */
+    private static final int DH_GROUP_4096_BIT_MODP = 16;
+
     static IkeSessionParams buildIkeSessionParams(
             @NonNull Context context, @NonNull Ikev2VpnProfile profile, @NonNull Network network) {
         final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity());
@@ -103,11 +110,11 @@
         return ikeOptionsBuilder.build();
     }
 
-    static ChildSessionParams buildChildSessionParams() {
+    static ChildSessionParams buildChildSessionParams(List<String> allowedAlgorithms) {
         final TunnelModeChildSessionParams.Builder childOptionsBuilder =
                 new TunnelModeChildSessionParams.Builder();
 
-        for (final ChildSaProposal childProposal : getChildSaProposals()) {
+        for (final ChildSaProposal childProposal : getChildSaProposals(allowedAlgorithms)) {
             childOptionsBuilder.addSaProposal(childProposal);
         }
 
@@ -144,7 +151,7 @@
     }
 
     private static List<IkeSaProposal> getIkeSaProposals() {
-        // TODO: filter this based on allowedAlgorithms
+        // TODO: Add ability to filter this when IKEv2 API is made Public API
         final List<IkeSaProposal> proposals = new ArrayList<>();
 
         // Encryption Algorithms: Currently only AES_CBC is supported.
@@ -160,7 +167,6 @@
         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
         normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
-        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
 
         // Add AEAD options
         final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder();
@@ -176,8 +182,9 @@
 
         // Add dh, prf for both builders
         for (final IkeSaProposal.Builder builder : Arrays.asList(normalModeBuilder, aeadBuilder)) {
+            builder.addDhGroup(DH_GROUP_4096_BIT_MODP);
+            builder.addDhGroup(DH_GROUP_3072_BIT_MODP);
             builder.addDhGroup(DH_GROUP_2048_BIT_MODP);
-            builder.addDhGroup(DH_GROUP_1024_BIT_MODP);
             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC);
             builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_HMAC_SHA1);
         }
@@ -187,38 +194,59 @@
         return proposals;
     }
 
-    private static List<ChildSaProposal> getChildSaProposals() {
-        // TODO: filter this based on allowedAlgorithms
+    /** Builds a child SA proposal based on the allowed IPsec algorithms */
+    private static List<ChildSaProposal> getChildSaProposals(List<String> allowedAlgorithms) {
         final List<ChildSaProposal> proposals = new ArrayList<>();
 
         // Add non-AEAD options
-        final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder();
+        if (Ikev2VpnProfile.hasNormalModeAlgorithms(allowedAlgorithms)) {
+            final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder();
 
-        // Encryption Algorithms: Currently only AES_CBC is supported.
-        normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
-        normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
-        normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
+            // Encryption Algorithms:
+            // AES-CBC is currently the only supported encryption algorithm.
+            normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
+            normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
+            normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
 
-        // Authentication/Integrity Algorithms
-        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
-        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
-        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
-        normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
+            // Authentication/Integrity Algorithms:
+            // Guaranteed by Ikev2VpnProfile constructor to contain at least one of these.
+            if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA512)) {
+                normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
+            }
+            if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA384)) {
+                normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
+            }
+            if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)) {
+                normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
+            }
+
+            ChildSaProposal proposal = normalModeBuilder.build();
+            if (proposal.getIntegrityAlgorithms().isEmpty()) {
+                // Should be impossible; Verified in Ikev2VpnProfile.
+                Log.wtf(TAG, "Missing integrity algorithm when buildling Child SA proposal");
+            } else {
+                proposals.add(normalModeBuilder.build());
+            }
+        }
 
         // Add AEAD options
-        final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder();
-        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
-        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
-        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
-        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
-        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
-        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
-        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
-        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
-        aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+        if (Ikev2VpnProfile.hasAeadAlgorithms(allowedAlgorithms)) {
+            final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder();
 
-        proposals.add(normalModeBuilder.build());
-        proposals.add(aeadBuilder.build());
+            // AES-GCM is currently the only supported AEAD algorithm
+            aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
+            aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
+            aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
+            aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
+            aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
+            aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
+            aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
+            aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
+            aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+
+            proposals.add(aeadBuilder.build());
+        }
+
         return proposals;
     }
 
diff --git a/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java b/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java
index 1cf27ff..cc7915c 100644
--- a/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java
+++ b/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java
@@ -20,90 +20,80 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings;
-import android.telephony.CellInfo;
-import android.telephony.CellInfoGsm;
-import android.telephony.CellInfoLte;
-import android.telephony.CellInfoWcdma;
-import android.telephony.CellLocation;
-import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Slog;
 
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.SystemService;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
- * A service that listens to connectivity and SIM card changes and determines if the emergency mode
- * should be enabled
+ * A service that listens to connectivity and SIM card changes and determines if the emergency
+ * affordance should be enabled.
  */
 public class EmergencyAffordanceService extends SystemService {
 
     private static final String TAG = "EmergencyAffordanceService";
+    private static final boolean DBG = false;
 
-    private static final int NUM_SCANS_UNTIL_ABORT = 4;
+    private static final String SERVICE_NAME = "emergency_affordance";
 
     private static final int INITIALIZE_STATE = 1;
-    private static final int CELL_INFO_STATE_CHANGED = 2;
-    private static final int SUBSCRIPTION_CHANGED = 3;
-
     /**
-     * Global setting, whether the last scan of the sim cards reveal that a sim was inserted that
-     * requires the emergency affordance. The value is a boolean (1 or 0).
-     * @hide
+     * @param arg1 slot Index
+     * @param arg2 0
+     * @param obj ISO country code
      */
-    private static final String EMERGENCY_SIM_INSERTED_SETTING = "emergency_sim_inserted_before";
+    private static final int NETWORK_COUNTRY_CHANGED = 2;
+    private static final int SUBSCRIPTION_CHANGED = 3;
+    private static final int UPDATE_AIRPLANE_MODE_STATUS = 4;
+
+    // Global Settings to override emergency affordance country ISO for debugging.
+    // Available only on debug build. The value is a country ISO string in lower case (eg. "us").
+    private static final String EMERGENCY_AFFORDANCE_OVERRIDE_ISO =
+            "emergency_affordance_override_iso";
 
     private final Context mContext;
-    private final ArrayList<Integer> mEmergencyCallMccNumbers;
-
-    private final Object mLock = new Object();
-
-    private TelephonyManager mTelephonyManager;
+    // Country ISOs that require affordance
+    private final ArrayList<String> mEmergencyCallCountryIsos;
     private SubscriptionManager mSubscriptionManager;
-    private boolean mEmergencyAffordanceNeeded;
+    private TelephonyManager mTelephonyManager;
     private MyHandler mHandler;
-    private int mScansCompleted;
-    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-        @Override
-        public void onCellInfoChanged(List<CellInfo> cellInfo) {
-            if (!isEmergencyAffordanceNeeded()) {
-                requestCellScan();
-            }
-        }
-
-        @Override
-        public void onCellLocationChanged(CellLocation location) {
-            if (!isEmergencyAffordanceNeeded()) {
-                requestCellScan();
-            }
-        }
-    };
-    private BroadcastReceiver mAirplaneModeReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Settings.Global.getInt(context.getContentResolver(),
-                    Settings.Global.AIRPLANE_MODE_ON, 0) == 0) {
-                startScanning();
-                requestCellScan();
-            }
-        }
-    };
-    private boolean mSimNeedsEmergencyAffordance;
-    private boolean mNetworkNeedsEmergencyAffordance;
+    private boolean mAnySimNeedsEmergencyAffordance;
+    private boolean mAnyNetworkNeedsEmergencyAffordance;
+    private boolean mEmergencyAffordanceNeeded;
+    private boolean mAirplaneModeEnabled;
     private boolean mVoiceCapable;
 
-    private void requestCellScan() {
-        mHandler.obtainMessage(CELL_INFO_STATE_CHANGED).sendToTarget();
-    }
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED.equals(intent.getAction())) {
+                String countryCode = intent.getStringExtra(TelephonyManager.EXTRA_NETWORK_COUNTRY);
+                int slotId = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
+                        SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+                mHandler.obtainMessage(
+                        NETWORK_COUNTRY_CHANGED, slotId, 0, countryCode).sendToTarget();
+            } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
+                mHandler.obtainMessage(UPDATE_AIRPLANE_MODE_STATUS).sendToTarget();
+            }
+        }
+    };
 
     private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener
             = new SubscriptionManager.OnSubscriptionsChangedListener() {
@@ -116,207 +106,200 @@
     public EmergencyAffordanceService(Context context) {
         super(context);
         mContext = context;
-        int[] numbers = context.getResources().getIntArray(
-                com.android.internal.R.array.config_emergency_mcc_codes);
-        mEmergencyCallMccNumbers = new ArrayList<>(numbers.length);
-        for (int i = 0; i < numbers.length; i++) {
-            mEmergencyCallMccNumbers.add(numbers[i]);
+        String[] isos = context.getResources().getStringArray(
+                com.android.internal.R.array.config_emergency_iso_country_codes);
+        mEmergencyCallCountryIsos = new ArrayList<>(isos.length);
+        for (String iso : isos) {
+            mEmergencyCallCountryIsos.add(iso);
         }
-    }
 
-    private void updateEmergencyAffordanceNeeded() {
-        synchronized (mLock) {
-            mEmergencyAffordanceNeeded = mVoiceCapable && (mSimNeedsEmergencyAffordance ||
-                    mNetworkNeedsEmergencyAffordance);
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Settings.Global.EMERGENCY_AFFORDANCE_NEEDED,
-                    mEmergencyAffordanceNeeded ? 1 : 0);
-            if (mEmergencyAffordanceNeeded) {
-                stopScanning();
+        if (Build.IS_DEBUGGABLE) {
+            String overrideIso = Settings.Global.getString(
+                    mContext.getContentResolver(), EMERGENCY_AFFORDANCE_OVERRIDE_ISO);
+            if (!TextUtils.isEmpty(overrideIso)) {
+                if (DBG) Slog.d(TAG, "Override ISO to " + overrideIso);
+                mEmergencyCallCountryIsos.clear();
+                mEmergencyCallCountryIsos.add(overrideIso);
             }
         }
     }
 
-    private void stopScanning() {
-        synchronized (mLock) {
-            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
-            mScansCompleted = 0;
-        }
-    }
-
-    private boolean isEmergencyAffordanceNeeded() {
-        synchronized (mLock) {
-            return mEmergencyAffordanceNeeded;
-        }
-    }
-
     @Override
     public void onStart() {
+        if (DBG) Slog.i(TAG, "onStart");
+        publishBinderService(SERVICE_NAME, new BinderService());
     }
 
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
-            mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
-            mVoiceCapable = mTelephonyManager.isVoiceCapable();
-            if (!mVoiceCapable) {
-                updateEmergencyAffordanceNeeded();
-                return;
-            }
-            mSubscriptionManager = SubscriptionManager.from(mContext);
-            HandlerThread thread = new HandlerThread(TAG);
-            thread.start();
-            mHandler = new MyHandler(thread.getLooper());
-            mHandler.obtainMessage(INITIALIZE_STATE).sendToTarget();
-            startScanning();
-            IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-            mContext.registerReceiver(mAirplaneModeReceiver, filter);
-            mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionChangedListener);
+            if (DBG) Slog.i(TAG, "onBootPhase");
+            handleThirdPartyBootPhase();
         }
     }
 
-    private void startScanning() {
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_INFO
-                | PhoneStateListener.LISTEN_CELL_LOCATION);
-    }
-
     /** Handler to do the heavier work on */
     private class MyHandler extends Handler {
-
         public MyHandler(Looper l) {
             super(l);
         }
 
         @Override
         public void handleMessage(Message msg) {
+            if (DBG) Slog.d(TAG, "handleMessage: " + msg.what);
             switch (msg.what) {
                 case INITIALIZE_STATE:
                     handleInitializeState();
                     break;
-                case CELL_INFO_STATE_CHANGED:
-                    handleUpdateCellInfo();
+                case NETWORK_COUNTRY_CHANGED:
+                    final String countryIso = (String) msg.obj;
+                    final int slotId = msg.arg1;
+                    handleNetworkCountryChanged(countryIso, slotId);
                     break;
                 case SUBSCRIPTION_CHANGED:
                     handleUpdateSimSubscriptionInfo();
                     break;
+                case UPDATE_AIRPLANE_MODE_STATUS:
+                    handleUpdateAirplaneModeStatus();
+                    break;
+                default:
+                    Slog.e(TAG, "Unexpected message received: " + msg.what);
             }
         }
     }
 
     private void handleInitializeState() {
-        if (handleUpdateSimSubscriptionInfo()) {
-            return;
-        }
-        if (handleUpdateCellInfo()) {
-            return;
-        }
+        if (DBG) Slog.d(TAG, "handleInitializeState");
+        handleUpdateAirplaneModeStatus();
+        handleUpdateSimSubscriptionInfo();
+        updateNetworkCountry();
         updateEmergencyAffordanceNeeded();
     }
 
-    private boolean handleUpdateSimSubscriptionInfo() {
-        boolean neededBefore = simNeededAffordanceBefore();
-        boolean neededNow = neededBefore;
+    private void handleThirdPartyBootPhase() {
+        if (DBG) Slog.d(TAG, "handleThirdPartyBootPhase");
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        mVoiceCapable = mTelephonyManager.isVoiceCapable();
+        if (!mVoiceCapable) {
+            updateEmergencyAffordanceNeeded();
+            return;
+        }
+
+        HandlerThread thread = new HandlerThread(TAG);
+        thread.start();
+        mHandler = new MyHandler(thread.getLooper());
+
+        mSubscriptionManager = SubscriptionManager.from(mContext);
+        mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionChangedListener);
+
+        IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
+        mContext.registerReceiver(mBroadcastReceiver, filter);
+
+        mHandler.obtainMessage(INITIALIZE_STATE).sendToTarget();
+    }
+
+    private void handleUpdateAirplaneModeStatus() {
+        mAirplaneModeEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+        if (DBG) Slog.d(TAG, "APM status updated to " + mAirplaneModeEnabled);
+    }
+
+    private void handleUpdateSimSubscriptionInfo() {
         List<SubscriptionInfo> activeSubscriptionInfoList =
                 mSubscriptionManager.getActiveSubscriptionInfoList();
+        if (DBG) Slog.d(TAG, "handleUpdateSimSubscriptionInfo: " + activeSubscriptionInfoList);
         if (activeSubscriptionInfoList == null) {
-            setSimNeedsEmergencyAffordance(neededNow);
-            return neededNow;
+            return;
         }
+
+        boolean needsAffordance = false;
         for (SubscriptionInfo info : activeSubscriptionInfoList) {
-            int mcc = info.getMcc();
-            if (mccRequiresEmergencyAffordance(mcc)) {
-                neededNow = true;
+            if (isoRequiresEmergencyAffordance(info.getCountryIso())) {
+                needsAffordance = true;
                 break;
-            } else if (mcc != 0 && mcc != Integer.MAX_VALUE){
-                // a Sim with a different mcc code was found
-                neededNow = false;
-            }
-            String simOperator = mTelephonyManager
-                    .createForSubscriptionId(info.getSubscriptionId()).getSimOperator();
-            mcc = 0;
-            if (simOperator != null && simOperator.length() >= 3) {
-                mcc = Integer.parseInt(simOperator.substring(0, 3));
-            }
-            if (mcc != 0) {
-                if (mccRequiresEmergencyAffordance(mcc)) {
-                    neededNow = true;
-                    break;
-                } else {
-                    // a Sim with a different mcc code was found
-                    neededNow = false;
-                }
             }
         }
-        setSimNeedsEmergencyAffordance(neededNow);
-        return neededNow;
+
+        mAnySimNeedsEmergencyAffordance = needsAffordance;
+        updateEmergencyAffordanceNeeded();
     }
 
-    private void setSimNeedsEmergencyAffordance(boolean simNeedsEmergencyAffordance) {
-        if (simNeededAffordanceBefore() != simNeedsEmergencyAffordance) {
+    private void handleNetworkCountryChanged(String countryIso, int slotId) {
+        if (DBG) {
+            Slog.d(TAG, "handleNetworkCountryChanged: countryIso=" + countryIso
+                    + ", slotId=" + slotId);
+        }
+
+        if (TextUtils.isEmpty(countryIso) && mAirplaneModeEnabled) {
+            Slog.w(TAG, "Ignore empty countryIso report when APM is on.");
+            return;
+        }
+
+        updateNetworkCountry();
+
+        updateEmergencyAffordanceNeeded();
+    }
+
+    private void updateNetworkCountry() {
+        boolean needsAffordance = false;
+
+        final int activeModems = mTelephonyManager.getActiveModemCount();
+        for (int i = 0; i < activeModems; i++) {
+            String countryIso = mTelephonyManager.getNetworkCountryIso(i);
+            if (DBG) Slog.d(TAG, "UpdateNetworkCountry: slotId=" + i + " countryIso=" + countryIso);
+            if (isoRequiresEmergencyAffordance(countryIso)) {
+                needsAffordance = true;
+                break;
+            }
+        }
+
+        mAnyNetworkNeedsEmergencyAffordance = needsAffordance;
+
+        updateEmergencyAffordanceNeeded();
+    }
+
+    private boolean isoRequiresEmergencyAffordance(String iso) {
+        return mEmergencyCallCountryIsos.contains(iso);
+    }
+
+    private void updateEmergencyAffordanceNeeded() {
+        if (DBG) {
+            Slog.d(TAG, "updateEmergencyAffordanceNeeded: mEmergencyAffordanceNeeded="
+                    + mEmergencyAffordanceNeeded + ", mVoiceCapable=" + mVoiceCapable
+                    + ", mAnySimNeedsEmergencyAffordance=" + mAnySimNeedsEmergencyAffordance
+                    + ", mAnyNetworkNeedsEmergencyAffordance="
+                    + mAnyNetworkNeedsEmergencyAffordance);
+        }
+        boolean lastAffordanceNeeded = mEmergencyAffordanceNeeded;
+
+        mEmergencyAffordanceNeeded = mVoiceCapable
+                && (mAnySimNeedsEmergencyAffordance || mAnyNetworkNeedsEmergencyAffordance);
+
+        if (lastAffordanceNeeded != mEmergencyAffordanceNeeded) {
             Settings.Global.putInt(mContext.getContentResolver(),
-                    EMERGENCY_SIM_INSERTED_SETTING,
-                    simNeedsEmergencyAffordance ? 1 : 0);
-        }
-        if (simNeedsEmergencyAffordance != mSimNeedsEmergencyAffordance) {
-            mSimNeedsEmergencyAffordance = simNeedsEmergencyAffordance;
-            updateEmergencyAffordanceNeeded();
+                    Settings.Global.EMERGENCY_AFFORDANCE_NEEDED,
+                    mEmergencyAffordanceNeeded ? 1 : 0);
         }
     }
 
-    private boolean simNeededAffordanceBefore() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                EMERGENCY_SIM_INSERTED_SETTING, 0) != 0;
+    private void dumpInternal(IndentingPrintWriter ipw) {
+        ipw.println("EmergencyAffordanceService (dumpsys emergency_affordance) state:\n");
+        ipw.println("mEmergencyAffordanceNeeded=" + mEmergencyAffordanceNeeded);
+        ipw.println("mVoiceCapable=" + mVoiceCapable);
+        ipw.println("mAnySimNeedsEmergencyAffordance=" + mAnySimNeedsEmergencyAffordance);
+        ipw.println("mAnyNetworkNeedsEmergencyAffordance=" + mAnyNetworkNeedsEmergencyAffordance);
+        ipw.println("mEmergencyCallCountryIsos=" + String.join(",", mEmergencyCallCountryIsos));
     }
 
-    private boolean handleUpdateCellInfo() {
-        List<CellInfo> cellInfos = mTelephonyManager.getAllCellInfo();
-        if (cellInfos == null) {
-            return false;
-        }
-        boolean stopScanningAfterScan = false;
-        for (CellInfo cellInfo : cellInfos) {
-            int mcc = 0;
-            if (cellInfo instanceof CellInfoGsm) {
-                mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMcc();
-            } else if (cellInfo instanceof CellInfoLte) {
-                mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMcc();
-            } else if (cellInfo instanceof CellInfoWcdma) {
-                mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMcc();
+    private final class BinderService extends Binder {
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
+                return;
             }
-            if (mccRequiresEmergencyAffordance(mcc)) {
-                setNetworkNeedsEmergencyAffordance(true);
-                return true;
-            } else if (mcc != 0 && mcc != Integer.MAX_VALUE) {
-                // we found an mcc that isn't in the list, abort
-                stopScanningAfterScan = true;
-            }
-        }
-        if (stopScanningAfterScan) {
-            stopScanning();
-        } else {
-            onCellScanFinishedUnsuccessful();
-        }
-        setNetworkNeedsEmergencyAffordance(false);
-        return false;
-    }
 
-    private void setNetworkNeedsEmergencyAffordance(boolean needsAffordance) {
-        synchronized (mLock) {
-            mNetworkNeedsEmergencyAffordance = needsAffordance;
-            updateEmergencyAffordanceNeeded();
+            dumpInternal(new IndentingPrintWriter(pw, "  "));
         }
     }
-
-    private void onCellScanFinishedUnsuccessful() {
-        synchronized (mLock) {
-            mScansCompleted++;
-            if (mScansCompleted >= NUM_SCANS_UNTIL_ABORT) {
-                stopScanning();
-            }
-        }
-    }
-
-    private boolean mccRequiresEmergencyAffordance(int mcc) {
-        return mEmergencyCallMccNumbers.contains(mcc);
-    }
 }
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 7c42cc2..d7bf37d 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -306,53 +306,6 @@
     static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
     static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
 
-    // Property name for the local device configurations.
-    // TODO(OEM): OEM should provide this property, and the value is the comma separated integer
-    //     values which denotes the device type in HDMI Spec 1.4.
-    static final String PROPERTY_DEVICE_TYPE = "ro.hdmi.device_type";
-
-    // TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
-    //            True by default.
-    static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
-
-    // TODO(OEM): Set this to true to enable 'Set Menu Language' feature. False by default.
-    static final String PROPERTY_SET_MENU_LANGUAGE = "ro.hdmi.set_menu_language";
-
-    /**
-     * Property to save the ARC port id on system audio device.
-     * <p>When ARC is initiated, this port will be used to turn on ARC.
-     */
-    static final String PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT =
-            "ro.hdmi.property_sytem_audio_device_arc_port";
-
-    /**
-     * Property to disable muting logic in System Audio Control handling. Default is true.
-     *
-     * <p>True means enabling muting logic.
-     * <p>False means never mute device.
-     */
-    static final String PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE =
-            "ro.hdmi.property_system_audio_mode_muting_enable";
-
-    /**
-     * When set to true the HdmiControlService will never request a Logical Address for the
-     * playback device type. Default is false.
-     *
-     * <p> This is useful when HDMI CEC multiple device types is not supported by the cec driver
-     */
-    static final String PROPERTY_HDMI_CEC_NEVER_CLAIM_PLAYBACK_LOGICAL_ADDRESS =
-            "ro.hdmi.property_hdmi_cec_never_claim_playback_logical_address";
-
-    /**
-     * A comma separated list of logical addresses that HdmiControlService
-     * will never assign local CEC devices to.
-     *
-     * <p> This is useful when HDMI CEC hardware module can't assign multiple logical addresses
-     * in the range same range of 0-7 or 8-15.
-     */
-    static final String PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES =
-            "ro.hdmi.property_hdmi_cec_never_assign_logical_addresses";
-
     // Set to false to allow playback device to go to suspend mode even
     // when it's an active source. True by default.
     static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 86be585..1530a07 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -22,7 +22,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
-import android.os.SystemProperties;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -117,18 +116,10 @@
 
     private final NativeWrapper mNativeWrapperImpl;
 
-    /** List of logical addresses that should not be assigned to the current device.
-     *
-     * <p>Parsed from {@link Constants#PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES}
-     */
-    private final List<Integer> mNeverAssignLogicalAddresses;
-
     // Private constructor.  Use HdmiCecController.create().
     private HdmiCecController(HdmiControlService service, NativeWrapper nativeWrapper) {
         mService = service;
         mNativeWrapperImpl = nativeWrapper;
-        mNeverAssignLogicalAddresses = mService.getIntList(SystemProperties.get(
-            Constants.PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES));
     }
 
     /**
@@ -221,8 +212,7 @@
         for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
             int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
             if (curAddress != Constants.ADDR_UNREGISTERED
-                    && deviceType == HdmiUtils.getTypeFromAddress(curAddress)
-                    && !mNeverAssignLogicalAddresses.contains(curAddress)) {
+                    && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
                 boolean acked = false;
                 for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
                     if (sendPollMessage(curAddress, curAddress, 1)) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 9e2fd4e..496273b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -35,6 +35,7 @@
 import android.media.tv.TvInputManager.TvInputCallback;
 import android.os.SystemProperties;
 import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -90,8 +91,7 @@
 
     // If the current device uses TvInput for ARC. We assume all other inputs also use TvInput
     // when ARC is using TvInput.
-    private boolean mArcIntentUsed = SystemProperties
-            .get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, "0").contains("tvinput");
+    private boolean mArcIntentUsed = HdmiProperties.arc_port().orElse("0").contains("tvinput");
 
     // Keeps the mapping (HDMI port ID to TV input URI) to keep track of the TV inputs ready to
     // accept input switching request from HDMI devices.
@@ -823,7 +823,7 @@
     private void enableAudioReturnChannel(boolean enabled) {
         assertRunOnServiceThread();
         mService.enableAudioReturnChannel(
-                SystemProperties.getInt(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, 0),
+                Integer.parseInt(HdmiProperties.arc_port().orElse("0")),
                 enabled);
     }
 
@@ -895,9 +895,7 @@
         boolean currentMuteStatus =
                 mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
         if (currentMuteStatus == newSystemAudioMode) {
-            if (mService.readBooleanSystemProperty(
-                    Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true)
-                            || newSystemAudioMode) {
+            if (HdmiProperties.system_audio_mode_muting().orElse(true) || newSystemAudioMode) {
                 mService.getAudioManager()
                         .adjustStreamVolume(
                                 AudioManager.STREAM_MUSIC,
@@ -1133,7 +1131,7 @@
         if (portId == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) {
             switchToHomeTvInput();
         } else if (portId == Constants.CEC_SWITCH_ARC) {
-            switchToTvInput(SystemProperties.get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT));
+            switchToTvInput(HdmiProperties.arc_port().orElse("0"));
             setLocalActivePort(portId);
             return;
         } else {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 560f7a0..603dfaf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -24,6 +24,7 @@
 import android.os.PowerManager.WakeLock;
 import android.os.SystemProperties;
 import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -43,11 +44,10 @@
 public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
     private static final String TAG = "HdmiCecLocalDevicePlayback";
 
-    private static final boolean WAKE_ON_HOTPLUG =
-            SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
+    private static final boolean WAKE_ON_HOTPLUG = false;
 
     private static final boolean SET_MENU_LANGUAGE =
-            SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false);
+            HdmiProperties.set_menu_language_enabled().orElse(false);
 
     // Used to keep the device awake while it is the active source. For devices that
     // cannot wake up via CEC commands, this address the inconvenience of having to
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index ae008b4..eb6612f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -16,12 +16,10 @@
 
 package com.android.server.hdmi;
 
-import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
-
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
-import android.os.SystemProperties;
+import android.sysprop.HdmiProperties;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -44,8 +42,7 @@
 
     // Device has cec switch functionality or not.
     // Default is false.
-    protected boolean mIsSwitchDevice = SystemProperties.getBoolean(
-            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+    protected boolean mIsSwitchDevice = HdmiProperties.is_switch().orElse(false);
 
     // Routing port number used for Routing Control.
     // This records the default routing port or the previous valid routing port.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index a122611..d8111ab 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -213,9 +213,11 @@
         mLocalDeviceAddresses = initLocalDeviceAddresses();
         resetSelectRequestBuffer();
         launchDeviceDiscovery();
+        if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
+            mService.sendCecCommand(HdmiCecMessageBuilder.buildRequestActiveSource(mAddress));
+        }
     }
 
-
     @ServiceThreadOnly
     private List<Integer> initLocalDeviceAddresses() {
         assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a83dd21..51d363a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -19,7 +19,6 @@
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
 
-import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
 import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
 import static com.android.server.hdmi.Constants.DISABLED;
 import static com.android.server.hdmi.Constants.ENABLED;
@@ -67,6 +66,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -94,6 +94,8 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Provides a service for sending and processing HDMI control messages,
@@ -152,10 +154,6 @@
     @GuardedBy("mLock")
     private boolean mSystemAudioActivated = false;
 
-    private static final boolean isHdmiCecNeverClaimPlaybackLogicAddr =
-            SystemProperties.getBoolean(
-                    Constants.PROPERTY_HDMI_CEC_NEVER_CLAIM_PLAYBACK_LOGICAL_ADDRESS, false);
-
     /**
      * Interface to report send result.
      */
@@ -435,7 +433,14 @@
 
     public HdmiControlService(Context context) {
         super(context);
-        mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
+        List<Integer> deviceTypes = HdmiProperties.device_type();
+        if (deviceTypes.contains(null)) {
+            Slog.w(TAG, "Error parsing ro.hdmi.device.type: " + SystemProperties.get(
+                    "ro.hdmi.device_type"));
+            deviceTypes = deviceTypes.stream().filter(Objects::nonNull).collect(
+                    Collectors.toList());
+        }
+        mLocalDevices = deviceTypes;
         mSettingsObserver = new SettingsObserver(mHandler);
     }
 
@@ -704,10 +709,6 @@
         // A container for [Device type, Local device info].
         ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
         for (int type : mLocalDevices) {
-            if (type == HdmiDeviceInfo.DEVICE_PLAYBACK
-                    && isHdmiCecNeverClaimPlaybackLogicAddr) {
-                continue;
-            }
             HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
             if (localDevice == null) {
                 localDevice = HdmiCecLocalDevice.create(this, type);
@@ -1105,10 +1106,6 @@
             }
             ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
             for (int type : mLocalDevices) {
-                if (type == HdmiDeviceInfo.DEVICE_PLAYBACK
-                        && isHdmiCecNeverClaimPlaybackLogicAddr) {
-                    continue;
-                }
                 HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
                 if (localDevice == null) {
                     localDevice = HdmiCecLocalDevice.create(this, type);
@@ -2522,8 +2519,7 @@
     }
 
     boolean isSwitchDevice() {
-        return SystemProperties.getBoolean(
-            PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+        return HdmiProperties.is_switch().orElse(false);
     }
 
     boolean isTvDeviceEnabled() {
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
new file mode 100644
index 0000000..25ef9fa
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -0,0 +1,6 @@
+set noparent
+
+ogunwale@google.com
+yukawa@google.com
+tarandeep@google.com
+lumark@google.com
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
index 86a84e3..18d9f69 100644
--- a/services/core/java/com/android/server/location/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -79,7 +79,7 @@
 
     // Represents an HAL interface version. Instances of this class are created in the JNI layer
     // and returned through native methods.
-    private static class HalInterfaceVersion {
+    static class HalInterfaceVersion {
         final int mMajor;
         final int mMinor;
 
@@ -205,6 +205,10 @@
         native_set_satellite_blacklist(constellations, svids);
     }
 
+    HalInterfaceVersion getHalInterfaceVersion() {
+        return native_get_gnss_configuration_version();
+    }
+
     interface SetCarrierProperty {
         boolean set(int value);
     }
@@ -231,8 +235,7 @@
 
         logConfigurations();
 
-        final HalInterfaceVersion gnssConfigurationIfaceVersion =
-                native_get_gnss_configuration_version();
+        final HalInterfaceVersion gnssConfigurationIfaceVersion = getHalInterfaceVersion();
         if (gnssConfigurationIfaceVersion != null) {
             // Set to a range checked value.
             if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion)
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 8d0397e..7782316 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -774,10 +774,15 @@
 
         locationRequest.setProvider(provider);
 
-        // Ignore location settings if in emergency mode.
-        if (isUserEmergency && mNIHandler.getInEmergency()) {
-            locationRequest.setLocationSettingsIgnored(true);
-            durationMillis *= EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER;
+        // Ignore location settings if in emergency mode. This is only allowed for
+        // isUserEmergency request (introduced in HAL v2.0), or DBH request in HAL v1.1.
+        if (mNIHandler.getInEmergency()) {
+            GnssConfiguration.HalInterfaceVersion halVersion =
+                    mGnssConfiguration.getHalInterfaceVersion();
+            if (isUserEmergency || (halVersion.mMajor < 2 && !independentFromGnss)) {
+                locationRequest.setLocationSettingsIgnored(true);
+                durationMillis *= EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER;
+            }
         }
 
         Log.i(TAG,
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 563dcf7..48f1ddb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -21,7 +21,7 @@
 import android.annotation.NonNull;
 import android.net.Network;
 import android.net.NetworkTemplate;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProvider;
 import android.telephony.SubscriptionPlan;
 
 import java.util.Set;
@@ -130,8 +130,8 @@
             Set<String> packageNames, int userId);
 
     /**
-     *  Notifies that the specified {@link AbstractNetworkStatsProvider} has reached its quota
-     *  which was set through {@link AbstractNetworkStatsProvider#setLimit(String, long)}.
+     *  Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+     *  which was set through {@link NetworkStatsProvider#onSetLimit(String, long)}.
      *
      * @param tag the human readable identifier of the custom network stats provider.
      */
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 10cf250..22ed661 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -75,7 +75,7 @@
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.net.netstats.provider.AbstractNetworkStatsProvider.QUOTA_UNLIMITED;
+import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
 import static android.os.Trace.TRACE_TAG_NETWORK;
 import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
 import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED;
@@ -1869,8 +1869,11 @@
                 mNetIdToSubId.put(state.network.netId, parseSubId(state));
             }
             if (state.networkInfo != null && state.networkInfo.isConnected()) {
+                // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
+                // in the object created here is never used and its value doesn't matter, so use
+                // NETWORK_TYPE_UNKNOWN.
                 final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
-                        true);
+                        true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
                 identified.put(state, ident);
             }
         }
@@ -3088,31 +3091,64 @@
     private void enforceSubscriptionPlanValidity(SubscriptionPlan[] plans) {
         // nothing to check if no plans
         if (plans.length == 0) {
+            Log.d(TAG, "Received empty plans list. Clearing existing SubscriptionPlans.");
             return;
         }
 
-        long applicableNetworkTypes = 0;
-        boolean allNetworks = false;
-        for (SubscriptionPlan plan : plans) {
-            if (plan.getNetworkTypes() == null) {
-                allNetworks = true;
+        final int[] allNetworkTypes = TelephonyManager.getAllNetworkTypes();
+        final ArraySet<Integer> allNetworksSet = new ArraySet<>();
+        addAll(allNetworksSet, allNetworkTypes);
+
+        final ArraySet<Integer> applicableNetworkTypes = new ArraySet<>();
+        boolean hasGeneralPlan = false;
+        for (int i = 0; i < plans.length; i++) {
+            final int[] planNetworkTypes = plans[i].getNetworkTypes();
+            final ArraySet<Integer> planNetworksSet = new ArraySet<>();
+            for (int j = 0; j < planNetworkTypes.length; j++) {
+                // ensure all network types are valid
+                if (allNetworksSet.contains(planNetworkTypes[j])) {
+                    // ensure no duplicate network types in the same SubscriptionPlan
+                    if (!planNetworksSet.add(planNetworkTypes[j])) {
+                        throw new IllegalArgumentException(
+                                "Subscription plan contains duplicate network types.");
+                    }
+                } else {
+                    throw new IllegalArgumentException("Invalid network type: "
+                            + planNetworkTypes[j]);
+                }
+            }
+
+            if (planNetworkTypes.length == allNetworkTypes.length) {
+                hasGeneralPlan = true;
             } else {
-                if ((applicableNetworkTypes & plan.getNetworkTypesBitMask()) != 0) {
+                // ensure no network type applies to multiple plans
+                if (!addAll(applicableNetworkTypes, planNetworkTypes)) {
                     throw new IllegalArgumentException(
                             "Multiple subscription plans defined for a single network type.");
-                } else {
-                    applicableNetworkTypes |= plan.getNetworkTypesBitMask();
                 }
             }
         }
 
         // ensure at least one plan applies for every network type
-        if (!allNetworks) {
+        if (!hasGeneralPlan) {
             throw new IllegalArgumentException(
                     "No generic subscription plan that applies to all network types.");
         }
     }
 
+    /**
+     * Adds all of the {@code elements} to the {@code set}.
+     *
+     * @return {@code false} if any element is not added because the set already has the value.
+     */
+    private static boolean addAll(@NonNull ArraySet<Integer> set, @NonNull int... elements) {
+        boolean result = true;
+        for (int i = 0; i < elements.length; i++) {
+            result &= set.add(elements[i]);
+        }
+        return result;
+    }
+
     @Override
     public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 22b01be..3dac106 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -229,7 +229,7 @@
                     entry.txPackets += reader.nextLong();
                 }
 
-                stats.addEntry(entry);
+                stats.insertEntry(entry);
                 reader.finishLine();
             }
         } catch (NullPointerException|NumberFormatException e) {
@@ -279,7 +279,7 @@
                 entry.txBytes = reader.nextLong();
                 entry.txPackets = reader.nextLong();
 
-                stats.addEntry(entry);
+                stats.insertEntry(entry);
                 reader.finishLine();
             }
         } catch (NullPointerException|NumberFormatException e) {
@@ -385,11 +385,10 @@
         // Migrate data usage over a VPN to the TUN network.
         for (VpnInfo info : vpnArray) {
             delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
+            // Filter out debug entries as that may lead to over counting.
+            delta.filterDebugEntries();
         }
 
-        // Filter out debug entries as that may lead to over counting.
-        delta.filterDebugEntries();
-
         // Update mTunAnd464xlatAdjustedStats with migrated delta.
         mTunAnd464xlatAdjustedStats.combineAllValues(delta);
         mTunAnd464xlatAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime());
@@ -439,7 +438,7 @@
                 if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
                         && (limitUid == UID_ALL || limitUid == entry.uid)
                         && (limitTag == TAG_ALL || limitTag == entry.tag)) {
-                    stats.addEntry(entry);
+                    stats.insertEntry(entry);
                 }
 
                 reader.finishLine();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 66e691a..5d1cc2a 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -17,14 +17,17 @@
 package com.android.server.net;
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_UID;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.ConnectivityManager.isNetworkTypeMobile;
+import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
 import static android.net.NetworkStack.checkNetworkStackPermission;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.IFACE_ALL;
@@ -47,6 +50,7 @@
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.os.Trace.TRACE_TAG_NETWORK;
 import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
+import static android.provider.Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED;
 import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION;
 import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE;
 import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES;
@@ -101,12 +105,13 @@
 import android.net.TrafficStats;
 import android.net.netstats.provider.INetworkStatsProvider;
 import android.net.netstats.provider.INetworkStatsProviderCallback;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
 import android.os.BestClock;
 import android.os.Binder;
 import android.os.DropBoxManager;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -114,7 +119,6 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.PowerManager;
-import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -123,8 +127,8 @@
 import android.provider.Settings.Global;
 import android.service.NetworkInterfaceProto;
 import android.service.NetworkStatsServiceDumpProto;
+import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionPlan;
-import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -155,6 +159,8 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
@@ -171,6 +177,10 @@
     private static final int MSG_PERFORM_POLL = 1;
     // Perform polling, persist network, and register the global alert again.
     private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2;
+    private static final int MSG_UPDATE_IFACES = 3;
+    // A message for broadcasting ACTION_NETWORK_STATS_UPDATED in handler thread to prevent
+    // deadlock.
+    private static final int MSG_BROADCAST_NETWORK_STATS_UPDATED = 4;
 
     /** Flags to control detail level of poll event. */
     private static final int FLAG_PERSIST_NETWORK = 0x1;
@@ -193,7 +203,6 @@
     private final NetworkStatsFactory mStatsFactory;
     private final AlarmManager mAlarmManager;
     private final Clock mClock;
-    private final TelephonyManager mTeleManager;
     private final NetworkStatsSettings mSettings;
     private final NetworkStatsObservers mStatsObservers;
 
@@ -218,21 +227,23 @@
     private static final String PREFIX_UID_TAG = "uid_tag";
 
     /**
-     * Virtual network interface for video telephony. This is for VT data usage counting purpose.
-     */
-    // TODO: Remove this after no one is using it.
-    public static final String VT_INTERFACE = NetworkStats.IFACE_VT;
-
-    /**
      * Settings that can be changed externally.
      */
     public interface NetworkStatsSettings {
-        public long getPollInterval();
-        public long getPollDelay();
-        public boolean getSampleEnabled();
-        public boolean getAugmentEnabled();
+        long getPollInterval();
+        long getPollDelay();
+        boolean getSampleEnabled();
+        boolean getAugmentEnabled();
+        /**
+         * When enabled, all mobile data is reported under {@link NetworkIdentity#SUBTYPE_COMBINED}.
+         * When disabled, mobile data is broken down by a granular subtype representative of the
+         * actual subtype. {@see NetworkTemplate#getCollapsedRatType}.
+         * Enabling this decreases the level of detail but saves performance, disk space and
+         * amount of data logged.
+         */
+        boolean getCombineSubtypeEnabled();
 
-        public static class Config {
+        class Config {
             public final long bucketDuration;
             public final long rotateAgeMillis;
             public final long deleteAgeMillis;
@@ -244,16 +255,16 @@
             }
         }
 
-        public Config getDevConfig();
-        public Config getXtConfig();
-        public Config getUidConfig();
-        public Config getUidTagConfig();
+        Config getDevConfig();
+        Config getXtConfig();
+        Config getUidConfig();
+        Config getUidTagConfig();
 
-        public long getGlobalAlertBytes(long def);
-        public long getDevPersistBytes(long def);
-        public long getXtPersistBytes(long def);
-        public long getUidPersistBytes(long def);
-        public long getUidTagPersistBytes(long def);
+        long getGlobalAlertBytes(long def);
+        long getDevPersistBytes(long def);
+        long getXtPersistBytes(long def);
+        long getUidPersistBytes(long def);
+        long getUidTagPersistBytes(long def);
     }
 
     private final Object mStatsLock = new Object();
@@ -278,12 +289,17 @@
     @GuardedBy("mStatsLock")
     private Network[] mDefaultNetworks = new Network[0];
 
+    /** Last states of all networks sent from ConnectivityService. */
+    @GuardedBy("mStatsLock")
+    @Nullable
+    private NetworkState[] mLastNetworkStates = null;
+
     private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
             new DropBoxNonMonotonicObserver();
 
     private static final int MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS = 100;
-    private final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList =
-            new RemoteCallbackList<>();
+    private final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList =
+            new CopyOnWriteArrayList<>();
     /** Semaphore used to wait for stats provider to respond to request stats update. */
     private final Semaphore mStatsProviderSem = new Semaphore(0, true);
 
@@ -326,6 +342,9 @@
     @NonNull
     private final Dependencies mDeps;
 
+    @NonNull
+    private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
+
     private static @NonNull File getDefaultSystemDir() {
         return new File(Environment.getDataDirectory(), "system");
     }
@@ -353,11 +372,24 @@
                     performPoll(FLAG_PERSIST_ALL);
                     break;
                 }
+                case MSG_UPDATE_IFACES: {
+                    // If no cached states, ignore.
+                    if (mLastNetworkStates == null) break;
+                    updateIfaces(mDefaultNetworks, mLastNetworkStates, mActiveIface);
+                    break;
+                }
                 case MSG_PERFORM_POLL_REGISTER_ALERT: {
                     performPoll(FLAG_PERSIST_NETWORK);
                     registerGlobalAlert();
                     break;
                 }
+                case MSG_BROADCAST_NETWORK_STATS_UPDATED: {
+                    final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
+                    updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    mContext.sendBroadcastAsUser(updatedIntent, UserHandle.ALL,
+                            READ_NETWORK_USAGE_HISTORY);
+                    break;
+                }
             }
         }
     }
@@ -369,8 +401,8 @@
         PowerManager.WakeLock wakeLock =
                 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
-        NetworkStatsService service = new NetworkStatsService(context, networkManager, alarmManager,
-                wakeLock, getDefaultClock(), context.getSystemService(TelephonyManager.class),
+        final NetworkStatsService service = new NetworkStatsService(context, networkManager,
+                alarmManager, wakeLock, getDefaultClock(),
                 new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(),
                 new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
                 new Dependencies());
@@ -384,16 +416,15 @@
     @VisibleForTesting
     NetworkStatsService(Context context, INetworkManagementService networkManager,
             AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
-            TelephonyManager teleManager, NetworkStatsSettings settings,
-            NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
-            File baseDir, @NonNull Dependencies deps) {
+            NetworkStatsSettings settings, NetworkStatsFactory factory,
+            NetworkStatsObservers statsObservers, File systemDir, File baseDir,
+            @NonNull Dependencies deps) {
         mContext = Objects.requireNonNull(context, "missing Context");
         mNetworkManager = Objects.requireNonNull(networkManager,
-            "missing INetworkManagementService");
+                "missing INetworkManagementService");
         mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager");
         mClock = Objects.requireNonNull(clock, "missing Clock");
         mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings");
-        mTeleManager = Objects.requireNonNull(teleManager, "missing TelephonyManager");
         mWakeLock = Objects.requireNonNull(wakeLock, "missing WakeLock");
         mStatsFactory = Objects.requireNonNull(factory, "missing factory");
         mStatsObservers = Objects.requireNonNull(statsObservers, "missing NetworkStatsObservers");
@@ -405,6 +436,8 @@
         final HandlerThread handlerThread = mDeps.makeHandlerThread();
         handlerThread.start();
         mHandler = new NetworkStatsHandler(handlerThread.getLooper());
+        mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
+                new HandlerExecutor(mHandler), this);
     }
 
     /**
@@ -420,6 +453,19 @@
         public HandlerThread makeHandlerThread() {
             return new HandlerThread(TAG);
         }
+
+        /**
+         * Create a {@link NetworkStatsSubscriptionsMonitor}, can be used to monitor RAT change
+         * event in NetworkStatsService.
+         */
+        @NonNull
+        public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(@NonNull Context context,
+                @NonNull Executor executor, @NonNull NetworkStatsService service) {
+            // TODO: Update RatType passively in NSS, instead of querying into the monitor
+            //  when forceUpdateIface.
+            return new NetworkStatsSubscriptionsMonitor(context, executor, (subscriberId, type) ->
+                    service.handleOnCollapsedRatTypeChanged());
+        }
     }
 
     private void registerLocalService() {
@@ -484,6 +530,12 @@
         mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
                 mSettings.getPollInterval(), pollIntent);
 
+        // TODO: listen to settings changed to support dynamically enable/disable.
+        // watch for networkType changes
+        if (!mSettings.getCombineSubtypeEnabled()) {
+            mNetworkStatsSubscriptionsMonitor.start();
+        }
+
         registerGlobalAlert();
     }
 
@@ -504,6 +556,10 @@
         mContext.unregisterReceiver(mUserReceiver);
         mContext.unregisterReceiver(mShutdownReceiver);
 
+        if (!mSettings.getCombineSubtypeEnabled()) {
+            mNetworkStatsSubscriptionsMonitor.stop();
+        }
+
         final long currentTime = mClock.millis();
 
         // persist any pending stats
@@ -556,7 +612,7 @@
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
-        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setAlert(mGlobalAlertBytes));
+        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetAlert(mGlobalAlertBytes));
     }
 
     @Override
@@ -757,7 +813,7 @@
         final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
 
         final NetworkStats stats = new NetworkStats(end - start, 1);
-        stats.addEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
+        stats.insertEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
                 METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets,
                 entry.txBytes, entry.txPackets, entry.operations));
         return stats;
@@ -1154,6 +1210,17 @@
         }
     };
 
+    /**
+     * Handle collapsed RAT type changed event.
+     */
+    @VisibleForTesting
+    public void handleOnCollapsedRatTypeChanged() {
+        // Protect service from frequently updating. Remove pending messages if any.
+        mHandler.removeMessages(MSG_UPDATE_IFACES);
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(MSG_UPDATE_IFACES), mSettings.getPollDelay());
+    }
+
     private void updateIfaces(
             Network[] defaultNetworks,
             NetworkState[] networkStates,
@@ -1175,7 +1242,8 @@
      * they are combined under a single {@link NetworkIdentitySet}.
      */
     @GuardedBy("mStatsLock")
-    private void updateIfacesLocked(Network[] defaultNetworks, NetworkState[] states) {
+    private void updateIfacesLocked(@Nullable Network[] defaultNetworks,
+            @NonNull NetworkState[] states) {
         if (!mSystemReady) return;
         if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
 
@@ -1195,13 +1263,18 @@
             mDefaultNetworks = defaultNetworks;
         }
 
+        mLastNetworkStates = states;
+
+        final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
         final ArraySet<String> mobileIfaces = new ArraySet<>();
         for (NetworkState state : states) {
             if (state.networkInfo.isConnected()) {
                 final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType());
                 final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
+                final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
+                        : getSubTypeForState(state);
                 final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
-                        isDefault);
+                        isDefault, subType);
 
                 // Traffic occurring on the base interface is always counted for
                 // both total usage and UID details.
@@ -1262,6 +1335,19 @@
         mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]);
     }
 
+    /**
+     * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through
+     * {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different
+     * transport types do not actually fill this value.
+     */
+    private int getSubTypeForState(@NonNull NetworkState state) {
+        if (!state.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+            return 0;
+        }
+
+        return mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(state.subscriberId);
+    }
+
     private static <K> NetworkIdentitySet findOrCreateNetworkIdentitySet(
             ArrayMap<K, NetworkIdentitySet> map, K key) {
         NetworkIdentitySet ident = map.get(key);
@@ -1369,12 +1455,17 @@
         final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;
 
         // Request asynchronous stats update from all providers for next poll. And wait a bit of
-        // time to allow providers report-in given that normally binder call should be fast.
+        // time to allow providers report-in given that normally binder call should be fast. Note
+        // that size of list might be changed because addition/removing at the same time. For
+        // addition, the stats of the missed provider can only be collected in next poll;
+        // for removal, wait might take up to MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS
+        // once that happened.
         // TODO: request with a valid token.
         Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
-        final int registeredCallbackCount = mStatsProviderCbList.getRegisteredCallbackCount();
+        final int registeredCallbackCount = mStatsProviderCbList.size();
         mStatsProviderSem.drainPermits();
-        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */));
+        invokeForAllStatsProviderCallbacks(
+                (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */));
         try {
             mStatsProviderSem.tryAcquire(registeredCallbackCount,
                     MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
@@ -1425,10 +1516,7 @@
         }
 
         // finally, dispatch updated event to any listeners
-        final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
-        updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mContext.sendBroadcastAsUser(updatedIntent, UserHandle.ALL,
-                READ_NETWORK_USAGE_HISTORY);
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_BROADCAST_NETWORK_STATS_UPDATED));
 
         Trace.traceEnd(TRACE_TAG_NETWORK);
     }
@@ -1548,8 +1636,8 @@
 
         @Override
         public void setStatsProviderLimitAsync(@NonNull String iface, long quota) {
-            Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")");
-            invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota));
+            if (LOGV) Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")");
+            invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetLimit(iface, quota));
         }
     }
 
@@ -1614,6 +1702,12 @@
                 return;
             }
 
+            pw.println("Configs:");
+            pw.increaseIndent();
+            pw.printPair(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
+            pw.println();
+            pw.decreaseIndent();
+
             pw.println("Active interfaces:");
             pw.increaseIndent();
             for (int i = 0; i < mActiveIfaces.size(); i++) {
@@ -1793,6 +1887,24 @@
         }
     }
 
+    // TODO: It is copied from ConnectivityService, consider refactor these check permission
+    //  functions to a proper util.
+    private boolean checkAnyPermissionOf(String... permissions) {
+        for (String permission : permissions) {
+            if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void enforceAnyPermissionOf(String... permissions) {
+        if (!checkAnyPermissionOf(permissions)) {
+            throw new SecurityException("Requires one of the following permissions: "
+                    + String.join(", ", permissions) + ".");
+        }
+    }
+
     /**
      * Registers a custom provider of {@link android.net.NetworkStats} to combine the network
      * statistics that cannot be seen by the kernel to system. To unregister, invoke the
@@ -1800,23 +1912,22 @@
      *
      * @param tag a human readable identifier of the custom network stats provider.
      * @param provider the {@link INetworkStatsProvider} binder corresponding to the
-     *                 {@link android.net.netstats.provider.AbstractNetworkStatsProvider} to be
-     *                 registered.
+     *                 {@link NetworkStatsProvider} to be registered.
      *
-     * @return a binder interface of
-     *         {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be
-     *         used to report events to the system.
+     * @return a {@link INetworkStatsProviderCallback} binder
+     *         interface, which can be used to report events to the system.
      */
     public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider(
             @NonNull String tag, @NonNull INetworkStatsProvider provider) {
-        mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+        enforceAnyPermissionOf(NETWORK_STATS_PROVIDER,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         Objects.requireNonNull(provider, "provider is null");
         Objects.requireNonNull(tag, "tag is null");
         try {
             NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
                     tag, provider, mStatsProviderSem, mAlertObserver,
                     mStatsProviderCbList);
-            mStatsProviderCbList.register(callback);
+            mStatsProviderCbList.add(callback);
             Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
                     + getCallingUid() + "/" + getCallingPid());
             return callback;
@@ -1840,20 +1951,11 @@
 
     private void invokeForAllStatsProviderCallbacks(
             @NonNull ThrowingConsumer<NetworkStatsProviderCallbackImpl, RemoteException> task) {
-        synchronized (mStatsLock) {
-            final int length = mStatsProviderCbList.beginBroadcast();
+        for (final NetworkStatsProviderCallbackImpl cb : mStatsProviderCbList) {
             try {
-                for (int i = 0; i < length; i++) {
-                    final NetworkStatsProviderCallbackImpl cb =
-                            mStatsProviderCbList.getBroadcastItem(i);
-                    try {
-                        task.accept(cb);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Fail to broadcast to provider: " + cb.mTag, e);
-                    }
-                }
-            } finally {
-                mStatsProviderCbList.finishBroadcast();
+                task.accept(cb);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Fail to broadcast to provider: " + cb.mTag, e);
             }
         }
     }
@@ -1865,7 +1967,7 @@
         @NonNull final INetworkStatsProvider mProvider;
         @NonNull private final Semaphore mSemaphore;
         @NonNull final INetworkManagementEventObserver mAlertObserver;
-        @NonNull final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
+        @NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
 
         @NonNull private final Object mProviderStatsLock = new Object();
 
@@ -1879,7 +1981,7 @@
                 @NonNull String tag, @NonNull INetworkStatsProvider provider,
                 @NonNull Semaphore semaphore,
                 @NonNull INetworkManagementEventObserver alertObserver,
-                @NonNull RemoteCallbackList<NetworkStatsProviderCallbackImpl> cbList)
+                @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList)
                 throws RemoteException {
             mTag = tag;
             mProvider = provider;
@@ -1910,7 +2012,7 @@
         }
 
         @Override
-        public void onStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
+        public void notifyStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
                 @Nullable NetworkStats uidStats) {
             // TODO: 1. Use token to map ifaces to correct NetworkIdentity.
             //       2. Store the difference and store it directly to the recorder.
@@ -1922,12 +2024,12 @@
         }
 
         @Override
-        public void onAlertReached() throws RemoteException {
+        public void notifyAlertReached() throws RemoteException {
             mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */);
         }
 
         @Override
-        public void onLimitReached() {
+        public void notifyLimitReached() {
             Log.d(TAG, mTag + ": onLimitReached");
             LocalServices.getService(NetworkPolicyManagerInternal.class)
                     .onStatsProviderLimitReached(mTag);
@@ -1936,13 +2038,13 @@
         @Override
         public void binderDied() {
             Log.d(TAG, mTag + ": binderDied");
-            mStatsProviderCbList.unregister(this);
+            mStatsProviderCbList.remove(this);
         }
 
         @Override
         public void unregister() {
             Log.d(TAG, mTag + ": unregister");
-            mStatsProviderCbList.unregister(this);
+            mStatsProviderCbList.remove(this);
         }
 
     }
@@ -2025,6 +2127,10 @@
             return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true);
         }
         @Override
+        public boolean getCombineSubtypeEnabled() {
+            return getGlobalBoolean(NETSTATS_COMBINE_SUBTYPE_ENABLED, false);
+        }
+        @Override
         public Config getDevConfig() {
             return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
                     getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
new file mode 100644
index 0000000..16a63d54
--- /dev/null
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -0,0 +1,211 @@
+/*
+ * 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.net;
+
+import static android.net.NetworkTemplate.getCollapsedRatType;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.telephony.Annotation;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class that watches for events that are triggered per subscription.
+ */
+// TODO (b/152176562): Write tests to verify subscription changes generate corresponding
+//  register/unregister calls.
+public class NetworkStatsSubscriptionsMonitor extends
+        SubscriptionManager.OnSubscriptionsChangedListener {
+
+    /**
+     * Interface that this monitor uses to delegate event handling to NetworkStatsService.
+     */
+    public interface Delegate {
+        /**
+         * Notify that the collapsed RAT type has been changed for any subscription. The method
+         * will also be triggered for any existing sub when start and stop monitoring.
+         *
+         * @param subscriberId IMSI of the subscription.
+         * @param collapsedRatType collapsed RAT type.
+         *                         @see android.net.NetworkTemplate#getCollapsedRatType(int).
+         */
+        void onCollapsedRatTypeChanged(@NonNull String subscriberId,
+                @Annotation.NetworkType int collapsedRatType);
+    }
+    private final Delegate mDelegate;
+
+    /**
+     * Receivers that watches for {@link ServiceState} changes for each subscription, to
+     * monitor the transitioning between Radio Access Technology(RAT) types for each sub.
+     */
+    @NonNull
+    private final CopyOnWriteArrayList<RatTypeListener> mRatListeners =
+            new CopyOnWriteArrayList<>();
+
+    @NonNull
+    private final SubscriptionManager mSubscriptionManager;
+    @NonNull
+    private final TelephonyManager mTeleManager;
+
+    @NonNull
+    private final Executor mExecutor;
+
+    NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Executor executor,
+            @NonNull Delegate delegate) {
+        super();
+        mSubscriptionManager = (SubscriptionManager) context.getSystemService(
+                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        mExecutor = executor;
+        mDelegate = delegate;
+    }
+
+    @Override
+    public void onSubscriptionsChanged() {
+        // Collect active subId list, hidden subId such as opportunistic subscriptions are
+        // also needed to track CBRS.
+        final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);
+
+        for (final int subId : newSubs) {
+            final RatTypeListener match = CollectionUtils.find(mRatListeners,
+                    it -> it.mSubId == subId);
+            if (match != null) continue;
+
+            // Create listener for every newly added sub. Also store subscriberId into it to
+            // prevent binder call to telephony when querying RAT.
+            final String subscriberId = mTeleManager.getSubscriberId(subId);
+            if (TextUtils.isEmpty(subscriberId)) {
+                Log.wtf(NetworkStatsService.TAG,
+                        "Empty subscriberId for newly added sub: " + subId);
+            }
+            final RatTypeListener listener =
+                    new RatTypeListener(mExecutor, this, subId, subscriberId);
+            mRatListeners.add(listener);
+
+            // Register listener to the telephony manager that associated with specific sub.
+            mTeleManager.createForSubscriptionId(subId)
+                    .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
+        }
+
+        for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
+            // If the new list contains the subId of the listener, keeps it.
+            final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId);
+            if (match != null) continue;
+
+            handleRemoveRatTypeListener(listener);
+        }
+    }
+
+    @NonNull
+    private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) {
+        final ArrayList<Integer> ret = new ArrayList<>();
+        final int[] ids = subscriptionManager.getActiveAndHiddenSubscriptionIdList();
+        for (int id : ids) ret.add(id);
+        return ret;
+    }
+
+    /**
+     * Get a collapsed RatType for the given subscriberId.
+     *
+     * @param subscriberId the target subscriberId
+     * @return collapsed RatType for the given subscriberId
+     */
+    public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
+        final RatTypeListener match = CollectionUtils.find(mRatListeners,
+                it -> TextUtils.equals(subscriberId, it.mSubscriberId));
+        return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    }
+
+    /**
+     * Start monitoring events that triggered per subscription.
+     */
+    public void start() {
+        mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, this);
+    }
+
+    /**
+     * Unregister subscription changes and all listeners for each subscription.
+     */
+    public void stop() {
+        mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
+
+        for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
+            handleRemoveRatTypeListener(listener);
+        }
+    }
+
+    private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
+        mTeleManager.createForSubscriptionId(listener.mSubId)
+                .listen(listener, PhoneStateListener.LISTEN_NONE);
+        mRatListeners.remove(listener);
+
+        // Removal of subscriptions doesn't generate RAT changed event, fire it for every
+        // RatTypeListener.
+        mDelegate.onCollapsedRatTypeChanged(
+                listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+    }
+
+    static class RatTypeListener extends PhoneStateListener {
+        // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}.
+        @NonNull
+        private final int mSubId;
+
+        // IMSI to identifying the corresponding network from {@link NetworkState}.
+        // See {@link TelephonyManager#getSubscriberId}.
+        @NonNull
+        private final String mSubscriberId;
+
+        private volatile int mLastCollapsedRatType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        @NonNull
+        private final NetworkStatsSubscriptionsMonitor mMonitor;
+
+        RatTypeListener(@NonNull Executor executor,
+                @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
+                @NonNull String subscriberId) {
+            super(executor);
+            mSubId = subId;
+            mSubscriberId = subscriberId;
+            mMonitor = monitor;
+        }
+
+        @Override
+        public void onServiceStateChanged(@NonNull ServiceState ss) {
+            final int networkType = ss.getDataNetworkType();
+            final int collapsedRatType = getCollapsedRatType(networkType);
+            if (collapsedRatType == mLastCollapsedRatType) return;
+
+            if (NetworkStatsService.LOGD) {
+                Log.d(NetworkStatsService.TAG, "subtype changed for sub(" + mSubId + "): "
+                        + mLastCollapsedRatType + " -> " + collapsedRatType);
+            }
+            mLastCollapsedRatType = collapsedRatType;
+            mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0bd2967..2d39e91 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -920,7 +920,7 @@
                         () -> mAm.crashApplication(uid, initialPid, pkg, -1,
                             "Bad notification(tag=" + tag + ", id=" + id + ") posted from package "
                                 + pkg + ", crashing app(uid=" + uid + ", pid=" + initialPid + "): "
-                                + message));
+                                + message, true /* force */));
             }
         }
 
@@ -1643,7 +1643,7 @@
             ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
             UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm,
             IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps,
-            UserManager userManager) {
+            UserManager userManager, TelephonyManager telephonyManager) {
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1766,7 +1766,15 @@
         mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
 
         mUserProfiles.updateCache(getContext());
-        listenForCallState();
+
+        telephonyManager.listen(new PhoneStateListener() {
+            @Override
+            public void onCallStateChanged(int state, String incomingNumber) {
+                if (mCallState == state) return;
+                if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
+                mCallState = state;
+            }
+        }, PhoneStateListener.LISTEN_CALL_STATE);
 
         mSettingsObserver = new SettingsObserver(mHandler);
 
@@ -1825,7 +1833,8 @@
                 UriGrantsManager.getService(),
                 LocalServices.getService(UriGrantsManagerInternal.class),
                 (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE),
-                getContext().getSystemService(UserManager.class));
+                getContext().getSystemService(UserManager.class),
+                getContext().getSystemService(TelephonyManager.class));
 
         // register for various Intents
         IntentFilter filter = new IntentFilter();
@@ -6056,6 +6065,10 @@
         if (isInCall() || mScreenOn) {
             return false;
         }
+        // check current user
+        if (!isNotificationForCurrentUser(record)) {
+            return false;
+        }
 
         return true;
     }
@@ -7467,17 +7480,6 @@
         }
     }
 
-    private void listenForCallState() {
-        getContext().getSystemService(TelephonyManager.class).listen(new PhoneStateListener() {
-            @Override
-            public void onCallStateChanged(int state, String incomingNumber) {
-                if (mCallState == state) return;
-                if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
-                mCallState = state;
-            }
-        }, PhoneStateListener.LISTEN_CALL_STATE);
-    }
-
     /**
      * Generates a NotificationRankingUpdate from 'sbns', considering only
      * notifications visible to the given listener.
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index ebc4191..0e3268b 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -844,13 +844,13 @@
             final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
                     getNotificationPolicy(config));
             if (!config.equals(mConfig)) {
+                mConfig = config;
                 dispatchOnConfigChanged();
                 updateConsolidatedPolicy(reason);
             }
             if (policyChanged) {
                 dispatchOnPolicyChanged();
             }
-            mConfig = config;
             mHandler.postApplyConfig(config, reason, triggeringComponent, setRingerMode);
             return true;
         } catch (SecurityException e) {
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index cf5ec05..4bb17a2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -33,9 +33,9 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.os.Binder;
 import android.os.Environment;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.sysprop.ApexProperties;
 import android.util.Slog;
 
@@ -73,12 +73,7 @@
      */
     static ApexManager create(Context systemContext) {
         if (ApexProperties.updatable().orElse(false)) {
-            try {
-                return new ApexManagerImpl(systemContext, IApexService.Stub.asInterface(
-                        ServiceManager.getServiceOrThrow("apexservice")));
-            } catch (ServiceManager.ServiceNotFoundException e) {
-                throw new IllegalStateException("Required service apexservice not available");
-            }
+            return new ApexManagerImpl(systemContext);
         } else {
             return new ApexManagerFlattenedApex();
         }
@@ -207,7 +202,14 @@
      *
      * @return {@code true} upon success, {@code false} if any remote exception occurs
      */
-    abstract boolean abortActiveSession();
+    abstract boolean revertActiveSessions();
+
+    /**
+     * Abandons the staged session with the given sessionId.
+     *
+     * @return {@code true} upon success, {@code false} if any remote exception occurs
+     */
+    abstract boolean abortStagedSession(int sessionId) throws PackageManagerException;
 
     /**
      * Uninstalls given {@code apexPackage}.
@@ -240,8 +242,7 @@
      * APEX packages.
      */
     @VisibleForTesting
-    static class ApexManagerImpl extends ApexManager {
-        private final IApexService mApexService;
+    protected static class ApexManagerImpl extends ApexManager {
         private final Context mContext;
         private final Object mLock = new Object();
         /**
@@ -254,9 +255,8 @@
         @GuardedBy("mLock")
         private List<PackageInfo> mAllPackagesCache;
 
-        ApexManagerImpl(Context context, IApexService apexService) {
+        ApexManagerImpl(Context context) {
             mContext = context;
-            mApexService = apexService;
         }
 
         /**
@@ -279,10 +279,23 @@
             return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
         }
 
+        /**
+         * Retrieve the service from ServiceManager. If the service is not running, it will be
+         * started, and this function will block until it is ready.
+         */
+        @VisibleForTesting
+        protected IApexService waitForApexService() {
+            try {
+                return IApexService.Stub.asInterface(Binder.waitForService("apexservice"));
+            } catch (RemoteException e) {
+                throw new IllegalStateException("Required service apexservice not available");
+            }
+        }
+
         @Override
         List<ActiveApexInfo> getActiveApexInfos() {
             try {
-                return Arrays.stream(mApexService.getActivePackages())
+                return Arrays.stream(waitForApexService().getActivePackages())
                         .map(apexInfo -> new ActiveApexInfo(
                                 new File(
                                 Environment.getApexDirectory() + File.separator
@@ -317,7 +330,7 @@
                     mAllPackagesCache = new ArrayList<>();
                     HashSet<String> activePackagesSet = new HashSet<>();
                     HashSet<String> factoryPackagesSet = new HashSet<>();
-                    final ApexInfo[] allPkgs = mApexService.getAllPackages();
+                    final ApexInfo[] allPkgs = waitForApexService().getAllPackages();
                     for (ApexInfo ai : allPkgs) {
                         // If the device is using flattened APEX, don't report any APEX
                         // packages since they won't be managed or updated by PackageManager.
@@ -424,7 +437,8 @@
         @Override
         @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
             try {
-                ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
+                ApexSessionInfo apexSessionInfo =
+                        waitForApexService().getStagedSessionInfo(sessionId);
                 if (apexSessionInfo.isUnknown) {
                     return null;
                 }
@@ -443,7 +457,7 @@
                 ApexSessionParams params = new ApexSessionParams();
                 params.sessionId = sessionId;
                 params.childSessionIds = childSessionIds;
-                mApexService.submitStagedSession(params, apexInfoList);
+                waitForApexService().submitStagedSession(params, apexInfoList);
                 return apexInfoList;
             } catch (RemoteException re) {
                 Slog.e(TAG, "Unable to contact apexservice", re);
@@ -458,7 +472,7 @@
         @Override
         void markStagedSessionReady(int sessionId) throws PackageManagerException {
             try {
-                mApexService.markStagedSessionReady(sessionId);
+                waitForApexService().markStagedSessionReady(sessionId);
             } catch (RemoteException re) {
                 Slog.e(TAG, "Unable to contact apexservice", re);
                 throw new RuntimeException(re);
@@ -472,7 +486,7 @@
         @Override
         void markStagedSessionSuccessful(int sessionId) {
             try {
-                mApexService.markStagedSessionSuccessful(sessionId);
+                waitForApexService().markStagedSessionSuccessful(sessionId);
             } catch (RemoteException re) {
                 Slog.e(TAG, "Unable to contact apexservice", re);
                 throw new RuntimeException(re);
@@ -489,20 +503,38 @@
         }
 
         @Override
-        boolean abortActiveSession() {
+        boolean revertActiveSessions() {
             try {
-                mApexService.abortActiveSession();
+                waitForApexService().revertActiveSessions();
                 return true;
             } catch (RemoteException re) {
                 Slog.e(TAG, "Unable to contact apexservice", re);
                 return false;
+            } catch (Exception e) {
+                Slog.e(TAG, e.getMessage(), e);
+                return false;
+            }
+        }
+
+        @Override
+        boolean abortStagedSession(int sessionId) throws PackageManagerException {
+            try {
+                waitForApexService().abortStagedSession(sessionId);
+                return true;
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Unable to contact apexservice", re);
+                return false;
+            } catch (Exception e) {
+                throw new PackageManagerException(
+                        PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                        "Failed to abort staged session : " + e.getMessage());
             }
         }
 
         @Override
         boolean uninstallApex(String apexPackagePath) {
             try {
-                mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
+                waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath));
                 return true;
             } catch (Exception e) {
                 return false;
@@ -553,7 +585,7 @@
                 ipw.increaseIndent();
                 ipw.println("APEX session state:");
                 ipw.increaseIndent();
-                final ApexSessionInfo[] sessions = mApexService.getSessions();
+                final ApexSessionInfo[] sessions = waitForApexService().getSessions();
                 for (ApexSessionInfo si : sessions) {
                     ipw.println("Session ID: " + si.sessionId);
                     ipw.increaseIndent();
@@ -569,12 +601,12 @@
                         ipw.println("State: ACTIVATION FAILED");
                     } else if (si.isSuccess) {
                         ipw.println("State: SUCCESS");
-                    } else if (si.isRollbackInProgress) {
-                        ipw.println("State: ROLLBACK IN PROGRESS");
-                    } else if (si.isRolledBack) {
-                        ipw.println("State: ROLLED BACK");
-                    } else if (si.isRollbackFailed) {
-                        ipw.println("State: ROLLBACK FAILED");
+                    } else if (si.isRevertInProgress) {
+                        ipw.println("State: REVERT IN PROGRESS");
+                    } else if (si.isReverted) {
+                        ipw.println("State: REVERTED");
+                    } else if (si.isRevertFailed) {
+                        ipw.println("State: REVERT FAILED");
                     }
                     ipw.decreaseIndent();
                 }
@@ -682,7 +714,12 @@
         }
 
         @Override
-        boolean abortActiveSession() {
+        boolean revertActiveSessions() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        boolean abortStagedSession(int sessionId) throws PackageManagerException {
             throw new UnsupportedOperationException();
         }
 
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index f8c173f..6fdde7a 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -30,8 +30,9 @@
 per-file CrossProfileAppsService.java = omakoto@google.com, yamasani@google.com
 per-file CrossProfileIntentFilter.java = omakoto@google.com, yamasani@google.com
 per-file CrossProfileIntentResolver.java = omakoto@google.com, yamasani@google.com
-per-file UserManagerService.java = omakoto@google.com, yamasani@google.com
+per-file UserManagerService.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
 per-file UserRestrictionsUtils.java = omakoto@google.com, rubinxu@google.com, sandness@google.com, yamasani@google.com
+per-file RestrictionsSet.java = bookatz@google.com, omakoto@google.com, yamasani@google.com, rubinxu@google.com, sandness@google.com
 per-file UserSystemPackageInstaller.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
 per-file UserTypeDetails.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
 per-file UserTypeFactory.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index fd8db4b..b217dce 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -26,7 +26,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PackageDeleteObserver;
-import android.app.PackageInstallObserver;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
@@ -994,73 +993,56 @@
         }
     }
 
-    static class PackageInstallObserverAdapter extends PackageInstallObserver {
-        private final Context mContext;
-        private final IntentSender mTarget;
-        private final int mSessionId;
-        private final boolean mShowNotification;
-        private final int mUserId;
-
-        public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
-                boolean showNotification, int userId) {
-            mContext = context;
-            mTarget = target;
-            mSessionId = sessionId;
-            mShowNotification = showNotification;
-            mUserId = userId;
+    static void sendOnUserActionRequired(Context context, IntentSender target, int sessionId,
+            Intent intent) {
+        final Intent fillIn = new Intent();
+        fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+        fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_PENDING_USER_ACTION);
+        fillIn.putExtra(Intent.EXTRA_INTENT, intent);
+        try {
+            target.sendIntent(context, 0, fillIn, null, null);
+        } catch (SendIntentException ignored) {
         }
+    }
 
-        @Override
-        public void onUserActionRequired(Intent intent) {
-            final Intent fillIn = new Intent();
-            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
-            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
-                    PackageInstaller.STATUS_PENDING_USER_ACTION);
-            fillIn.putExtra(Intent.EXTRA_INTENT, intent);
-            try {
-                mTarget.sendIntent(mContext, 0, fillIn, null, null);
-            } catch (SendIntentException ignored) {
+    static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
+            boolean showNotification, int userId, String basePackageName, int returnCode,
+            String msg, Bundle extras) {
+        if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) {
+            boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
+            Notification notification = buildSuccessNotification(context,
+                    context.getResources()
+                            .getString(update ? R.string.package_updated_device_owner :
+                                    R.string.package_installed_device_owner),
+                    basePackageName,
+                    userId);
+            if (notification != null) {
+                NotificationManager notificationManager = (NotificationManager)
+                        context.getSystemService(Context.NOTIFICATION_SERVICE);
+                notificationManager.notify(basePackageName,
+                        SystemMessage.NOTE_PACKAGE_STATE,
+                        notification);
             }
         }
-
-        @Override
-        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
-                Bundle extras) {
-            if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
-                boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
-                Notification notification = buildSuccessNotification(mContext,
-                        mContext.getResources()
-                                .getString(update ? R.string.package_updated_device_owner :
-                                        R.string.package_installed_device_owner),
-                        basePackageName,
-                        mUserId);
-                if (notification != null) {
-                    NotificationManager notificationManager = (NotificationManager)
-                            mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-                    notificationManager.notify(basePackageName,
-                            SystemMessage.NOTE_PACKAGE_STATE,
-                            notification);
-                }
+        final Intent fillIn = new Intent();
+        fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
+        fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+        fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+                PackageManager.installStatusToPublicStatus(returnCode));
+        fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+                PackageManager.installStatusToString(returnCode, msg));
+        fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+        if (extras != null) {
+            final String existing = extras.getString(
+                    PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
+            if (!TextUtils.isEmpty(existing)) {
+                fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
             }
-            final Intent fillIn = new Intent();
-            fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
-            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
-            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
-                    PackageManager.installStatusToPublicStatus(returnCode));
-            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
-                    PackageManager.installStatusToString(returnCode, msg));
-            fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
-            if (extras != null) {
-                final String existing = extras.getString(
-                        PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
-                if (!TextUtils.isEmpty(existing)) {
-                    fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
-                }
-            }
-            try {
-                mTarget.sendIntent(mContext, 0, fillIn, null, null);
-            } catch (SendIntentException ignored) {
-            }
+        }
+        try {
+            target.sendIntent(context, 0, fillIn, null, null);
+        } catch (SendIntentException ignored) {
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7091c7c..6f6c315 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -18,7 +18,6 @@
 
 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
@@ -79,7 +78,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelableException;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.RevocableFileDescriptor;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -107,7 +105,6 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.security.VerityUtils;
 
@@ -131,7 +128,7 @@
 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     private static final String TAG = "PackageInstallerSession";
     private static final boolean LOGD = true;
-    private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
+    private static final String REMOVE_MARKER_EXTENSION = ".removed";
 
     private static final int MSG_COMMIT = 1;
     private static final int MSG_ON_PACKAGE_INSTALLED = 2;
@@ -257,7 +254,7 @@
     private final ArrayList<FileBridge> mBridges = new ArrayList<>();
 
     @GuardedBy("mLock")
-    private IPackageInstallObserver2 mRemoteObserver;
+    private IntentSender mRemoteStatusReceiver;
 
     /** Fields derived from commit parsing */
     @GuardedBy("mLock")
@@ -294,9 +291,6 @@
     private File mResolvedBaseFile;
 
     @GuardedBy("mLock")
-    private File mResolvedStageDir;
-
-    @GuardedBy("mLock")
     private final List<File> mResolvedStagedFiles = new ArrayList<>();
     @GuardedBy("mLock")
     private final List<File> mResolvedInheritedFiles = new ArrayList<>();
@@ -315,7 +309,7 @@
             // Installers can't stage directories, so it's fine to ignore
             // entries like "lost+found".
             if (file.isDirectory()) return false;
-            if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
+            if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
             if (DexMetadataHelper.isDexMetadataFile(file)) return false;
             if (VerityUtils.isFsveritySignatureFile(file)) return false;
             return true;
@@ -325,7 +319,7 @@
         @Override
         public boolean accept(File file) {
             if (file.isDirectory()) return false;
-            if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
+            if (!file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
             return true;
         }
     };
@@ -342,14 +336,14 @@
                     final String packageName = (String) args.arg1;
                     final String message = (String) args.arg2;
                     final Bundle extras = (Bundle) args.arg3;
-                    final IPackageInstallObserver2 observer = (IPackageInstallObserver2) args.arg4;
+                    final IntentSender statusReceiver = (IntentSender) args.arg4;
                     final int returnCode = args.argi1;
                     args.recycle();
 
-                    try {
-                        observer.onPackageInstalled(packageName, returnCode, message, extras);
-                    } catch (RemoteException ignored) {
-                    }
+                    PackageInstallerService.sendOnPackageInstalled(mContext,
+                            statusReceiver, sessionId,
+                            isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId,
+                            packageName, returnCode, message, extras);
 
                     break;
             }
@@ -559,23 +553,6 @@
         }
     }
 
-    /**
-     * Resolve the actual location where staged data should be written. This
-     * might point at an ASEC mount point, which is why we delay path resolution
-     * until someone actively works with the session.
-     */
-    @GuardedBy("mLock")
-    private File resolveStageDirLocked() throws IOException {
-        if (mResolvedStageDir == null) {
-            if (stageDir != null) {
-                mResolvedStageDir = stageDir;
-            } else {
-                throw new IOException("Missing stageDir");
-            }
-        }
-        return mResolvedStageDir;
-    }
-
     @Override
     public void setClientProgress(float progress) {
         synchronized (mLock) {
@@ -615,14 +592,32 @@
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
 
-            try {
-                return resolveStageDirLocked().list();
-            } catch (IOException e) {
-                throw ExceptionUtils.wrap(e);
-            }
+            return getNamesLocked();
         }
     }
 
+    @GuardedBy("mLock")
+    private String[] getNamesLocked() {
+        return stageDir.list();
+    }
+
+    private static File[] filterFiles(File parent, String[] names, FileFilter filter) {
+        return Arrays.stream(names).map(name -> new File(parent, name)).filter(
+                file -> filter.accept(file)).toArray(File[]::new);
+    }
+
+    @GuardedBy("mLock")
+    private File[] getAddedFilesLocked() {
+        String[] names = getNamesLocked();
+        return filterFiles(stageDir, names, sAddedFilter);
+    }
+
+    @GuardedBy("mLock")
+    private File[] getRemovedFilesLocked() {
+        String[] names = getNamesLocked();
+        return filterFiles(stageDir, names, sRemovedFilter);
+    }
+
     @Override
     public void removeSplit(String splitName) {
         if (TextUtils.isEmpty(params.appPackageName)) {
@@ -641,13 +636,17 @@
         }
     }
 
+    private static String getRemoveMarkerName(String name) {
+        final String markerName = name + REMOVE_MARKER_EXTENSION;
+        if (!FileUtils.isValidExtFilename(markerName)) {
+            throw new IllegalArgumentException("Invalid marker: " + markerName);
+        }
+        return markerName;
+    }
+
     private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
         try {
-            final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
-            if (!FileUtils.isValidExtFilename(markerName)) {
-                throw new IllegalArgumentException("Invalid marker: " + markerName);
-            }
-            final File target = new File(resolveStageDirLocked(), markerName);
+            final File target = new File(stageDir, getRemoveMarkerName(splitName));
             target.createNewFile();
             Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
         } catch (ErrnoException e) {
@@ -681,7 +680,6 @@
         // will block any attempted install transitions.
         final RevocableFileDescriptor fd;
         final FileBridge bridge;
-        final File stageDir;
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("openWrite");
@@ -695,8 +693,6 @@
                 bridge = new FileBridge();
                 mBridges.add(bridge);
             }
-
-            stageDir = resolveStageDirLocked();
         }
 
         try {
@@ -802,7 +798,7 @@
             if (!FileUtils.isValidExtFilename(name)) {
                 throw new IllegalArgumentException("Invalid name: " + name);
             }
-            final File target = new File(resolveStageDirLocked(), name);
+            final File target = new File(stageDir, name);
             final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), O_RDONLY, 0);
             return new ParcelFileDescriptor(targetFd);
         } catch (ErrnoException e) {
@@ -948,7 +944,7 @@
      * This method may be called multiple times to update the status receiver validate caller
      * permissions.
      */
-    public boolean markAsCommitted(
+    private boolean markAsCommitted(
             @NonNull IntentSender statusReceiver, boolean forTransfer) {
         Preconditions.checkNotNull(statusReceiver);
 
@@ -959,10 +955,7 @@
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotDestroyedLocked("commit");
 
-            final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
-                    mContext, statusReceiver, sessionId,
-                    isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId);
-            mRemoteObserver = adapter.getBinder();
+            mRemoteStatusReceiver = statusReceiver;
 
             if (forTransfer) {
                 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
@@ -986,12 +979,7 @@
             if (!mSealed) {
                 try {
                     sealAndValidateLocked(childSessions);
-                } catch (IOException e) {
-                    throw new IllegalArgumentException(e);
                 } catch (PackageManagerException e) {
-                    // Do now throw an exception here to stay compatible with O and older
-                    destroyInternal();
-                    dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
                     return false;
                 }
             }
@@ -1091,52 +1079,50 @@
      */
     @GuardedBy("mLock")
     private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
-            throws PackageManagerException, IOException {
-        assertNoWriteFileTransfersOpenLocked();
-        assertPreparedAndNotDestroyedLocked("sealing of session");
+            throws PackageManagerException {
+        try {
+            assertNoWriteFileTransfersOpenLocked();
+            assertPreparedAndNotDestroyedLocked("sealing of session");
 
-        mSealed = true;
+            mSealed = true;
 
-        if (childSessions != null) {
-            assertMultiPackageConsistencyLocked(childSessions);
-        }
-
-        if (params.isStaged) {
-            final PackageInstallerSession activeSession = mStagingManager.getActiveSession();
-            final boolean anotherSessionAlreadyInProgress =
-                    activeSession != null && sessionId != activeSession.sessionId
-                            && mParentSessionId != activeSession.sessionId;
-            if (anotherSessionAlreadyInProgress) {
-                throw new PackageManagerException(
-                        PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
-                        "There is already in-progress committed staged session "
-                                + activeSession.sessionId, null);
+            if (childSessions != null) {
+                assertMultiPackageConsistencyLocked(childSessions);
             }
-        }
 
-        // Read transfers from the original owner stay open, but as the session's data
-        // cannot be modified anymore, there is no leak of information. For staged sessions,
-        // further validation is performed by the staging manager.
-        if (!params.isMultiPackage) {
-            final PackageInfo pkgInfo = mPm.getPackageInfo(
-                    params.appPackageName, PackageManager.GET_SIGNATURES
-                            | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+            // Read transfers from the original owner stay open, but as the session's data
+            // cannot be modified anymore, there is no leak of information. For staged sessions,
+            // further validation is performed by the staging manager.
+            if (!params.isMultiPackage) {
+                final PackageInfo pkgInfo = mPm.getPackageInfo(
+                        params.appPackageName, PackageManager.GET_SIGNATURES
+                                | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
 
-            resolveStageDirLocked();
-
-            try {
-                if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
-                    validateApexInstallLocked();
-                } else {
-                    validateApkInstallLocked(pkgInfo);
+                try {
+                    if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+                        validateApexInstallLocked();
+                    } else {
+                        validateApkInstallLocked(pkgInfo);
+                    }
+                } catch (PackageManagerException e) {
+                    throw e;
+                } catch (Throwable e) {
+                    // Convert all exceptions into package manager exceptions as only those are
+                    // handled in the code above.
+                    throw new PackageManagerException(e);
                 }
-            } catch (PackageManagerException e) {
-                throw e;
-            } catch (Throwable e) {
-                // Convert all exceptions into package manager exceptions as only those are handled
-                // in the code above
-                throw new PackageManagerException(e);
             }
+
+            if (params.isStaged) {
+                mStagingManager.checkNonOverlappingWithStagedSessions(this);
+            }
+        } catch (PackageManagerException e) {
+            // Session is sealed but could not be verified, we need to destroy it.
+            destroyInternal();
+            // Dispatch message to remove session from PackageInstallerService
+            dispatchSessionFinished(
+                    e.error, ExceptionUtils.getCompleteMessage(e), null);
+            throw e;
         }
     }
 
@@ -1159,15 +1145,8 @@
         synchronized (mLock) {
             try {
                 sealAndValidateLocked(childSessions);
-            } catch (IOException e) {
-                throw new IllegalStateException(e);
             } catch (PackageManagerException e) {
                 Slog.e(TAG, "Package not valid", e);
-                // Session is sealed but could not be verified, we need to destroy it.
-                destroyInternal();
-                // Dispatch message to remove session from PackageInstallerService
-                dispatchSessionFinished(
-                        e.error, ExceptionUtils.getCompleteMessage(e), null);
             }
         }
     }
@@ -1208,13 +1187,7 @@
 
             try {
                 sealAndValidateLocked(childSessions);
-            } catch (IOException e) {
-                throw new IllegalStateException(e);
             } catch (PackageManagerException e) {
-                // Session is sealed but could not be verified, we need to destroy it
-                destroyInternal();
-                dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
-
                 throw new IllegalArgumentException("Package is not valid", e);
             }
 
@@ -1299,11 +1272,10 @@
                 }
             }
             if (!success) {
-                try {
-                    mRemoteObserver.onPackageInstalled(
-                            null, failure.error, failure.getLocalizedMessage(), null);
-                } catch (RemoteException ignored) {
-                }
+                PackageInstallerService.sendOnPackageInstalled(mContext,
+                        mRemoteStatusReceiver, sessionId,
+                        isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
+                        failure.error, failure.getLocalizedMessage(), null);
                 return;
             }
             mPm.installStage(activeChildSessions);
@@ -1347,10 +1319,9 @@
                     final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
                     intent.setPackage(mPm.getPackageInstallerPackageName());
                     intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-                    try {
-                        mRemoteObserver.onUserActionRequired(intent);
-                    } catch (RemoteException ignored) {
-                    }
+
+                    PackageInstallerService.sendOnUserActionRequired(mContext,
+                            mRemoteStatusReceiver, sessionId, intent);
 
                     // Commit was keeping session marked as active until now; release
                     // that extra refcount so session appears idle.
@@ -1363,7 +1334,7 @@
                 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
                     try {
                         final List<File> fromFiles = mResolvedInheritedFiles;
-                        final File toDir = resolveStageDirLocked();
+                        final File toDir = stageDir;
 
                         if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
                         if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
@@ -1413,8 +1384,7 @@
                 computeProgressLocked(true);
 
                 // Unpack native libraries
-                extractNativeLibraries(mResolvedStageDir, params.abiOverride,
-                        mayInheritNativeLibs());
+                extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
             }
 
             // We've reached point of no return; call into PMS to install the stage.
@@ -1470,12 +1440,13 @@
     /**
      * Validate apex install.
      * <p>
-     * Sets {@link #mResolvedBaseFile} for RollbackManager to use.
+     * Sets {@link #mResolvedBaseFile} for RollbackManager to use. Sets {@link #mPackageName} for
+     * StagingManager to use.
      */
     @GuardedBy("mLock")
     private void validateApexInstallLocked()
             throws PackageManagerException {
-        final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
+        final File[] addedFiles = getAddedFilesLocked();
         if (ArrayUtils.isEmpty(addedFiles)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
         }
@@ -1485,13 +1456,6 @@
                     "Too many files for apex install");
         }
 
-        try {
-            resolveStageDirLocked();
-        } catch (IOException e) {
-            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
-                    "Failed to resolve stage location", e);
-        }
-
         File addedFile = addedFiles[0]; // there is only one file
 
         // Ensure file name has proper suffix
@@ -1504,10 +1468,24 @@
                     "Invalid filename: " + targetName);
         }
 
-        final File targetFile = new File(mResolvedStageDir, targetName);
+        final File targetFile = new File(stageDir, targetName);
         resolveAndStageFile(addedFile, targetFile);
-
         mResolvedBaseFile = targetFile;
+
+        // Populate package name of the apex session
+        mPackageName = null;
+        final ApkLite apk;
+        try {
+            apk = PackageParser.parseApkLite(
+                    mResolvedBaseFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+        } catch (PackageParserException e) {
+            throw PackageManagerException.from(e);
+        }
+
+        if (mPackageName == null) {
+            mPackageName = apk.packageName;
+            mVersionCode = apk.getLongVersionCode();
+        }
     }
 
     /**
@@ -1545,25 +1523,18 @@
                 && params.mode == SessionParams.MODE_INHERIT_EXISTING
                 && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
 
-        try {
-            resolveStageDirLocked();
-        } catch (IOException e) {
-            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
-                    "Failed to resolve stage location", e);
-        }
-
-        final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);
+        final File[] removedFiles = getRemovedFilesLocked();
         final List<String> removeSplitList = new ArrayList<>();
         if (!ArrayUtils.isEmpty(removedFiles)) {
             for (File removedFile : removedFiles) {
                 final String fileName = removedFile.getName();
                 final String splitName = fileName.substring(
-                        0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());
+                        0, fileName.length() - REMOVE_MARKER_EXTENSION.length());
                 removeSplitList.add(splitName);
             }
         }
 
-        final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
+        final File[] addedFiles = getAddedFilesLocked();
         if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
         }
@@ -1607,7 +1578,7 @@
                         "Invalid filename: " + targetName);
             }
 
-            final File targetFile = new File(mResolvedStageDir, targetName);
+            final File targetFile = new File(stageDir, targetName);
             resolveAndStageFile(addedFile, targetFile);
 
             // Base is coming from session
@@ -1622,7 +1593,7 @@
                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                             "Invalid filename: " + dexMetadataFile);
                 }
-                final File targetDexMetadataFile = new File(mResolvedStageDir,
+                final File targetDexMetadataFile = new File(stageDir,
                         DexMetadataHelper.buildDexMetadataPathForApk(targetName));
                 resolveAndStageFile(dexMetadataFile, targetDexMetadataFile);
             }
@@ -1883,6 +1854,15 @@
     }
 
     /**
+     * @return the package name of this session
+     */
+    String getPackageName() {
+        synchronized (mLock) {
+            return mPackageName;
+        }
+    }
+
+    /**
      * @return the timestamp of when this session last changed state
      */
     public long getUpdatedMillis() {
@@ -2182,17 +2162,17 @@
     }
 
     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
-        final IPackageInstallObserver2 observer;
+        final IntentSender statusReceiver;
         final String packageName;
         synchronized (mLock) {
             mFinalStatus = returnCode;
             mFinalMessage = msg;
 
-            observer = mRemoteObserver;
+            statusReceiver = mRemoteStatusReceiver;
             packageName = mPackageName;
         }
 
-        if (observer != null) {
+        if (statusReceiver != null) {
             // Execute observer.onPackageInstalled on different tread as we don't want callers
             // inside the system server have to worry about catching the callbacks while they are
             // calling into the session
@@ -2200,7 +2180,7 @@
             args.arg1 = packageName;
             args.arg2 = msg;
             args.arg3 = extras;
-            args.arg4 = observer;
+            args.arg4 = statusReceiver;
             args.argi1 = returnCode;
 
             mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ce0c21c..50bbfa9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3004,8 +3004,7 @@
 
             mWellbeingPackage = getWellbeingPackageName();
             mDocumenterPackage = getDocumenterPackageName();
-            mConfiguratorPackage =
-                    mContext.getString(R.string.config_deviceConfiguratorPackageName);
+            mConfiguratorPackage = getDeviceConfiguratorPackageName();
             mAppPredictionServicePackage = getAppPredictionServicePackageName();
             mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
             mTelephonyPackages = getTelephonyPackageNames();
@@ -17556,36 +17555,48 @@
         int count = 0;
         final String packageName = pkg.packageName;
 
+        boolean handlesWebUris = false;
+        final boolean alreadyVerified;
         synchronized (mPackages) {
             // If this is a new install and we see that we've already run verification for this
             // package, we have nothing to do: it means the state was restored from backup.
-            if (!replacing) {
-                IntentFilterVerificationInfo ivi =
-                        mSettings.getIntentFilterVerificationLPr(packageName);
-                if (ivi != null) {
-                    if (DEBUG_DOMAIN_VERIFICATION) {
-                        Slog.i(TAG, "Package " + packageName+ " already verified: status="
-                                + ivi.getStatusString());
-                    }
-                    return;
+            final IntentFilterVerificationInfo ivi =
+                    mSettings.getIntentFilterVerificationLPr(packageName);
+            alreadyVerified = (ivi != null);
+            if (!replacing && alreadyVerified) {
+                if (DEBUG_DOMAIN_VERIFICATION) {
+                    Slog.i(TAG, "Package " + packageName + " already verified: status="
+                            + ivi.getStatusString());
                 }
+                return;
             }
 
-            // If any filters need to be verified, then all need to be.
+            // If any filters need to be verified, then all need to be.  In addition, we need to
+            // know whether an updating app has any web navigation intent filters, to re-
+            // examine handling policy even if not re-verifying.
             boolean needToVerify = false;
             for (PackageParser.Activity a : pkg.activities) {
                 for (ActivityIntentInfo filter : a.intents) {
+                    if (filter.handlesWebUris(true)) {
+                        handlesWebUris = true;
+                    }
                     if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) {
                         if (DEBUG_DOMAIN_VERIFICATION) {
                             Slog.d(TAG,
                                     "Intent filter needs verification, so processing all filters");
                         }
                         needToVerify = true;
+                        // It's safe to break out here because filter.needsVerification()
+                        // can only be true if filter.handlesWebUris(true) returns true, so
+                        // we've already noted that.
                         break;
                     }
                 }
             }
 
+            // Note whether this app publishes any web navigation handling support at all,
+            // and whether there are any web-nav filters that fit the profile for running
+            // a verification pass now.
             if (needToVerify) {
                 final int verificationId = mIntentFilterVerificationToken++;
                 for (PackageParser.Activity a : pkg.activities) {
@@ -17603,13 +17614,23 @@
         }
 
         if (count > 0) {
+            // count > 0 means that we're running a full verification pass
             if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
                     + " IntentFilter verification" + (count > 1 ? "s" : "")
                     +  " for userId:" + userId);
             mIntentFilterVerifier.startVerifications(userId);
+        } else if (alreadyVerified && handlesWebUris) {
+            // App used autoVerify in the past, no longer does, but still handles web
+            // navigation starts.
+            if (DEBUG_DOMAIN_VERIFICATION) {
+                Slog.d(TAG, "App changed web filters but no longer verifying - resetting policy");
+            }
+            synchronized (mPackages) {
+                clearIntentFilterVerificationsLPw(packageName, userId);
+            }
         } else {
             if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(TAG, "No filters or not all autoVerify for " + packageName);
+                Slog.d(TAG, "No web filters or no prior verify policy for " + packageName);
             }
         }
     }
@@ -20515,7 +20536,8 @@
 
     @Override
     public String getSystemTextClassifierPackageName() {
-        return mContext.getString(R.string.config_defaultTextClassifierPackage);
+        return ensureSystemPackageName(mContext.getString(
+                R.string.config_defaultTextClassifierPackage));
     }
 
     @Override
@@ -20525,7 +20547,7 @@
         if (flattenedComponentName != null) {
             ComponentName componentName = ComponentName.unflattenFromString(flattenedComponentName);
             if (componentName != null && componentName.getPackageName() != null) {
-                return componentName.getPackageName();
+                return ensureSystemPackageName(componentName.getPackageName());
             }
         }
         return null;
@@ -20550,9 +20572,15 @@
         }
     }
 
+    @Nullable
+    private String getDeviceConfiguratorPackageName() {
+        return ensureSystemPackageName(mContext.getString(
+                R.string.config_deviceConfiguratorPackageName));
+    }
+
     @Override
     public String getWellbeingPackageName() {
-        return mContext.getString(R.string.config_defaultWellbeingPackage);
+        return ensureSystemPackageName(mContext.getString(R.string.config_defaultWellbeingPackage));
     }
 
     @Override
@@ -20567,7 +20595,7 @@
         if (appPredictionServiceComponentName == null) {
             return null;
         }
-        return appPredictionServiceComponentName.getPackageName();
+        return ensureSystemPackageName(appPredictionServiceComponentName.getPackageName());
     }
 
     @Override
@@ -20584,11 +20612,33 @@
         if (systemCaptionsServiceComponentName == null) {
             return null;
         }
-        return systemCaptionsServiceComponentName.getPackageName();
+        return ensureSystemPackageName(systemCaptionsServiceComponentName.getPackageName());
     }
 
     public String getIncidentReportApproverPackageName() {
-        return mContext.getString(R.string.config_incidentReportApproverPackage);
+        return ensureSystemPackageName(mContext.getString(
+                R.string.config_incidentReportApproverPackage));
+    }
+
+    @Nullable
+    private String ensureSystemPackageName(@Nullable String packageName) {
+        if (packageName == null) {
+            return null;
+        }
+        long token = Binder.clearCallingIdentity();
+        try {
+            if (getPackageInfo(packageName, MATCH_FACTORY_ONLY, UserHandle.USER_SYSTEM) == null) {
+                PackageInfo packageInfo = getPackageInfo(packageName, 0, UserHandle.USER_SYSTEM);
+                if (packageInfo != null) {
+                    EventLog.writeEvent(0x534e4554, "145981139", packageInfo.applicationInfo.uid,
+                            "");
+                }
+                return null;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return packageName;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d023ebb..ad8de40 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1163,42 +1163,7 @@
                 pw.println("Success");
                 return 0;
             }
-
-            long timeoutMs = params.timeoutMs <= 0
-                    ? DEFAULT_WAIT_MS
-                    : params.timeoutMs;
-            PackageInstaller.SessionInfo si = mInterface.getPackageInstaller()
-                    .getSessionInfo(sessionId);
-            long currentTime = System.currentTimeMillis();
-            long endTime = currentTime + timeoutMs;
-            // Using a loop instead of BroadcastReceiver since we can receive session update
-            // broadcast only if packageInstallerName is "android". We can't always force
-            // "android" as packageIntallerName, e.g, rollback auto implies
-            // "-i com.android.shell".
-            while (currentTime < endTime) {
-                if (si != null
-                        && (si.isStagedSessionReady() || si.isStagedSessionFailed())) {
-                    break;
-                }
-                SystemClock.sleep(Math.min(endTime - currentTime, 100));
-                currentTime = System.currentTimeMillis();
-                si = mInterface.getPackageInstaller().getSessionInfo(sessionId);
-            }
-            if (si == null) {
-                pw.println("Failure [failed to retrieve SessionInfo]");
-                return 1;
-            }
-            if (!si.isStagedSessionReady() && !si.isStagedSessionFailed()) {
-                pw.println("Failure [timed out after " + timeoutMs + " ms]");
-                return 1;
-            }
-            if (!si.isStagedSessionReady()) {
-                pw.println("Error [" + si.getStagedSessionErrorCode() + "] ["
-                        + si.getStagedSessionErrorMessage() + "]");
-                return 1;
-            }
-            pw.println("Success. Reboot device to apply staged session");
-            return 0;
+            return doWaitForStagedSessionRead(sessionId, params.timeoutMs, pw);
         } finally {
             if (abandonSession) {
                 try {
@@ -1209,14 +1174,92 @@
         }
     }
 
+    private int doWaitForStagedSessionRead(int sessionId, long timeoutMs, PrintWriter pw)
+              throws RemoteException {
+        if (timeoutMs <= 0) {
+            timeoutMs = DEFAULT_WAIT_MS;
+        }
+        PackageInstaller.SessionInfo si = mInterface.getPackageInstaller()
+                .getSessionInfo(sessionId);
+        if (si == null) {
+            pw.println("Failure [Unknown session " + sessionId + "]");
+            return 1;
+        }
+        if (!si.isStaged()) {
+            pw.println("Failure [Session " + sessionId + " is not a staged session]");
+            return 1;
+        }
+        long currentTime = System.currentTimeMillis();
+        long endTime = currentTime + timeoutMs;
+        // Using a loop instead of BroadcastReceiver since we can receive session update
+        // broadcast only if packageInstallerName is "android". We can't always force
+        // "android" as packageIntallerName, e.g, rollback auto implies
+        // "-i com.android.shell".
+        while (currentTime < endTime) {
+            if (si != null && (si.isStagedSessionReady() || si.isStagedSessionFailed())) {
+                break;
+            }
+            SystemClock.sleep(Math.min(endTime - currentTime, 100));
+            currentTime = System.currentTimeMillis();
+            si = mInterface.getPackageInstaller().getSessionInfo(sessionId);
+        }
+        if (si == null) {
+            pw.println("Failure [failed to retrieve SessionInfo]");
+            return 1;
+        }
+        if (!si.isStagedSessionReady() && !si.isStagedSessionFailed()) {
+            pw.println("Failure [timed out after " + timeoutMs + " ms]");
+            return 1;
+        }
+        if (!si.isStagedSessionReady()) {
+            pw.println("Error [" + si.getStagedSessionErrorCode() + "] ["
+                    + si.getStagedSessionErrorMessage() + "]");
+            return 1;
+        }
+        pw.println("Success. Reboot device to apply staged session");
+        return 0;
+    }
+
     private int runInstallAbandon() throws RemoteException {
         final int sessionId = Integer.parseInt(getNextArg());
         return doAbandonSession(sessionId, true /*logSuccess*/);
     }
 
     private int runInstallCommit() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        String opt;
+        boolean waitForStagedSessionReady = true;
+        long timeoutMs = -1;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "--wait":
+                    waitForStagedSessionReady = true;
+                    // If there is only one remaining argument, then it represents the sessionId, we
+                    // shouldn't try to parse it as timeoutMs.
+                    if (getRemainingArgsCount() > 1) {
+                        try {
+                            timeoutMs = Long.parseLong(peekNextArg());
+                            getNextArg();
+                        } catch (NumberFormatException ignore) {
+                        }
+                    }
+                    break;
+                case "--no-wait":
+                    waitForStagedSessionReady = false;
+                    break;
+            }
+        }
         final int sessionId = Integer.parseInt(getNextArg());
-        return doCommitSession(sessionId, true /*logSuccess*/);
+        if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+            return 1;
+        }
+        final PackageInstaller.SessionInfo si = mInterface.getPackageInstaller()
+                .getSessionInfo(sessionId);
+        if (si == null || !si.isStaged() || !waitForStagedSessionReady) {
+            pw.println("Success");
+            return 0;
+        }
+        return doWaitForStagedSessionRead(sessionId, timeoutMs, pw);
     }
 
     private int runInstallCreate() throws RemoteException {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 994fca8..9fa12b2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1252,6 +1252,7 @@
             return false;
         }
         ps.clearDomainVerificationStatusForUser(userId);
+        ps.setIntentFilterVerificationInfo(null);
         return true;
     }
 
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 8cc66b2..3e9610e 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -39,17 +39,22 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelableException;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.apk.ApkSignatureVerifier;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageHelper;
 import com.android.internal.os.BackgroundThread;
 
 import java.io.File;
@@ -59,6 +64,7 @@
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -73,7 +79,8 @@
     private final PackageInstallerService mPi;
     private final ApexManager mApexManager;
     private final PowerManager mPowerManager;
-    private final Handler mBgHandler;
+    private final Context mContext;
+    private final PreRebootVerificationHandler mPreRebootVerificationHandler;
 
     @GuardedBy("mStagedSessions")
     private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
@@ -81,8 +88,10 @@
     StagingManager(PackageInstallerService pi, ApexManager am, Context context) {
         mPi = pi;
         mApexManager = am;
+        mContext = context;
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mBgHandler = BackgroundThread.getHandler();
+        mPreRebootVerificationHandler = new PreRebootVerificationHandler(
+                BackgroundThread.get().getLooper());
     }
 
     private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) {
@@ -110,18 +119,17 @@
      * Validates the signature used to sign the container of the new apex package
      *
      * @param newApexPkg The new apex package that is being installed
-     * @param installFlags flags related to the session
      * @throws PackageManagerException
      */
-    private void validateApexSignature(PackageInfo newApexPkg, int installFlags)
+    private void validateApexSignature(PackageInfo newApexPkg)
             throws PackageManagerException {
         // Get signing details of the new package
         final String apexPath = newApexPkg.applicationInfo.sourceDir;
         final String packageName = newApexPkg.packageName;
 
-        final SigningDetails signingDetails;
+        final SigningDetails newSigningDetails;
         try {
-            signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR);
+            newSigningDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR);
         } catch (PackageParserException e) {
             throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Failed to parse APEX package " + apexPath, e);
@@ -146,8 +154,10 @@
         }
 
         // Verify signing details for upgrade
-        if (signingDetails.checkCapability(existingSigningDetails,
-                PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
+        if (newSigningDetails.checkCapability(existingSigningDetails,
+                SigningDetails.CertCapabilities.INSTALLED_DATA)
+                || existingSigningDetails.checkCapability(newSigningDetails,
+                SigningDetails.CertCapabilities.ROLLBACK)) {
             return;
         }
 
@@ -208,7 +218,7 @@
         }
         final long activeVersion = activePackage.applicationInfo.longVersionCode;
         if (activeVersion != session.params.requiredInstalledVersionCode) {
-            if (!mApexManager.abortActiveSession()) {
+            if (!mApexManager.abortStagedSession(session.sessionId)) {
                 Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
             }
             throw new PackageManagerException(
@@ -227,7 +237,7 @@
         final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
                 session.params.installFlags, activePackage.applicationInfo.flags);
         if (activeVersion > newVersionCode && !allowsDowngrade) {
-            if (!mApexManager.abortActiveSession()) {
+            if (!mApexManager.abortStagedSession(session.sessionId)) {
                 Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
             }
             throw new PackageManagerException(
@@ -242,75 +252,6 @@
         return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0;
     }
 
-    private void preRebootVerification(@NonNull PackageInstallerSession session) {
-        Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
-        final boolean hasApex = sessionContainsApex(session);
-        // APEX checks. For single-package sessions, check if they contain an APEX. For
-        // multi-package sessions, find all the child sessions that contain an APEX.
-        if (hasApex) {
-            try {
-                final List<PackageInfo> apexPackages = submitSessionToApexService(session);
-                for (PackageInfo apexPackage : apexPackages) {
-                    validateApexSignature(apexPackage, session.params.installFlags);
-                }
-            } catch (PackageManagerException e) {
-                session.setStagedSessionFailed(e.error, e.getMessage());
-                return;
-            }
-        }
-
-        if (sessionContainsApk(session)) {
-            try {
-                Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
-                        + session.sessionId + " by performing a dry-run install");
-                installApksInSession(session, /* preReboot */ true);
-                // TODO(b/118865310): abort the session on apexd.
-            } catch (PackageManagerException e) {
-                session.setStagedSessionFailed(e.error, e.getMessage());
-                return;
-            }
-        }
-
-        if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
-            // If rollback is enabled for this session, we call through to the RollbackManager
-            // with the list of sessions it must enable rollback for. Note that notifyStagedSession
-            // is a synchronous operation.
-            final IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
-            try {
-                // NOTE: To stay consistent with the non-staged install flow, we don't fail the
-                // entire install if rollbacks can't be enabled.
-                if (!rm.notifyStagedSession(session.sessionId)) {
-                    Slog.e(TAG, "Unable to enable rollback for session: " + session.sessionId);
-                }
-            } catch (RemoteException re) {
-                // Cannot happen, the rollback manager is in the same process.
-            }
-        }
-
-        // Proactively mark session as ready before calling apexd. Although this call order looks
-        // counter-intuitive, this is the easiest way to ensure that session won't end up in the
-        // inconsistent state:
-        //  - If device gets rebooted right before call to apexd, then apexd will never activate
-        //      apex files of this staged session. This will result in StagingManager failing the
-        //      session.
-        // On the other hand, if the order of the calls was inverted (first call apexd, then mark
-        // session as ready), then if a device gets rebooted right after the call to apexd, only
-        // apex part of the train will be applied, leaving device in an inconsistent state.
-        Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
-        session.setStagedSessionReady();
-        if (!hasApex) {
-            // Session doesn't contain apex, nothing to do.
-            return;
-        }
-        try {
-            mApexManager.markStagedSessionReady(session.sessionId);
-        } catch (PackageManagerException e) {
-            session.setStagedSessionFailed(e.error, e.getMessage());
-        }
-    }
-
-
     private boolean sessionContains(@NonNull PackageInstallerSession session,
                                     Predicate<PackageInstallerSession> filter) {
         if (!session.isMultiPackage()) {
@@ -335,39 +276,100 @@
         return sessionContains(session, (s) -> !isApexSession(s));
     }
 
+    // Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
+    private void abortCheckpoint() {
+        try {
+            if (supportsCheckpoint() && needsCheckpoint()) {
+                mApexManager.revertActiveSessions();
+                PackageHelper.getStorageManager().abortChanges(
+                        "StagingManager initiated", false /*retry*/);
+            }
+        } catch (Exception e) {
+            Slog.wtf(TAG, "Failed to abort checkpoint", e);
+            mApexManager.revertActiveSessions();
+            mPowerManager.reboot(null);
+        }
+    }
+
+    private boolean supportsCheckpoint() throws RemoteException {
+        return PackageHelper.getStorageManager().supportsCheckpoint();
+    }
+
+    private boolean needsCheckpoint() throws RemoteException {
+        return PackageHelper.getStorageManager().needsCheckpoint();
+    }
+
     private void resumeSession(@NonNull PackageInstallerSession session) {
         Slog.d(TAG, "Resuming session " + session.sessionId);
+
         final boolean hasApex = sessionContainsApex(session);
+        ApexSessionInfo apexSessionInfo = null;
         if (hasApex) {
             // Check with apexservice whether the apex packages have been activated.
-            ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
+            apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
+
+            if (apexSessionInfo != null && apexSessionInfo.isVerified) {
+                // Session has been previously submitted to apexd, but didn't complete all the
+                // pre-reboot verification, perhaps because the device rebooted in the meantime.
+                // Greedily re-trigger the pre-reboot verification. We want to avoid marking it as
+                // failed when not in checkpoint mode, hence it is being processed separately.
+                Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to "
+                        + "be verified, resuming pre-reboot verification");
+                mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
+                return;
+            }
+        }
+
+        // Before we resume session, we check if revert is needed or not. Typically, we enter file-
+        // system checkpoint mode when we reboot first time in order to install staged sessions. We
+        // want to install staged sessions in this mode as rebooting now will revert user data. If
+        // something goes wrong, then we reboot again to enter fs-rollback mode. Rebooting now will
+        // have no effect on user data, so mark the sessions as failed instead.
+        try {
+            // If checkpoint is supported, then we only resume sessions if we are in checkpointing
+            // mode. If not, we fail all sessions.
+            if (supportsCheckpoint() && !needsCheckpoint()) {
+                // TODO(b/146343545): Persist failure reason across checkpoint reboot
+                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+                        "Reverting back to safe state");
+                return;
+            }
+        } catch (RemoteException e) {
+            // Cannot continue staged install without knowing if fs-checkpoint is supported
+            Slog.e(TAG, "Checkpoint support unknown. Aborting staged install for session "
+                    + session.sessionId, e);
+            // TODO: Mark all staged sessions together and reboot only once
+            session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+                    "Checkpoint support unknown. Aborting staged install.");
+            if (hasApex) {
+                mApexManager.revertActiveSessions();
+            }
+            mPowerManager.reboot("Checkpoint support unknown");
+            return;
+        }
+
+        if (hasApex) {
             if (apexSessionInfo == null) {
                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                         "apexd did not know anything about a staged session supposed to be"
                         + "activated");
+                abortCheckpoint();
                 return;
             }
             if (isApexSessionFailed(apexSessionInfo)) {
                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                         "APEX activation failed. Check logcat messages from apexd for "
                                 + "more information.");
-                return;
-            }
-            if (apexSessionInfo.isVerified) {
-                // Session has been previously submitted to apexd, but didn't complete all the
-                // pre-reboot verification, perhaps because the device rebooted in the meantime.
-                // Greedily re-trigger the pre-reboot verification.
-                Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to be "
-                        + "verified, resuming pre-reboot verification");
-                mBgHandler.post(() -> preRebootVerification(session));
+                abortCheckpoint();
                 return;
             }
             if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
-                // In all the remaining cases apexd will try to apply the session again at next
-                // boot. Nothing to do here for now.
-                Slog.w(TAG, "Staged session " + session.sessionId + " scheduled to be applied "
-                        + "at boot didn't activate nor fail. This usually means that apexd will "
-                        + "retry at next reboot.");
+                // Apexd did not apply the session for some unknown reason. There is no guarantee
+                // that apexd will install it next time. Safer to proactively mark as failed.
+                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                        "Staged session " + session.sessionId + "at boot didn't "
+                                + "activate nor fail. Marking it as failed anyway.");
+                abortCheckpoint();
                 return;
             }
             Slog.i(TAG, "APEX packages in session " + session.sessionId
@@ -376,15 +378,17 @@
         // The APEX part of the session is activated, proceed with the installation of APKs.
         try {
             Slog.d(TAG, "Installing APK packages in session " + session.sessionId);
-            installApksInSession(session, /* preReboot */ false);
+            installApksInSession(session);
         } catch (PackageManagerException e) {
             session.setStagedSessionFailed(e.error, e.getMessage());
+            abortCheckpoint();
 
+            // If checkpoint is not supported, we have to handle failure for one staged session.
             if (!hasApex) {
                 return;
             }
 
-            if (!mApexManager.abortActiveSession()) {
+            if (!mApexManager.revertActiveSessions()) {
                 Slog.e(TAG, "Failed to abort APEXd session");
             } else {
                 Slog.e(TAG,
@@ -467,53 +471,23 @@
         }
     }
 
-    private void commitApkSession(@NonNull PackageInstallerSession apkSession,
-            int originalSessionId, boolean preReboot) throws PackageManagerException {
-        final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
-                : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
-        if (!preReboot) {
-            if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
-                // If rollback is available for this session, notify the rollback
-                // manager of the apk session so it can properly enable rollback.
-                final IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                        ServiceManager.getService(Context.ROLLBACK_SERVICE));
-                try {
-                    rm.notifyStagedApkSession(originalSessionId, apkSession.sessionId);
-                } catch (RemoteException re) {
-                    // Cannot happen, the rollback manager is in the same process.
-                }
-            }
-        }
-
-        final LocalIntentReceiver receiver = new LocalIntentReceiver();
-        apkSession.commit(receiver.getIntentSender(), false);
-        final Intent result = receiver.getResult();
-        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                PackageInstaller.STATUS_FAILURE);
-        if (status != PackageInstaller.STATUS_SUCCESS) {
-
-            final String errorMessage = result.getStringExtra(
-                    PackageInstaller.EXTRA_STATUS_MESSAGE);
-            Slog.e(TAG, "Failure to install APK staged session " + originalSessionId + " ["
-                    + errorMessage + "]");
-            throw new PackageManagerException(errorCode, errorMessage);
-        }
-    }
-
-    private void installApksInSession(@NonNull PackageInstallerSession session,
-                                         boolean preReboot) throws PackageManagerException {
+    /**
+     * Extract apks in the given session into a new session. Returns {@code null} if there is no
+     * apks in the given session. Only parent session is returned for multi-package session.
+     */
+    @Nullable
+    private PackageInstallerSession extractApksInSession(PackageInstallerSession session,
+            boolean preReboot) throws PackageManagerException {
         final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
                 : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
         if (!session.isMultiPackage() && !isApexSession(session)) {
-            // APK single-packaged staged session. Do a regular install.
-            PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot);
-            commitApkSession(apkSession, session.sessionId, preReboot);
+            return createAndWriteApkSession(session, preReboot);
         } else if (session.isMultiPackage()) {
             // For multi-package staged sessions containing APKs, we identify which child sessions
             // contain an APK, and with those then create a new multi-package group of sessions,
             // carrying over all the session parameters and unmarking them as staged. On commit the
             // sessions will be installed atomically.
-            List<PackageInstallerSession> childSessions;
+            final List<PackageInstallerSession> childSessions;
             synchronized (mStagedSessions) {
                 childSessions =
                         Arrays.stream(session.getChildSessionIds())
@@ -525,18 +499,18 @@
             }
             if (childSessions.isEmpty()) {
                 // APEX-only multi-package staged session, nothing to do.
-                return;
+                return null;
             }
-            PackageInstaller.SessionParams params = session.params.copy();
+            final PackageInstaller.SessionParams params = session.params.copy();
             params.isStaged = false;
             if (preReboot) {
                 params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
             }
             // TODO(b/129744602): use the userid from the original session.
-            int apkParentSessionId = mPi.createSession(
+            final int apkParentSessionId = mPi.createSession(
                     params, session.getInstallerPackageName(),
                     0 /* UserHandle.SYSTEM */);
-            PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
+            final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
             try {
                 apkParentSession.open();
             } catch (IOException e) {
@@ -557,35 +531,162 @@
                             "Failed to add a child session " + apkChildSession.sessionId);
                 }
             }
-            commitApkSession(apkParentSession, session.sessionId, preReboot);
+            return apkParentSession;
         }
-        // APEX single-package staged session, nothing to do.
+        return null;
+    }
+
+    private void verifyApksInSession(PackageInstallerSession session)
+            throws PackageManagerException {
+
+        final PackageInstallerSession apksToVerify = extractApksInSession(
+                session,  /* preReboot */ true);
+        if (apksToVerify == null) {
+            return;
+        }
+
+        final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
+                (Intent result) -> {
+                    int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                            PackageInstaller.STATUS_FAILURE);
+                    if (status != PackageInstaller.STATUS_SUCCESS) {
+                        final String errorMessage = result.getStringExtra(
+                                PackageInstaller.EXTRA_STATUS_MESSAGE);
+                        Slog.e(TAG, "Failure to verify APK staged session "
+                                + session.sessionId + " [" + errorMessage + "]");
+                        session.setStagedSessionFailed(
+                                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
+                        return;
+                    }
+                    mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
+                            session.sessionId);
+                });
+
+        apksToVerify.commit(receiver.getIntentSender(), false);
+    }
+
+    private void installApksInSession(@NonNull PackageInstallerSession session)
+            throws PackageManagerException {
+
+        final PackageInstallerSession apksToInstall = extractApksInSession(
+                session, /* preReboot */ false);
+        if (apksToInstall == null) {
+            return;
+        }
+
+        if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+            // If rollback is available for this session, notify the rollback
+            // manager of the apk session so it can properly enable rollback.
+            final IRollbackManager rm = IRollbackManager.Stub.asInterface(
+                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
+            try {
+                rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId);
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
+                        + session.sessionId, re);
+            }
+        }
+
+        final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
+        apksToInstall.commit(receiver.getIntentSender(), false);
+        final Intent result = receiver.getResult();
+        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_FAILURE);
+        if (status != PackageInstaller.STATUS_SUCCESS) {
+            final String errorMessage = result.getStringExtra(
+                    PackageInstaller.EXTRA_STATUS_MESSAGE);
+            Slog.e(TAG, "Failure to install APK staged session "
+                    + session.sessionId + " [" + errorMessage + "]");
+            throw new PackageManagerException(
+                    SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
+        }
     }
 
     void commitSession(@NonNull PackageInstallerSession session) {
         updateStoredSession(session);
-        mBgHandler.post(() -> preRebootVerification(session));
+        mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
     }
 
-    @Nullable
-    PackageInstallerSession getActiveSession() {
+    private int parentOrOwnSessionId(PackageInstallerSession session) {
+        return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId;
+    }
+
+    /**
+     * <p> Check if the session provided is non-overlapping with the active staged sessions.
+     *
+     * <p> A session is non-overlapping if it meets one of the following conditions: </p>
+     * <ul>
+     *     <li>It is a parent session</li>
+     *     <li>It is already one of the active sessions</li>
+     *     <li>Its package name is not same as any of the active sessions</li>
+     * </ul>
+     * @throws PackageManagerException if session fails the check
+     */
+    void checkNonOverlappingWithStagedSessions(@NonNull PackageInstallerSession session)
+            throws PackageManagerException {
+        if (session.isMultiPackage()) {
+            // We cannot say a parent session overlaps until we process its children
+            return;
+        }
+        if (session.getPackageName() == null) {
+            throw new PackageManagerException(PackageManager.INSTALL_FAILED_INVALID_APK,
+                    "Cannot stage session " + session.sessionId + " with package name null");
+        }
+
+        boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
+                Context.STORAGE_SERVICE)).isCheckpointSupported();
+
         synchronized (mStagedSessions) {
             for (int i = 0; i < mStagedSessions.size(); i++) {
-                final PackageInstallerSession session = mStagedSessions.valueAt(i);
-                if (!session.isCommitted()) {
+                final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
+                if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()) {
                     continue;
                 }
-                if (session.hasParentSessionId()) {
-                    // Staging manager will finalize only parent session. Ignore child sessions
-                    // picking the active.
+                if (stagedSession.isMultiPackage()) {
+                    // This active parent staged session is useless as it doesn't have a package
+                    // name and the session we are checking is not a parent session either.
                     continue;
                 }
-                if (!session.isStagedSessionApplied() && !session.isStagedSessionFailed()) {
-                    return session;
+
+                // From here on, stagedSession is a non-parent active staged session
+
+                // Check if stagedSession has an active parent session or not
+                if (stagedSession.hasParentSessionId()) {
+                    int parentId = stagedSession.getParentSessionId();
+                    PackageInstallerSession parentSession = mStagedSessions.get(parentId);
+                    if (parentSession == null || parentSession.isStagedAndInTerminalState()) {
+                        // Parent session has been abandoned or terminated already
+                        continue;
+                    }
+                }
+
+                // Check if session is one of the active sessions
+                if (session.sessionId == stagedSession.sessionId) {
+                    Slog.w(TAG, "Session " + session.sessionId + " is already staged");
+                    continue;
+                }
+
+                // If session is not among the active sessions, then it cannot have same package
+                // name as any of the active sessions.
+                if (session.getPackageName().equals(stagedSession.getPackageName())) {
+                    throw new PackageManagerException(
+                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+                            "Package: " + session.getPackageName() + " in session: "
+                                    + session.sessionId + " has been staged already by session: "
+                                    + stagedSession.sessionId, null);
+                }
+
+                // Staging multiple root sessions is not allowed if device doesn't support
+                // checkpoint. If session and stagedSession do not have common ancestor, they are
+                // from two different root sessions.
+                if (!supportsCheckpoint
+                        && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) {
+                    throw new PackageManagerException(
+                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+                            "Cannot stage multiple sessions without checkpoint support", null);
                 }
             }
         }
-        return null;
     }
 
     void createSession(@NonNull PackageInstallerSession sessionInfo) {
@@ -612,25 +713,29 @@
             ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
             if (apexSession == null || isApexSessionFinalized(apexSession)) {
                 Slog.w(TAG,
-                        "Cannot abort session because it is not active or APEXD is not reachable");
+                        "Cannot abort session " + session.sessionId
+                                + " because it is not active or APEXD is not reachable");
                 return;
             }
-            mApexManager.abortActiveSession();
+            try {
+                mApexManager.abortStagedSession(session.sessionId);
+            } catch (Exception ignore) {
+            }
         }
     }
 
     private boolean isApexSessionFinalized(ApexSessionInfo session) {
         /* checking if the session is in a final state, i.e., not active anymore */
         return session.isUnknown || session.isActivationFailed || session.isSuccess
-                || session.isRolledBack;
+                || session.isReverted;
     }
 
     private static boolean isApexSessionFailed(ApexSessionInfo apexSessionInfo) {
-        // isRollbackInProgress is included to cover the scenario, when a device is rebooted in
-        // during the rollback, and apexd fails to resume the rollback after reboot.
+        // isRevertInProgress is included to cover the scenario, when a device is rebooted
+        // during the revert, and apexd fails to resume the revert after reboot.
         return apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown
-                || apexSessionInfo.isRolledBack || apexSessionInfo.isRollbackInProgress
-                || apexSessionInfo.isRollbackFailed;
+                || apexSessionInfo.isReverted || apexSessionInfo.isRevertInProgress
+                || apexSessionInfo.isRevertFailed;
     }
 
     @GuardedBy("mStagedSessions")
@@ -691,7 +796,7 @@
         if (!session.isStagedSessionReady()) {
             // The framework got restarted before the pre-reboot verification could complete,
             // restart the verification.
-            mBgHandler.post(() -> preRebootVerification(session));
+            mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
         } else {
             // Session had already being marked ready. Start the checks to verify if there is any
             // follow-up work.
@@ -699,14 +804,34 @@
         }
     }
 
-    private static class LocalIntentReceiver {
+    private static class LocalIntentReceiverAsync {
+        final Consumer<Intent> mConsumer;
+
+        LocalIntentReceiverAsync(Consumer<Intent> consumer) {
+            mConsumer = consumer;
+        }
+
+        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+                mConsumer.accept(intent);
+            }
+        };
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+    }
+
+    private static class LocalIntentReceiverSync {
         private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
 
         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
             public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
-                             IIntentReceiver finishedReceiver, String requiredPermission,
-                             Bundle options) {
+                    IIntentReceiver finishedReceiver, String requiredPermission,
+                    Bundle options) {
                 try {
                     mResult.offer(intent, 5, TimeUnit.SECONDS);
                 } catch (InterruptedException e) {
@@ -727,4 +852,201 @@
             }
         }
     }
+
+    private final class PreRebootVerificationHandler extends Handler {
+
+        PreRebootVerificationHandler(Looper looper) {
+            super(looper);
+        }
+
+        /**
+         * Handler for states of pre reboot verification. The states are arranged linearly (shown
+         * below) with each state either calling the next state, or calling some other method that
+         * eventually calls the next state.
+         *
+         * <p><ul>
+         *     <li>MSG_PRE_REBOOT_VERIFICATION_START</li>
+         *     <li>MSG_PRE_REBOOT_VERIFICATION_APEX</li>
+         *     <li>MSG_PRE_REBOOT_VERIFICATION_APK</li>
+         *     <li>MSG_PRE_REBOOT_VERIFICATION_END</li>
+         * </ul></p>
+         *
+         * Details about each of state can be found in corresponding handler of node.
+         */
+        private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
+        private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
+        private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
+        private static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
+
+        @Override
+        public void handleMessage(Message msg) {
+            final int sessionId = msg.arg1;
+            final PackageInstallerSession session;
+            synchronized (mStagedSessions) {
+                session = mStagedSessions.get(sessionId);
+            }
+            // Maybe session was aborted before pre-reboot verification was complete
+            if (session == null) {
+                Slog.d(TAG, "Stopping pre-reboot verification for sessionId: " + sessionId);
+                return;
+            }
+            switch (msg.what) {
+                case MSG_PRE_REBOOT_VERIFICATION_START:
+                    handlePreRebootVerification_Start(session);
+                    break;
+                case MSG_PRE_REBOOT_VERIFICATION_APEX:
+                    handlePreRebootVerification_Apex(session);
+                    break;
+                case MSG_PRE_REBOOT_VERIFICATION_APK:
+                    handlePreRebootVerification_Apk(session);
+                    break;
+                case MSG_PRE_REBOOT_VERIFICATION_END:
+                    handlePreRebootVerification_End(session);
+                    break;
+            }
+        }
+
+        // Method for starting the pre-reboot verification
+        private void startPreRebootVerification(int sessionId) {
+            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
+        }
+
+        private void notifyPreRebootVerification_Start_Complete(int sessionId) {
+            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget();
+        }
+
+        private void notifyPreRebootVerification_Apex_Complete(int sessionId) {
+            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, sessionId, 0).sendToTarget();
+        }
+
+        private void notifyPreRebootVerification_Apk_Complete(int sessionId) {
+            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, sessionId, 0).sendToTarget();
+        }
+
+        /**
+         * A dummy state for starting the pre reboot verification.
+         *
+         * See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
+         */
+        private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
+            Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
+            notifyPreRebootVerification_Start_Complete(session.sessionId);
+        }
+
+        /**
+         * Pre-reboot verification state for apex files:
+         *
+         * <p><ul>
+         *     <li>submits session to apex service</li>
+         *     <li>validates signatures of apex files</li>
+         * </ul></p>
+         */
+        private void handlePreRebootVerification_Apex(@NonNull PackageInstallerSession session) {
+            final boolean hasApex = sessionContainsApex(session);
+
+            // APEX checks. For single-package sessions, check if they contain an APEX. For
+            // multi-package sessions, find all the child sessions that contain an APEX.
+            if (hasApex) {
+                try {
+                    final List<PackageInfo> apexPackages =
+                            submitSessionToApexService(session);
+                    for (PackageInfo apexPackage : apexPackages) {
+                        validateApexSignature(apexPackage);
+                    }
+                } catch (PackageManagerException e) {
+                    session.setStagedSessionFailed(e.error, e.getMessage());
+                    return;
+                }
+            }
+
+            notifyPreRebootVerification_Apex_Complete(session.sessionId);
+        }
+
+        /**
+         * Pre-reboot verification state for apk files:
+         *   <p><ul>
+         *       <li>performs a dry-run install of apk</li>
+         *   </ul></p>
+         */
+        private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) {
+            if (!sessionContainsApk(session)) {
+                notifyPreRebootVerification_Apk_Complete(session.sessionId);
+                return;
+            }
+
+            try {
+                Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
+                        + session.sessionId + " by performing a dry-run install");
+
+                // verifyApksInSession will notify the handler when APK verification is complete
+                verifyApksInSession(session);
+                // TODO(b/118865310): abort the session on apexd.
+            } catch (PackageManagerException e) {
+                session.setStagedSessionFailed(e.error, e.getMessage());
+            }
+        }
+
+        /**
+         * Pre-reboot verification state for wrapping up:
+         * <p><ul>
+         *     <li>enables rollback if required</li>
+         *     <li>marks session as ready</li>
+         * </ul></p>
+         */
+        private void handlePreRebootVerification_End(@NonNull PackageInstallerSession session) {
+            if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+                // If rollback is enabled for this session, we call through to the RollbackManager
+                // with the list of sessions it must enable rollback for. Note that
+                // notifyStagedSession is a synchronous operation.
+                final IRollbackManager rm = IRollbackManager.Stub.asInterface(
+                        ServiceManager.getService(Context.ROLLBACK_SERVICE));
+                try {
+                    // NOTE: To stay consistent with the non-staged install flow, we don't fail the
+                    // entire install if rollbacks can't be enabled.
+                    if (!rm.notifyStagedSession(session.sessionId)) {
+                        Slog.e(TAG, "Unable to enable rollback for session: "
+                                + session.sessionId);
+                    }
+                } catch (RemoteException re) {
+                    Slog.e(TAG, "Failed to notifyStagedSession for session: "
+                            + session.sessionId, re);
+                }
+            }
+            // Before marking the session as ready, start checkpoint service if available
+            try {
+                IStorageManager storageManager = PackageHelper.getStorageManager();
+                if (storageManager.supportsCheckpoint()) {
+                    storageManager.startCheckpoint(1);
+                }
+            } catch (Exception e) {
+                // Failed to get hold of StorageManager
+                Slog.e(TAG, "Failed to get hold of StorageManager", e);
+                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+                        "Failed to get hold of StorageManager");
+                return;
+            }
+
+            // Proactively mark session as ready before calling apexd. Although this call order
+            // looks counter-intuitive, this is the easiest way to ensure that session won't end up
+            // in the inconsistent state:
+            //  - If device gets rebooted right before call to apexd, then apexd will never activate
+            //      apex files of this staged session. This will result in StagingManager failing
+            //      the session.
+            // On the other hand, if the order of the calls was inverted (first call apexd, then
+            // mark session as ready), then if a device gets rebooted right after the call to apexd,
+            // only apex part of the train will be applied, leaving device in an inconsistent state.
+            Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
+            session.setStagedSessionReady();
+            final boolean hasApex = sessionContainsApex(session);
+            if (!hasApex) {
+                // Session doesn't contain apex, nothing to do.
+                return;
+            }
+            try {
+                mApexManager.markStagedSessionReady(session.sessionId);
+            } catch (PackageManagerException e) {
+                session.setStagedSessionFailed(e.error, e.getMessage());
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index f78d263..add0b01 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -19,8 +19,6 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.security.keystore.IKeystoreService;
 import android.util.Slog;
 
 import com.android.internal.policy.IKeyguardService;
@@ -53,16 +51,11 @@
     private final LockPatternUtils mLockPatternUtils;
     private final StateCallback mCallback;
 
-    IKeystoreService mKeystoreService;
-
     public KeyguardStateMonitor(Context context, IKeyguardService service, StateCallback callback) {
         mLockPatternUtils = new LockPatternUtils(context);
         mCurrentUserId = ActivityManager.getCurrentUser();
         mCallback = callback;
 
-        mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager
-                .getService("android.security.keystore"));
-
         try {
             service.addStateMonitorCallback(this);
         } catch (RemoteException e) {
@@ -95,23 +88,6 @@
         mIsShowing = showing;
 
         mCallback.onShowingChanged();
-        int retry = 2;
-        while (retry > 0) {
-            try {
-                mKeystoreService.onKeyguardVisibilityChanged(showing, mCurrentUserId);
-                break;
-            } catch (RemoteException e) {
-                if (retry == 2) {
-                    Slog.w(TAG, "Error informing keystore of screen lock. Keystore may have died"
-                            + " -> refreshing service token and retrying");
-                    mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager
-                            .getService("android.security.keystore"));
-                } else {
-                    Slog.e(TAG, "Error informing keystore of screen lock after retrying once", e);
-                }
-                --retry;
-            }
-        }
     }
 
     @Override // Binder interface
@@ -123,10 +99,6 @@
         mCurrentUserId = userId;
     }
 
-    private synchronized int getCurrentUser() {
-        return mCurrentUserId;
-    }
-
     @Override // Binder interface
     public void onInputRestrictedStateChanged(boolean inputRestricted) {
         mInputRestricted = inputRestricted;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 26a623f..5d5c2a7 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2838,6 +2838,10 @@
     private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
             @Nullable final String reason, boolean wait) {
         if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
+            if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+                throw new UnsupportedOperationException(
+                        "Attempted userspace reboot on a device that doesn't support it");
+            }
             UserspaceRebootLogger.noteUserspaceRebootWasRequested();
         }
         if (mHandler == null || !mSystemReady) {
diff --git a/services/core/java/com/android/server/role/OWNERS b/services/core/java/com/android/server/role/OWNERS
new file mode 100644
index 0000000..b94d988
--- /dev/null
+++ b/services/core/java/com/android/server/role/OWNERS
@@ -0,0 +1,6 @@
+svetoslavganov@google.com
+moltmann@google.com
+zhanghai@google.com
+evanseverson@google.com
+eugenesusla@google.com
+ntmyren@google.com
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index b051bab..8ff2a1b 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -37,9 +37,9 @@
         }
 
         switch (cmd) {
-            case "suggestTelephonyTimeZone":
+            case "suggest_telephony_time_zone":
                 return runSuggestTelephonyTimeZone();
-            case "suggestManualTimeZone":
+            case "suggest_manual_time_zone":
                 return runSuggestManualTimeZone();
             default: {
                 return handleDefaultCommands(cmd);
@@ -105,9 +105,9 @@
         pw.println("Time Zone Detector (time_zone_detector) commands:");
         pw.println("  help");
         pw.println("    Print this help text.");
-        pw.println("  suggestTelephonyTimeZone");
+        pw.println("  suggest_telephony_time_zone");
         pw.println("    --suggestion <telephony suggestion opts>");
-        pw.println("  suggestManualTimeZone");
+        pw.println("  suggest_manual_time_zone");
         pw.println("    --suggestion <manual suggestion opts>");
         pw.println();
         ManualTimeZoneSuggestion.printCommandLineOpts(pw);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 0eb27cc..e0b3ad5 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -22,9 +22,12 @@
 import java.io.PrintWriter;
 
 /**
- * The interface for the class that implement the time detection algorithm used by the
+ * The interface for the class that implements the time detection algorithm used by the
  * {@link TimeZoneDetectorService}.
  *
+ * <p>The strategy uses suggestions to decide whether to modify the device's time zone setting
+ * and what to set it to.
+ *
  * <p>Most calls will be handled by a single thread but that is not true for all calls. For example
  * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
  * handle thread safety.
@@ -33,7 +36,9 @@
  */
 public interface TimeZoneDetectorStrategy {
 
-    /** Process the suggested manually-entered (i.e. user sourced) time zone. */
+    /**
+     * Suggests a time zone for the device using manually-entered (i.e. user sourced) information.
+     */
     void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion);
 
     /**
@@ -41,8 +46,7 @@
      * {@link TelephonyTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to
      * a specific {@link TelephonyTimeZoneSuggestion#getSlotIndex() slotIndex}.
      * See {@link TelephonyTimeZoneSuggestion} for an explanation of the metadata associated with a
-     * suggestion. The strategy uses suggestions to decide whether to modify the device's time zone
-     * setting and what to set it to.
+     * suggestion.
      */
     void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index cc33fb0..d318b1a 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -276,18 +276,6 @@
             return;
         }
 
-        // Special case handling for uninitialized devices. This should only happen once.
-        String newZoneId = bestTelephonySuggestion.suggestion.getZoneId();
-        if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
-            String cause = "Device has no time zone set. Attempting to set the device to the best"
-                    + " available suggestion."
-                    + " bestTelephonySuggestion=" + bestTelephonySuggestion
-                    + ", detectionReason=" + detectionReason;
-            Slog.i(LOG_TAG, cause);
-            setDeviceTimeZoneIfRequired(ORIGIN_TELEPHONY, newZoneId, cause);
-            return;
-        }
-
         boolean suggestionGoodEnough =
                 bestTelephonySuggestion.score >= TELEPHONY_SCORE_USAGE_THRESHOLD;
         if (!suggestionGoodEnough) {
@@ -301,6 +289,7 @@
 
         // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
         // zone ID.
+        String newZoneId = bestTelephonySuggestion.suggestion.getZoneId();
         if (newZoneId == null) {
             Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
                     + " bestTelephonySuggestion=" + bestTelephonySuggestion
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 7408dd4..5f5cd3c 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -53,6 +53,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.security.KeyStore;
 import android.service.trust.TrustAgentService;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -135,6 +136,33 @@
     @GuardedBy("mUserIsTrusted")
     private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray();
 
+    /**
+     * Stores the locked state for users on the device. There are three different type of users
+     * which are handled slightly differently:
+     * <ul>
+     *  <li> Users with real keyguard
+     *  These are users who can be switched to ({@link UserInfo#supportsSwitchToByUser()}). Their
+     *  locked state is derived by a combination of user secure state, keyguard state, trust agent
+     *  decision and biometric authentication result. These are updated via
+     *  {@link #refreshDeviceLockedForUser(int)} and result stored in {@link #mDeviceLockedForUser}.
+     *  <li> Managed profiles with unified challenge
+     *  Managed profile with unified challenge always shares the same locked state as their parent,
+     *  so their locked state is not recorded in  {@link #mDeviceLockedForUser}. Instead,
+     *  {@link ITrustManager#isDeviceLocked(int)} always resolves their parent user handle and
+     *  queries its locked state instead.
+     *  <li> Managed profiles with separate challenge
+     *  Locked state for profile with separate challenge is determined by other parts of the
+     *  framework (mostly PowerManager) and pushed to TrustManagerService via
+     *  {@link ITrustManager#setDeviceLockedForUser(int, boolean)}. Although in a corner case when
+     *  the profile has a separate but empty challenge, setting its {@link #mDeviceLockedForUser} to
+     *  {@code false} is actually done by {@link #refreshDeviceLockedForUser(int)}.
+     * </ul>
+     * TODO: Rename {@link ITrustManager#setDeviceLockedForUser(int, boolean)} to
+     * {@code setDeviceLockedForProfile} to better reflect its purpose. Unifying
+     * {@code setDeviceLockedForProfile} and {@link #setDeviceLockedForUser} would also be nice.
+     * At the moment they both update {@link #mDeviceLockedForUser} but have slightly different
+     * side-effects: one notifies trust agents while the other sends out a broadcast.
+     */
     @GuardedBy("mDeviceLockedForUser")
     private final SparseBooleanArray mDeviceLockedForUser = new SparseBooleanArray();
 
@@ -601,6 +629,10 @@
         }
     }
 
+    /**
+     * Update the user's locked state. Only applicable to users with a real keyguard
+     * ({@link UserInfo#supportsSwitchToByUser}) and unsecured managed profiles.
+     */
     private void refreshDeviceLockedForUser(int userId) {
         if (userId != UserHandle.USER_ALL && userId < UserHandle.USER_SYSTEM) {
             Log.e(TAG, "refreshDeviceLockedForUser(userId=" + userId + "): Invalid user handle,"
@@ -661,6 +693,15 @@
         }
         if (changed) {
             dispatchDeviceLocked(userId, locked);
+
+            KeyStore.getInstance().onUserLockedStateChanged(userId, locked);
+            // Also update the user's profiles who have unified challenge, since they
+            // share the same unlocked state (see {@link #isDeviceLocked(int)})
+            for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) {
+                if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) {
+                    KeyStore.getInstance().onUserLockedStateChanged(profileHandle, locked);
+                }
+            }
         }
     }
 
@@ -1194,6 +1235,10 @@
             return "0x" + Integer.toHexString(i);
         }
 
+        /**
+         * Changes the lock status for the given user. This is only applicable to managed profiles,
+         * other users should be handled by Keyguard.
+         */
         @Override
         public void setDeviceLockedForUser(int userId, boolean locked) {
             enforceReportPermission();
@@ -1204,6 +1249,9 @@
                     synchronized (mDeviceLockedForUser) {
                         mDeviceLockedForUser.put(userId, locked);
                     }
+
+                    KeyStore.getInstance().onUserLockedStateChanged(userId, locked);
+
                     if (locked) {
                         try {
                             ActivityManager.getService().notifyLockedProfile(userId);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 848971d..1592f23 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -4268,6 +4268,11 @@
 
     final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
             Intent resultData) {
+        if (!srec.attachedToProcess()) {
+            // Nothing to do if the caller is not attached, because this method should be called
+            // from an alive activity.
+            return false;
+        }
         final TaskRecord task = srec.getTaskRecord();
         final ArrayList<ActivityRecord> activities = task.mActivities;
         final int start = activities.indexOf(srec);
@@ -4321,14 +4326,14 @@
         }
 
         if (parent != null && foundParentInTask) {
+            final int callingUid = srec.info.applicationInfo.uid;
             final int parentLaunchMode = parent.info.launchMode;
             final int destIntentFlags = destIntent.getFlags();
             if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
                     parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
                     parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
                     (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
-                parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent,
-                        srec.packageName);
+                parent.deliverNewIntentLocked(callingUid, destIntent, srec.packageName);
             } else {
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
@@ -4341,10 +4346,10 @@
                             .setActivityInfo(aInfo)
                             .setResultTo(parent.appToken)
                             .setCallingPid(-1)
-                            .setCallingUid(parent.launchedFromUid)
-                            .setCallingPackage(parent.launchedFromPackage)
+                            .setCallingUid(callingUid)
+                            .setCallingPackage(srec.packageName)
                             .setRealCallingPid(-1)
-                            .setRealCallingUid(parent.launchedFromUid)
+                            .setRealCallingUid(callingUid)
                             .setComponentSpecified(true)
                             .execute();
                     foundParentInTask = res == ActivityManager.START_SUCCESS;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5b697ee..f37698d 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2763,6 +2763,11 @@
         return mRequest.intent;
     }
 
+    @VisibleForTesting
+    int getCallingUid() {
+        return mRequest.callingUid;
+    }
+
     ActivityStarter setReason(String reason) {
         mRequest.reason = reason;
         return this;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 54ab906e..ba4e11a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -343,7 +343,7 @@
      */
     final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
 
-    /** @see #computeCompatSmallestWidth(boolean, int, int, int, DisplayCutout) */
+    /** @see #computeCompatSmallestWidth(boolean, int, int, int) */
     private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
 
     /**
@@ -1715,7 +1715,7 @@
         config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
         config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
         config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode, dw,
-                dh, displayInfo.displayCutout);
+                dh);
         config.densityDpi = displayInfo.logicalDensityDpi;
 
         config.colorMode =
@@ -1800,8 +1800,7 @@
         mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
     }
 
-    private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
-            DisplayCutout displayCutout) {
+    private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh) {
         mTmpDisplayMetrics.setTo(mDisplayMetrics);
         final DisplayMetrics tmpDm = mTmpDisplayMetrics;
         final int unrotDw, unrotDh;
@@ -1812,19 +1811,21 @@
             unrotDw = dw;
             unrotDh = dh;
         }
-        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh,
-                displayCutout);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw,
-                displayCutout);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh,
-                displayCutout);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw,
-                displayCutout);
+        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw,
+                unrotDh);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh,
+                unrotDw);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw,
+                unrotDh);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh,
+                unrotDw);
         return sw;
     }
 
     private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
-            DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout) {
+            DisplayMetrics dm, int dw, int dh) {
+        final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
+                rotation).getDisplayCutout();
         dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
                 displayCutout);
         dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
@@ -1865,20 +1866,20 @@
             return;
         }
         int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
-                displayInfo.displayCutout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode,
-                displayInfo.displayCutout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode,
-                displayInfo.displayCutout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode,
-                displayInfo.displayCutout);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode);
         outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
         outConfig.screenLayout = sl;
     }
 
     private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
-            int uiMode, DisplayCutout displayCutout) {
+            int uiMode) {
+        // Get the display cutout at this rotation.
+        final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
+                rotation).getDisplayCutout();
+
         // Get the app screen size at this rotation.
         int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
         int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout);
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 0ab1a3e..4be4c89 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -10,3 +10,4 @@
 erosky@google.com
 riddlehsu@google.com
 louischang@google.com
+winsonc@google.com
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 72fc189..9d56263 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -28,12 +28,14 @@
 import android.graphics.Bitmap.Config;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.UserManagerInternal;
 import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.AtomicFile;
+import com.android.server.LocalServices;
 import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
 
 import java.io.File;
@@ -73,6 +75,7 @@
     private boolean mStarted;
     private final Object mLock = new Object();
     private final DirectoryResolver mDirectoryResolver;
+    private final UserManagerInternal mUserManagerInternal;
     private final float mReducedScale;
 
     /**
@@ -84,6 +87,7 @@
 
     TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
         mDirectoryResolver = resolver;
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
         if (service.mLowRamTaskSnapshotsAndRecents) {
             // Use very low res snapshots if we are using Go version of recents.
             mReducedScale = LOW_RAM_RECENTS_REDUCED_SCALE;
@@ -172,7 +176,7 @@
                     return;
                 }
             }
-            SystemClock.sleep(100);
+            SystemClock.sleep(DELAY_MS);
         }
     }
 
@@ -218,7 +222,7 @@
 
     private boolean createDirectory(int userId) {
         final File dir = getDirectory(userId);
-        return dir.exists() || dir.mkdirs();
+        return dir.exists() || dir.mkdir();
     }
 
     private void deleteSnapshot(int taskId, int userId) {
@@ -243,18 +247,26 @@
             android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
             while (true) {
                 WriteQueueItem next;
+                boolean isReadyToWrite = false;
                 synchronized (mLock) {
                     if (mPaused) {
                         next = null;
                     } else {
                         next = mWriteQueue.poll();
                         if (next != null) {
-                            next.onDequeuedLocked();
+                            if (next.isReady()) {
+                                isReadyToWrite = true;
+                                next.onDequeuedLocked();
+                            } else {
+                                mWriteQueue.addLast(next);
+                            }
                         }
                     }
                 }
                 if (next != null) {
-                    next.write();
+                    if (isReadyToWrite) {
+                        next.write();
+                    }
                     SystemClock.sleep(DELAY_MS);
                 }
                 synchronized (mLock) {
@@ -274,6 +286,13 @@
     };
 
     private abstract class WriteQueueItem {
+        /**
+         * @return {@code true} if item is ready to have {@link WriteQueueItem#write} called
+         */
+        boolean isReady() {
+            return true;
+        }
+
         abstract void write();
 
         /**
@@ -313,6 +332,11 @@
         }
 
         @Override
+        boolean isReady() {
+            return mUserManagerInternal.isUserUnlocked(mUserId);
+        }
+
+        @Override
         void write() {
             if (!createDirectory(mUserId)) {
                 Slog.e(TAG, "Unable to create snapshot directory for user dir="
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e1f8544..404863a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7717,14 +7717,19 @@
 
     @Override
     public void syncInputTransactions() {
-        waitForAnimationsToComplete();
+        long token = Binder.clearCallingIdentity();
+        try {
+            waitForAnimationsToComplete();
 
-        synchronized (mGlobalLock) {
-            mWindowPlacerLocked.performSurfacePlacementIfScheduled();
-            mRoot.forAllDisplays(displayContent ->
+            synchronized (mGlobalLock) {
+                mWindowPlacerLocked.performSurfacePlacementIfScheduled();
+                mRoot.forAllDisplays(displayContent ->
                     displayContent.getInputMonitor().updateInputWindowsImmediately());
+            }
+            new SurfaceControl.Transaction().syncInputWindows().apply(true);
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
-        new SurfaceControl.Transaction().syncInputWindows().apply(true);
     }
 
     private void waitForAnimationsToComplete() {
diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp
index e79612f..a99c0a3 100644
--- a/services/core/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/core/jni/com_android_server_AlarmManagerService.cpp
@@ -40,6 +40,7 @@
 #include <linux/rtc.h>
 
 #include <array>
+#include <limits>
 #include <memory>
 
 namespace android {
@@ -213,22 +214,20 @@
 static jint android_server_AlarmManagerService_setKernelTime(JNIEnv*, jobject, jlong nativeData, jlong millis)
 {
     AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
-    struct timeval tv;
-    int ret;
 
-    if (millis <= 0 || millis / 1000LL >= INT_MAX) {
+    if (millis <= 0 || millis / 1000LL >= std::numeric_limits<time_t>::max()) {
         return -1;
     }
 
-    tv.tv_sec = (time_t) (millis / 1000LL);
-    tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL);
+    struct timeval tv;
+    tv.tv_sec = (millis / 1000LL);
+    tv.tv_usec = ((millis % 1000LL) * 1000LL);
 
-    ALOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec);
+    ALOGD("Setting time of day to sec=%ld", tv.tv_sec);
 
-    ret = impl->setTime(&tv);
-
-    if(ret < 0) {
-        ALOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno));
+    int ret = impl->setTime(&tv);
+    if (ret < 0) {
+        ALOGW("Unable to set rtc to %ld: %s", tv.tv_sec, strerror(errno));
         ret = -1;
     }
     return ret;
diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp
index 636d110..a942108 100644
--- a/services/core/xsd/vts/Android.bp
+++ b/services/core/xsd/vts/Android.bp
@@ -36,7 +36,7 @@
     ],
     test_suites: [
         "general-tests",
-        "vts-core"
+        "vts"
     ],
     test_config: "vts_defaultPermissions_validate_test.xml",
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 03f64fc..cc8d258 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -78,6 +78,7 @@
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
 import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 import static android.provider.Telephony.Carriers.DPC_URI;
@@ -133,10 +134,6 @@
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.backup.IBackupManager;
-import android.app.timedetector.ManualTimeSuggestion;
-import android.app.timedetector.TimeDetector;
-import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.app.trust.TrustManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ActivityNotFoundException;
@@ -1957,14 +1954,6 @@
             return mContext.getSystemService(AlarmManager.class);
         }
 
-        TimeDetector getTimeDetector() {
-            return mContext.getSystemService(TimeDetector.class);
-        }
-
-        TimeZoneDetector getTimeZoneDetector() {
-            return mContext.getSystemService(TimeZoneDetector.class);
-        }
-
         ConnectivityManager getConnectivityManager() {
             return mContext.getSystemService(ConnectivityManager.class);
         }
@@ -5558,6 +5547,14 @@
         }
     }
 
+    private void enforceNetworkStackOrProfileOrDeviceOwner(ComponentName who) {
+        if (mContext.checkCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
+                == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+        enforceProfileOrDeviceOwner(who);
+    }
+
     @Override
     public boolean approveCaCert(String alias, int userId, boolean approval) {
         enforceManageUsers();
@@ -6485,7 +6482,7 @@
 
     @Override
     public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
-        enforceProfileOrDeviceOwner(admin);
+        enforceNetworkStackOrProfileOrDeviceOwner(admin);
 
         final int userId = mInjector.userHandleGetCallingUserId();
         final long token = mInjector.binderClearCallingIdentity();
@@ -10878,10 +10875,7 @@
         if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
             return false;
         }
-        ManualTimeSuggestion manualTimeSuggestion = TimeDetector.createManualTimeSuggestion(
-                millis, "DevicePolicyManagerService: setTime");
-        mInjector.binderWithCleanCallingIdentity(
-                () -> mInjector.getTimeDetector().suggestManualTime(manualTimeSuggestion));
+        mInjector.binderWithCleanCallingIdentity(() -> mInjector.getAlarmManager().setTime(millis));
         return true;
     }
 
@@ -10893,11 +10887,8 @@
         if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
             return false;
         }
-        ManualTimeZoneSuggestion manualTimeZoneSuggestion =
-                TimeZoneDetector.createManualTimeZoneSuggestion(
-                        timeZone, "DevicePolicyManagerService: setTimeZone");
         mInjector.binderWithCleanCallingIdentity(() ->
-                mInjector.getTimeZoneDetector().suggestManualTimeZone(manualTimeZoneSuggestion));
+                mInjector.getAlarmManager().setTimeZone(timeZone));
         return true;
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2b28ca2..23b1512 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -165,6 +165,8 @@
 import com.android.server.wm.WindowManagerGlobalLock;
 import com.android.server.wm.WindowManagerService;
 
+import libcore.timezone.ZoneInfoDb;
+
 import dalvik.system.VMRuntime;
 
 import java.io.File;
@@ -395,8 +397,9 @@
             // Default the timezone property to GMT if not set.
             //
             String timezoneProperty = SystemProperties.get("persist.sys.timezone");
-            if (timezoneProperty == null || timezoneProperty.isEmpty()) {
-                Slog.w(TAG, "Timezone not set; setting to GMT.");
+            if (!isValidTimeZoneId(timezoneProperty)) {
+                Slog.w(TAG, "persist.sys.timezone is not valid (" + timezoneProperty
+                        + "); setting to GMT.");
                 SystemProperties.set("persist.sys.timezone", "GMT");
             }
 
@@ -564,6 +567,12 @@
         throw new RuntimeException("Main thread loop unexpectedly exited");
     }
 
+    private static boolean isValidTimeZoneId(String timezoneProperty) {
+        return timezoneProperty != null
+                && !timezoneProperty.isEmpty()
+                && ZoneInfoDb.getInstance().hasTimeZone(timezoneProperty);
+    }
+
     private boolean isFirstBootOrUpgrade() {
         return mPackageManagerService.isFirstBoot() || mPackageManagerService.isDeviceUpgrading();
     }
@@ -990,7 +999,8 @@
             traceEnd();
 
             traceBeginAndSlog("StartTelephonyRegistry");
-            telephonyRegistry = new TelephonyRegistry(context);
+            telephonyRegistry = new TelephonyRegistry(
+                    context, new TelephonyRegistry.ConfigurationProvider());
             ServiceManager.addService("telephony.registry", telephonyRegistry);
             traceEnd();
 
@@ -1328,7 +1338,7 @@
 
             traceBeginAndSlog("StartIpSecService");
             try {
-                ipSecService = IpSecService.create(context);
+                ipSecService = IpSecService.create(context, networkManagement);
                 ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
             } catch (Throwable e) {
                 reportWtf("starting IpSec Service", e);
diff --git a/services/net/Android.bp b/services/net/Android.bp
index dbc2df8..8b444b0 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -12,14 +12,53 @@
         ":services.net-sources",
     ],
     static_libs: [
-        "dnsresolver_aidl_interface-V2-java",
-        "netd_aidl_interface-unstable-java",
+        "netd_aidl_interfaces-platform-java",
         "netlink-client",
         "networkstack-client",
         "net-utils-services-common",
     ],
 }
 
+// Version of services.net for usage by the wifi mainline module.
+// Note: This is compiled against module_current.
+// TODO(b/145825329): This should be moved to networkstack-client,
+// with dependencies moved to frameworks/libs/net right.
+java_library {
+    name: "services.net-module-wifi",
+    srcs: [
+        ":framework-services-net-module-wifi-shared-srcs",
+        ":net-module-utils-srcs",
+        "java/android/net/ip/IpClientCallbacks.java",
+        "java/android/net/ip/IpClientManager.java",
+        "java/android/net/ip/IpClientUtil.java",
+        "java/android/net/util/KeepalivePacketDataUtil.java",
+        "java/android/net/util/NetworkConstants.java",
+        "java/android/net/IpMemoryStore.java",
+        "java/android/net/NetworkMonitorManager.java",
+        "java/android/net/TcpKeepalivePacketData.java",
+    ],
+    sdk_version: "module_current",
+    libs: [
+        "unsupportedappusage",
+        "framework-wifi-util-lib",
+    ],
+    static_libs: [
+        // All the classes in netd_aidl_interface must be jarjar so they do not conflict with the
+        // classes generated by netd_aidl_interfaces-platform-java above.
+        "netd_aidl_interface-V3-java",
+        "netlink-client",
+        "networkstack-client",
+        "net-utils-services-common",
+    ],
+    apex_available: [
+        "com.android.wifi",
+    ],
+    visibility: [
+        "//frameworks/opt/net/wifi/service",
+        "//frameworks/opt/net/wifi/tests/wifitests",
+    ],
+}
+
 filegroup {
     name: "services-tethering-shared-srcs",
     srcs: [
diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java
index dcefb53..8df2e0d 100644
--- a/services/net/java/android/net/IpMemoryStore.java
+++ b/services/net/java/android/net/IpMemoryStore.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.net.networkstack.ModuleNetworkStackClient;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -41,7 +42,7 @@
         super(context);
         mService = new CompletableFuture<>();
         mTailNode = new AtomicReference<CompletableFuture<IIpMemoryStore>>(mService);
-        getNetworkStackClient().fetchIpMemoryStore(
+        getModuleNetworkStackClient(context).fetchIpMemoryStore(
                 new IIpMemoryStoreCallbacks.Stub() {
                     @Override
                     public void onIpMemoryStoreFetched(@NonNull final IIpMemoryStore memoryStore) {
@@ -85,8 +86,8 @@
     }
 
     @VisibleForTesting
-    protected NetworkStackClient getNetworkStackClient() {
-        return NetworkStackClient.getInstance();
+    protected ModuleNetworkStackClient getModuleNetworkStackClient(Context context) {
+        return ModuleNetworkStackClient.getInstance(context);
     }
 
     /** Gets an instance of the memory store */
diff --git a/services/net/java/android/net/TcpKeepalivePacketData.java b/services/net/java/android/net/TcpKeepalivePacketData.java
index aad75ae..c0c386b 100644
--- a/services/net/java/android/net/TcpKeepalivePacketData.java
+++ b/services/net/java/android/net/TcpKeepalivePacketData.java
@@ -74,6 +74,19 @@
         ipTtl = tcpDetails.ttl;
     }
 
+    private TcpKeepalivePacketData(final InetAddress srcAddress, int srcPort,
+            final InetAddress dstAddress, int dstPort, final byte[] data, int tcpSeq,
+            int tcpAck, int tcpWnd, int tcpWndScale, int ipTos, int ipTtl)
+            throws InvalidPacketException {
+        super(srcAddress, srcPort, dstAddress, dstPort, data);
+        this.tcpSeq = tcpSeq;
+        this.tcpAck = tcpAck;
+        this.tcpWnd = tcpWnd;
+        this.tcpWndScale = tcpWndScale;
+        this.ipTos = ipTos;
+        this.ipTtl = ipTtl;
+    }
+
     /**
      * Factory method to create tcp keepalive packet structure.
      */
@@ -139,10 +152,12 @@
     public boolean equals(@Nullable final Object o) {
         if (!(o instanceof TcpKeepalivePacketData)) return false;
         final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
-        return this.srcAddress.equals(other.srcAddress)
-                && this.dstAddress.equals(other.dstAddress)
-                && this.srcPort == other.srcPort
-                && this.dstPort == other.dstPort
+        final InetAddress srcAddress = getSrcAddress();
+        final InetAddress dstAddress = getDstAddress();
+        return srcAddress.equals(other.getSrcAddress())
+                && dstAddress.equals(other.getDstAddress())
+                && getSrcPort() == other.getSrcPort()
+                && getDstPort() == other.getDstPort()
                 && this.tcpAck == other.tcpAck
                 && this.tcpSeq == other.tcpSeq
                 && this.tcpWnd == other.tcpWnd
@@ -153,8 +168,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(srcAddress, dstAddress, srcPort, dstPort, tcpAck, tcpSeq, tcpWnd,
-                tcpWndScale, ipTos, ipTtl);
+        return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort(),
+                tcpAck, tcpSeq, tcpWnd, tcpWndScale, ipTos, ipTtl);
     }
 
     /**
@@ -169,7 +184,11 @@
 
     /** Write to parcel. */
     public void writeToParcel(Parcel out, int flags) {
-        super.writeToParcel(out, flags);
+        out.writeString(getSrcAddress().getHostAddress());
+        out.writeString(getDstAddress().getHostAddress());
+        out.writeInt(getSrcPort());
+        out.writeInt(getDstPort());
+        out.writeByteArray(getPacket());
         out.writeInt(tcpSeq);
         out.writeInt(tcpAck);
         out.writeInt(tcpWnd);
@@ -178,21 +197,32 @@
         out.writeInt(ipTtl);
     }
 
-    private TcpKeepalivePacketData(Parcel in) {
-        super(in);
-        tcpSeq = in.readInt();
-        tcpAck = in.readInt();
-        tcpWnd = in.readInt();
-        tcpWndScale = in.readInt();
-        ipTos = in.readInt();
-        ipTtl = in.readInt();
+    private static TcpKeepalivePacketData readFromParcel(Parcel in) throws InvalidPacketException {
+        InetAddress srcAddress = InetAddresses.parseNumericAddress(in.readString());
+        InetAddress dstAddress = InetAddresses.parseNumericAddress(in.readString());
+        int srcPort = in.readInt();
+        int dstPort = in.readInt();
+        byte[] packet = in.createByteArray();
+        int tcpSeq = in.readInt();
+        int tcpAck = in.readInt();
+        int tcpWnd = in.readInt();
+        int tcpWndScale = in.readInt();
+        int ipTos = in.readInt();
+        int ipTtl = in.readInt();
+        return new TcpKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, packet, tcpSeq,
+                tcpAck, tcpWnd, tcpWndScale, ipTos, ipTtl);
     }
 
     /** Parcelable Creator. */
     public static final @NonNull Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
             new Parcelable.Creator<TcpKeepalivePacketData>() {
                 public TcpKeepalivePacketData createFromParcel(Parcel in) {
-                    return new TcpKeepalivePacketData(in);
+                    try {
+                        return readFromParcel(in);
+                    } catch (InvalidPacketException e) {
+                        throw new IllegalArgumentException(
+                                "Invalid NAT-T keepalive data: " + e.getError());
+                    }
                 }
 
                 public TcpKeepalivePacketData[] newArray(int size) {
@@ -206,10 +236,12 @@
     @NonNull
     public TcpKeepalivePacketDataParcelable toStableParcelable() {
         final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
+        final InetAddress srcAddress = getSrcAddress();
+        final InetAddress dstAddress = getDstAddress();
         parcel.srcAddress = srcAddress.getAddress();
-        parcel.srcPort = srcPort;
+        parcel.srcPort = getSrcPort();
         parcel.dstAddress = dstAddress.getAddress();
-        parcel.dstPort = dstPort;
+        parcel.dstPort = getDstPort();
         parcel.seq = tcpSeq;
         parcel.ack = tcpAck;
         parcel.rcvWnd = tcpWnd;
@@ -221,10 +253,10 @@
 
     @Override
     public String toString() {
-        return "saddr: " + srcAddress
-                + " daddr: " + dstAddress
-                + " sport: " + srcPort
-                + " dport: " + dstPort
+        return "saddr: " + getSrcAddress()
+                + " daddr: " + getDstAddress()
+                + " sport: " + getSrcPort()
+                + " dport: " + getDstPort()
                 + " seq: " + tcpSeq
                 + " ack: " + tcpAck
                 + " wnd: " + tcpWnd
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
index 09e333e..db464e7 100644
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ b/services/net/java/android/net/ip/IpClientManager.java
@@ -21,6 +21,7 @@
 import android.net.NattKeepalivePacketData;
 import android.net.ProxyInfo;
 import android.net.TcpKeepalivePacketData;
+import android.net.shared.Layer2Information;
 import android.net.shared.ProvisioningConfiguration;
 import android.net.util.KeepalivePacketDataUtil;
 import android.os.Binder;
@@ -292,4 +293,20 @@
             Binder.restoreCallingIdentity(token);
         }
     }
+
+    /**
+     * Update the bssid, L2 key and group hint layer2 information.
+     */
+    public boolean updateLayer2Information(Layer2Information info) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mIpClient.updateLayer2Information(info.toStableParcelable());
+            return true;
+        } catch (RemoteException e) {
+            log("Error updating layer2 information", e);
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
 }
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index a3618b4..b329aee 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -22,7 +22,7 @@
 import android.net.DhcpResultsParcelable;
 import android.net.Layer2PacketParcelable;
 import android.net.LinkProperties;
-import android.net.NetworkStackClient;
+import android.net.networkstack.ModuleNetworkStackClient;
 import android.os.ConditionVariable;
 
 import java.io.FileDescriptor;
@@ -75,11 +75,11 @@
      *
      * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
      * {@link IIpClientCallbacks}.
-     * @see {@link NetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
+     * @see {@link ModuleNetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
      */
     public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
-        // TODO: migrate clients and remove context argument
-        NetworkStackClient.getInstance().makeIpClient(ifName, new IpClientCallbacksProxy(callback));
+        ModuleNetworkStackClient.getInstance(context)
+                .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
     }
 
     /**
diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
index 9a51729..4466ea0 100644
--- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java
+++ b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
@@ -20,6 +20,8 @@
 import android.net.NattKeepalivePacketData;
 import android.net.NattKeepalivePacketDataParcelable;
 
+import java.net.InetAddress;
+
 /** @hide */
 public final class KeepalivePacketDataUtil {
      /**
@@ -29,11 +31,12 @@
     public static NattKeepalivePacketDataParcelable toStableParcelable(
             NattKeepalivePacketData pkt) {
         final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable();
-
-        parcel.srcAddress = pkt.srcAddress.getAddress();
-        parcel.srcPort = pkt.srcPort;
-        parcel.dstAddress = pkt.dstAddress.getAddress();
-        parcel.dstPort = pkt.dstPort;
+        final InetAddress srcAddress = pkt.getSrcAddress();
+        final InetAddress dstAddress = pkt.getDstAddress();
+        parcel.srcAddress = srcAddress.getAddress();
+        parcel.srcPort = pkt.getSrcPort();
+        parcel.dstAddress = dstAddress.getAddress();
+        parcel.dstPort = pkt.getDstPort();
         return parcel;
     }
 }
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index a08b3e7..17d0bbf 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -27,7 +27,7 @@
         "services.net",
     ],
 
-    libs: ["ike-stubs"],
+    libs: ["android.net.ipsec.ike.stubs.system"],
 }
 
 //##################################################################
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 6fcc242..a3ccc6e 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -29,7 +29,7 @@
         "services.net",
     ],
 
-    libs: ["ike-stubs"],
+    libs: ["android.net.ipsec.ike.stubs.system"],
 }
 
 //##################################################################
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
deleted file mode 100644
index d192748..0000000
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ /dev/null
@@ -1,56 +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.server;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.os.storage.StorageManagerInternal;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class StorageManagerServiceTest {
-
-    private StorageManagerService mService;
-
-    @Mock private Context mContext;
-    @Mock private PackageManager mPm;
-    @Mock private PackageManagerInternal mPmi;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        LocalServices.removeServiceForTest(StorageManagerInternal.class);
-
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.addService(PackageManagerInternal.class, mPmi);
-
-        when(mContext.getPackageManager()).thenReturn(mPm);
-
-        mService = new StorageManagerService(mContext);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 0e24793..85e93df 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -249,6 +249,28 @@
     }
 
     @Test
+    public void testAllowRemoveOverrideNoOverride() throws Exception {
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L)
+                .addLoggingOnlyChangeWithId(2L)
+                .build();
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.some.package")
+                .build();
+        when(mPackageManager.getApplicationInfo(eq("com.some.package"), anyInt()))
+                .thenReturn(applicationInfo);
+
+        // Reject all override attempts.
+        // Force the validator to prevent overriding the change by using a user build.
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+        // Try to remove a non existing override, and it doesn't fail.
+        assertThat(compatConfig.removeOverride(1234L, "com.some.package")).isFalse();
+        assertThat(compatConfig.removeOverride(2L, "com.some.package")).isFalse();
+        compatConfig.removePackageOverrides("com.some.package");
+    }
+
+    @Test
     public void testRemovePackageOverride() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addEnabledChangeWithId(1234L)
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index ce5d6d9b..e295fee 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -28,10 +28,12 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.Build;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.internal.compat.CompatibilityChangeInfo;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -68,6 +70,48 @@
     }
 
     @Test
+    public void testListAllChanges() {
+        mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addEnabledChangeWithId(1L)
+                .addDisabledChangeWithIdAndName(2L, "change2")
+                .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
+                .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+                .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+                .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+                .addLoggingOnlyChangeWithId(7L)
+                .build();
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
+                new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
+                new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
+                new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, false, false,
+                        "description"),
+                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
+                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""),
+                new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, false, false, ""),
+                new CompatibilityChangeInfo(7L, "", -1, false, true, ""));
+    }
+
+    @Test
+    public void testListUIChanges() {
+        mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addEnabledChangeWithId(1L)
+                .addDisabledChangeWithIdAndName(2L, "change2")
+                .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
+                .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
+                .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
+                .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
+                .addLoggingOnlyChangeWithId(7L)
+                .build();
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
+                new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
+                new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
+                new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
+                new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""));
+    }
+
+    @Test
     public void testRegisterListenerToSameIdThrows() throws Exception {
         // Registering a listener to change 1 is successful.
         mPlatformCompat.registerListener(1, mListener1);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 0763aa2..2ce4c54 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -22,8 +22,6 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.backup.IBackupManager;
-import android.app.timedetector.TimeDetector;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
@@ -219,16 +217,6 @@
         AlarmManager getAlarmManager() {return services.alarmManager;}
 
         @Override
-        TimeDetector getTimeDetector() {
-            return services.timeDetector;
-        }
-
-        @Override
-        TimeZoneDetector getTimeZoneDetector() {
-            return services.timeZoneDetector;
-        }
-
-        @Override
         LockPatternUtils newLockPatternUtils() {
             return services.lockPatternUtils;
         }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7c0afed..9ae9824 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -63,9 +63,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.PasswordMetrics;
-import android.app.timedetector.ManualTimeSuggestion;
-import android.app.timezonedetector.ManualTimeZoneSuggestion;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -3476,19 +3473,7 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
         dpm.setTime(admin1, 0);
-
-        BaseMatcher<ManualTimeSuggestion> hasZeroTime = new BaseMatcher<ManualTimeSuggestion>() {
-            @Override
-            public boolean matches(Object item) {
-                final ManualTimeSuggestion suggestion = (ManualTimeSuggestion) item;
-                return suggestion.getUtcTime().getValue() == 0;
-            }
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("ManualTimeSuggestion{utcTime.value=0}");
-            }
-        };
-        verify(getServices().timeDetector).suggestManualTime(argThat(hasZeroTime));
+        verify(getServices().alarmManager).setTime(0);
     }
 
     public void testSetTimeFailWithPO() throws Exception {
@@ -3508,9 +3493,7 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
         dpm.setTimeZone(admin1, "Asia/Shanghai");
-        ManualTimeZoneSuggestion suggestion =
-                TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info");
-        verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion);
+        verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
     }
 
     public void testSetTimeZoneFailWithPO() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 960f670..35c1150 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -207,8 +207,6 @@
         switch (name) {
             case Context.ALARM_SERVICE:
                 return mMockSystemServices.alarmManager;
-            case Context.TIME_DETECTOR_SERVICE:
-                return mMockSystemServices.timeDetector;
             case Context.USER_SERVICE:
                 return mMockSystemServices.userManager;
             case Context.POWER_SERVICE:
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 16d5db9..8f0aeea 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -31,8 +31,6 @@
 import android.app.IActivityTaskManager;
 import android.app.NotificationManager;
 import android.app.backup.IBackupManager;
-import android.app.timedetector.TimeDetector;
-import android.app.timezonedetector.TimeZoneDetector;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
@@ -109,8 +107,6 @@
     public final TelephonyManager telephonyManager;
     public final AccountManager accountManager;
     public final AlarmManager alarmManager;
-    public final TimeDetector timeDetector;
-    public final TimeZoneDetector timeZoneDetector;
     public final KeyChain.KeyChainConnection keyChainConnection;
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
@@ -150,8 +146,6 @@
         telephonyManager = mock(TelephonyManager.class);
         accountManager = mock(AccountManager.class);
         alarmManager = mock(AlarmManager.class);
-        timeDetector = mock(TimeDetector.class);
-        timeZoneDetector = mock(TimeZoneDetector.class);
         keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
 
         // Package manager is huge, so we use a partial mock instead.
diff --git a/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java b/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java
new file mode 100644
index 0000000..d438a0e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java
@@ -0,0 +1,486 @@
+/*
+ * 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.emergency;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Unit test for EmergencyAffordanceService (EAS for short) which determines when
+ * should we enable Emergency Affordance feature (EA for short).
+ *
+ * Please refer to https://source.android.com/devices/tech/connect/emergency-affordance
+ * to see the details of the feature.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class EmergencyAffordanceServiceTest {
+
+    // Default country ISO that should enable EA. Value comes from resource
+    // com.android.internal.R.array.config_emergency_iso_country_codes
+    private static final String EMERGENCY_ISO_CODE = "in";
+    // Randomly picked country ISO that should not enable EA.
+    private static final String NON_EMERGENCY_ISO_CODE = "us";
+
+    // Valid values for Settings.Global.EMERGENCY_AFFORDANCE_NEEDED
+    private static final int OFF = 0; // which means feature disabled
+    private static final int ON  = 1; // which means feature enabled
+
+    private static final int ACTIVE_MODEM_COUNT = 2;
+
+    @Mock private Resources mResources;
+    @Mock private SubscriptionManager mSubscriptionManager;
+    @Mock private TelephonyManager mTelephonyManager;
+
+    private TestContext mServiceContext;
+    private MockContentResolver mContentResolver;
+    private OnSubscriptionsChangedListener mSubscriptionChangedListener;
+    private EmergencyAffordanceService mService;
+
+    // Testable Context that mocks resources, content resolver and system services
+    private class TestContext extends BroadcastInterceptingContext {
+        TestContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return mContentResolver;
+        }
+
+        @Override
+        public Resources getResources() {
+            return mResources;
+        }
+
+        @Override
+        public Object getSystemService(String name) {
+            switch (name) {
+                case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
+                    return mSubscriptionManager;
+                case Context.TELEPHONY_SERVICE:
+                    return mTelephonyManager;
+                default:
+                    return super.getSystemService(name);
+            }
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn(new String[] { EMERGENCY_ISO_CODE }).when(mResources)
+                .getStringArray(com.android.internal.R.array.config_emergency_iso_country_codes);
+
+        final Context context = InstrumentationRegistry.getContext();
+        mServiceContext = new TestContext(context);
+        mContentResolver = new MockContentResolver(mServiceContext);
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+
+        // Initialize feature off, to have constant starting
+        Settings.Global.putInt(mContentResolver, Settings.Global.EMERGENCY_AFFORDANCE_NEEDED, 0);
+        mService = new EmergencyAffordanceService(mServiceContext);
+    }
+
+    /**
+     * Verify if the device is not voice capable, the feature should be disabled.
+     */
+    @Test
+    public void testSettings_shouldBeOff_whenVoiceCapableIsFalse() throws Exception {
+        // Given: the device is not voice capable
+        // When:  setup device and boot service
+        setUpDevice(false /* withVoiceCapable */, true /* withEmergencyIsoInSim */,
+                true /* withEmergencyIsoInCell */);
+
+        // Then: EA setting will should be 0
+        verifyEmergencyAffordanceNeededSettings(OFF);
+    }
+
+    /**
+     * Verify the voice capable device is booted up without EA-enabled cell network, with
+     * no EA-enabled SIM installed, feature should be disabled.
+     */
+    @Test
+    public void testSettings_shouldBeOff_whenWithoutEAEanbledNetworkNorSim() throws Exception {
+        // Given: the device is voice capble, no EA-enable SIM, no EA-enabled Cell
+        setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */,
+                false /* withEmergencyIsoInCell */);
+
+        // Then: EA setting will should be 0
+        verifyEmergencyAffordanceNeededSettings(OFF);
+    }
+
+    /**
+     * Verify the voice capable device is booted up with EA-enabled SIM installed, the
+     * feature should be enabled.
+     */
+    @Test
+    public void testSettings_shouldBeOn_whenBootUpWithEAEanbledSim() throws Exception {
+        // Given: the device is voice capble, with EA-enable SIM, no EA-enabled Cell
+        setUpDevice(true /* withVoiceCapable */, true /* withEmergencyIsoInSim */,
+                false /* withEmergencyIsoInCell */);
+
+        // Then: EA setting will immediately update to 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+    }
+
+    /**
+     * Verify the voice capable device is booted up with EA-enabled Cell network, the
+     * feature should be enabled.
+     */
+    @Test
+    public void testSettings_shouldBeOn_whenBootUpWithEAEanbledCell() throws Exception {
+        // Given: the device is voice capble, with EA-enable SIM, with EA-enabled Cell
+        setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */,
+                true /* withEmergencyIsoInCell */);
+
+        // Then: EA setting will immediately update to 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+    }
+
+    /**
+     * Verify when device boot up with no EA-enabled SIM, but later install one,
+     * feature should be enabled.
+     */
+    @Test
+    public void testSettings_shouldBeOn_whenSubscriptionInfoChangedWithEmergencyIso()
+            throws Exception {
+        // Given: the device is voice capable, boot up with no EA-enabled SIM, no EA-enabled Cell
+        setUpDevice(true /* withVoiceCapable */, false/* withEmergencyIsoInSim */,
+                false /* withEmergencyIsoInCell */);
+
+        // When: Insert EA-enabled SIM and get notified
+        setUpSim(true /* withEmergencyIsoInSim */);
+        mSubscriptionChangedListener.onSubscriptionsChanged();
+
+        // Then: EA Setting will update to 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+    }
+
+    /**
+     * Verify when feature was on, device re-insert with no EA-enabled SIMs,
+     * feature should be disabled.
+     */
+    @Test
+    public void testSettings_shouldBeOff_whenSubscriptionInfoChangedWithoutEmergencyIso()
+            throws Exception {
+        // Given: the device is voice capable, no EA-enabled Cell, with EA-enabled SIM
+        setUpDevice(true /* withVoiceCapable */, true /* withEmergencyIsoInSim */,
+                false /* withEmergencyIsoInCell */);
+
+        // When: All SIMs are replaced with EA-disabled ones.
+        setUpSim(false /* withEmergencyIsoInSim */);
+        mSubscriptionChangedListener.onSubscriptionsChanged();
+
+        // Then: EA Setting will update to 0
+        verifyEmergencyAffordanceNeededSettings(OFF);
+    }
+
+    /**
+     * Verify when device boot up with no EA-enabled Cell, but later move into one,
+     * feature should be enabled.
+     */
+    @Test
+    public void testSettings_shouldBeOn_whenCountryIsoChangedWithEmergencyIso()
+            throws Exception {
+        // Given: the device is voice capable, boot up with no EA-enabled SIM, no EA-enabled Cell
+        setUpDevice(true /* withVoiceCapable */, false/* withEmergencyIsoInSim */,
+                false /* withEmergencyIsoInCell */);
+
+        // When: device locale change to EA-enabled Cell and get notified
+        resetCell(true /* withEmergencyIsoInSim */);
+        sendBroadcastNetworkCountryChanged(EMERGENCY_COUNTRY_ISO);
+
+        // Then: EA Setting will update to 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+    }
+
+    /**
+     * Verify when device boot up with  EA-enabled Cell, but later move out of it,
+     * feature should be enabled.
+     */
+    @Test
+    public void testSettings_shouldBeOff_whenCountryIsoChangedWithoutEmergencyIso()
+            throws Exception {
+        // Given: the device is voice capable, boot up with no EA-enabled SIM, with EA-enabled Cell
+        setUpDevice(true /* withVoiceCapable */, false/* withEmergencyIsoInSim */,
+                true /* withEmergencyIsoInCell */);
+
+        // When: device locale change to no EA-enabled Cell and get notified
+        resetCell(false /* withEmergencyIsoInSim */);
+        sendBroadcastNetworkCountryChanged(NON_EMERGENCY_COUNTRY_ISO);
+
+        // Then: EA Setting will update to 0
+        verifyEmergencyAffordanceNeededSettings(OFF);
+    }
+    /**
+     * Verify if device is not in EA-enabled Mobile Network without EA-enable SIM(s) installed,
+     * when receive SubscriptionInfo change, the feature should not be enabled.
+     */
+    @Test
+    public void testSettings_shouldBeOff_whenNoEmergencyIsoInCellNorSim() throws Exception {
+        // Given: the device is voice capable, no EA-enabled Cell, no EA-enabled SIM
+        setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */,
+                false /* withEmergencyIsoInCell */);
+
+        // When: Subscription changed event received
+        mSubscriptionChangedListener.onSubscriptionsChanged();
+
+        // Then: EA Settings should be 0
+        verifyEmergencyAffordanceNeededSettings(OFF);
+    }
+
+    /**
+     * Verify while feature was on, when device receive empty country iso change, while APM is
+     * enabled, feature status should keep on.
+     */
+    @Test
+    public void testSettings_shouldOn_whenReceiveEmptyISOWithAPMEnabled() throws Exception {
+        // Given: the device is voice capable,  no EA-enabled SIM, with EA-enabled Cell
+        setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */,
+                true /* withEmergencyIsoInCell */);
+
+        // Then: EA Settings will update to 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+
+        // When: Airplane mode is enabled, and we receive EMPTY ISO in locale change
+        setAirplaneMode(true);
+        sendBroadcastNetworkCountryChanged(EMPTY_COUNTRY_ISO);
+
+        // Then: EA Settings will keep to 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+    }
+
+    /**
+     * Verify while feature was on, when device receive empty country iso change, while APM is
+     * disabled, feature should be disabled.
+     */
+    @Test
+    public void testSettings_shouldOff_whenReceiveEmptyISOWithAPMDisabled() throws Exception {
+        // Given: the device is voice capable,  no EA-enabled SIM, with EA-enabled Cell
+        setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */,
+                true /* withEmergencyIsoInCell */);
+
+        // Then: EA Settings will update to 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+
+        // When: Airplane mode is disabled, and we receive valid empty ISO in locale change
+        setUpCell(false /* withEmergencyIsoInCell */);
+        setAirplaneMode(false);
+        sendBroadcastNetworkCountryChanged(EMPTY_COUNTRY_ISO);
+
+        // Then: EA Settings will keep to 0
+        verifyEmergencyAffordanceNeededSettings(OFF);
+    }
+
+    /**
+     * Verify when airplane mode is turn on and off in cell network with EA-enabled ISO,
+     * feature should keep enabled.
+     */
+    @Test
+    public void testSettings_shouldBeOn_whenAirplaneModeOnOffWithEmergencyIsoInCell()
+            throws Exception {
+        // Given: the device is voice capable,  no EA-enabled SIM, with EA-enabled Cell
+        setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */,
+                true /* withEmergencyIsoInCell */);
+
+        // When: Device receive locale change with EA-enabled iso
+        sendBroadcastNetworkCountryChanged(EMERGENCY_COUNTRY_ISO);
+
+        // When: Airplane mode is disabled
+        setAirplaneMode(false);
+
+        // Then: EA Settings will keep with 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+
+        // When: Airplane mode is enabled
+        setAirplaneMode(true);
+
+        // Then: EA Settings is still 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+    }
+
+    /**
+     * Verify when airplane mode is turn on and off with EA-enabled ISO in SIM,
+     * feature should keep enabled.
+     */
+    @Test
+    public void testSettings_shouldBeOn_whenAirplaneModeOnOffWithEmergencyIsoInSim()
+            throws Exception {
+        // Given: the device is voice capable, no EA-enabled Cell Network, with EA-enabled SIM
+        setUpDevice(true /* withVoiceCapable */, true /* withEmergencyIsoInSim */,
+                false /* withEmergencyIsoInCell */);
+
+        // When: Airplane mode is disabled
+        setAirplaneMode(false);
+
+        // Then: EA Settings will keep with 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+
+        // When: Airplane mode is enabled
+        setAirplaneMode(true);
+
+        // Then: EA Settings is still 1
+        verifyEmergencyAffordanceNeededSettings(ON);
+    }
+
+    // EAS reads voice capable during boot up and cache it. To test non voice capable device,
+    // we can not put this in setUp
+    private void setUpDevice(boolean withVoiceCapable, boolean withEmergencyIsoInSim,
+            boolean withEmergencyIsoInCell) throws Exception {
+        setUpVoiceCapable(withVoiceCapable);
+
+        setUpSim(withEmergencyIsoInSim);
+
+        setUpCell(withEmergencyIsoInCell);
+
+        // bypass onStart which is used to publish binder service and need sepolicy policy update
+        // mService.onStart();
+
+        mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
+
+        if (!withVoiceCapable) {
+            return;
+        }
+
+        captureSubscriptionChangeListener();
+    }
+
+    private void setUpVoiceCapable(boolean voiceCapable) {
+        doReturn(voiceCapable).when(mTelephonyManager).isVoiceCapable();
+    }
+
+    private static final Supplier<String> EMPTY_COUNTRY_ISO = () -> "";
+    private static final Supplier<String> EMERGENCY_COUNTRY_ISO = () -> EMERGENCY_ISO_CODE;
+    private static final Supplier<String> NON_EMERGENCY_COUNTRY_ISO = () -> NON_EMERGENCY_ISO_CODE;
+    private void sendBroadcastNetworkCountryChanged(Supplier<String> countryIso) {
+        Intent intent = new Intent(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
+        intent.putExtra(TelephonyManager.EXTRA_NETWORK_COUNTRY, countryIso.get());
+        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, 0);
+        mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
+    private void setUpSim(boolean withEmergencyIsoInSim) {
+        List<SubscriptionInfo> subInfos = getSubscriptionInfoList(withEmergencyIsoInSim);
+        doReturn(subInfos).when(mSubscriptionManager).getActiveSubscriptionInfoList();
+    }
+
+    private void setUpCell(boolean withEmergencyIsoInCell) {
+        doReturn(ACTIVE_MODEM_COUNT).when(mTelephonyManager).getActiveModemCount();
+        doReturn(NON_EMERGENCY_ISO_CODE).when(mTelephonyManager).getNetworkCountryIso(0);
+        doReturn(withEmergencyIsoInCell ? EMERGENCY_ISO_CODE : NON_EMERGENCY_ISO_CODE)
+                .when(mTelephonyManager).getNetworkCountryIso(1);
+    }
+
+    private void resetCell(boolean withEmergencyIsoInCell) {
+        doReturn(withEmergencyIsoInCell ? EMERGENCY_ISO_CODE : NON_EMERGENCY_ISO_CODE)
+                .when(mTelephonyManager).getNetworkCountryIso(1);
+    }
+
+    private void captureSubscriptionChangeListener() {
+        final ArgumentCaptor<OnSubscriptionsChangedListener> subChangedListenerCaptor =
+                ArgumentCaptor.forClass(OnSubscriptionsChangedListener.class);
+        verify(mSubscriptionManager).addOnSubscriptionsChangedListener(
+                subChangedListenerCaptor.capture());
+        mSubscriptionChangedListener = subChangedListenerCaptor.getValue();
+    }
+
+    private void setAirplaneMode(boolean enabled) {
+        // Change the system settings
+        Settings.Global.putInt(mContentResolver, Settings.Global.AIRPLANE_MODE_ON,
+                enabled ? 1 : 0);
+
+        // Post the intent
+        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.putExtra("state", enabled);
+        mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
+    private List<SubscriptionInfo> getSubscriptionInfoList(boolean withEmergencyIso) {
+        List<SubscriptionInfo> subInfos = new ArrayList<>(2);
+
+        // Test with Multiple SIMs. SIM1 is a non-EA SIM
+        // Only country iso is valuable, all other info are filled with dummy values
+        SubscriptionInfo subInfo = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "T-mobile",
+                "T-mobile", 0, 255, "12345", 0, null,
+                "310", "226", NON_EMERGENCY_ISO_CODE, false, null, null);
+        subInfos.add(subInfo);
+
+        // SIM2 can configured to be non-EA or EA SIM according parameter withEmergencyIso
+        SubscriptionInfo subInfo2 = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "Airtel",
+                "Aritel", 0, 255, "12345", 0, null, "310", "226",
+                withEmergencyIso ? EMERGENCY_ISO_CODE : NON_EMERGENCY_ISO_CODE,
+                false, null, null);
+        subInfos.add(subInfo2);
+
+        return subInfos;
+    }
+
+    // EAS has handler thread to perform heavy work, while FakeSettingProvider does not support
+    // ContentObserver. To make sure consistent result, we use a simple sleep & retry to wait for
+    // real work finished before verify result.
+    private static final int TIME_DELAY_BEFORE_VERIFY_IN_MS = 50;
+    private static final int RETRIES_BEFORE_VERIFY = 20;
+    private void verifyEmergencyAffordanceNeededSettings(int expected) throws Exception {
+        try {
+            int ct = 0;
+            int actual = -1;
+            while (ct++ < RETRIES_BEFORE_VERIFY
+                    && (actual = Settings.Global.getInt(mContentResolver,
+                    Settings.Global.EMERGENCY_AFFORDANCE_NEEDED)) != expected) {
+                Thread.sleep(TIME_DELAY_BEFORE_VERIFY_IN_MS);
+            }
+            assertEquals(expected, actual);
+        } catch (Settings.SettingNotFoundException e) {
+            fail("SettingNotFoundException thrown for Settings.Global.EMERGENCY_AFFORDANCE_NEEDED");
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 1638329..3465ea9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -73,7 +73,6 @@
     private static final int HDMI_3_PHYSICAL_ADDRESS = 0x2300;
     private int mInvokeDeviceEventState;
     private HdmiDeviceInfo mDeviceInfo;
-    private boolean mMutingEnabled;
     private boolean mArcSupport;
     private HdmiPortInfo[] mHdmiPortInfo;
 
@@ -154,8 +153,6 @@
                 @Override
                 boolean readBooleanSystemProperty(String key, boolean defVal) {
                     switch (key) {
-                        case Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE:
-                            return mMutingEnabled;
                         case Constants.PROPERTY_ARC_SUPPORT:
                             return mArcSupport;
                         default:
@@ -209,7 +206,6 @@
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
         mTestLooper.dispatchAll();
         mNativeWrapper.clearResultMessages();
-        mMutingEnabled = true;
         mArcSupport = true;
         mInvokeDeviceEventState = 0;
         mDeviceInfo = null;
diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
index b0def60..21db5f8 100644
--- a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
@@ -58,6 +58,16 @@
                 fakeHwLight(105, LightsManager.LIGHT_TYPE_MICROPHONE, 2)
             };
         }
+
+        @Override
+        public int getInterfaceVersion() {
+            return this.VERSION;
+        }
+
+        @Override
+        public String getInterfaceHash() {
+            return this.HASH;
+        }
     };
 
     private static HwLight fakeHwLight(int id, int type, int ordinal) {
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index ece937a..f1662ff 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1006,7 +1006,7 @@
 
         // pretend that 512 bytes total have happened
         stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
+                .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
         when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
                 .thenReturn(stats.getTotalBytes());
 
@@ -1198,11 +1198,11 @@
             history.recordData(start, end,
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
             stats.clear();
-            stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.addEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
 
             reset(mNotifManager);
@@ -1226,9 +1226,9 @@
             history.recordData(start, end,
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
             stats.clear();
-            stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
-            stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
 
             reset(mNotifManager);
@@ -1260,7 +1260,7 @@
         // bring up wifi network with metered policy
         state = new NetworkState[] { buildWifi() };
         stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
 
         {
             when(mConnManager.getAllNetworkState()).thenReturn(state);
@@ -1692,7 +1692,7 @@
         final int CYCLE_DAY = 15;
 
         final NetworkStats stats = new NetworkStats(0L, 1);
-        stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+        stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
                 2999, 1, 2000, 1, 0);
         when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
                 .thenReturn(stats.getTotalBytes());
@@ -1716,7 +1716,7 @@
         reset(mStatsService);
 
         // Increase the usage.
-        stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+        stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
                 1000, 1, 999, 1, 0);
         when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
                 .thenReturn(stats.getTotalBytes());
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 143dc28..c086718 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -21,8 +21,10 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -68,7 +70,9 @@
     @Before
     public void setUp() throws RemoteException {
         mContext = InstrumentationRegistry.getInstrumentation().getContext();
-        mApexManager = new ApexManager.ApexManagerImpl(mContext, mApexService);
+        ApexManager.ApexManagerImpl managerImpl = spy(new ApexManager.ApexManagerImpl(mContext));
+        doReturn(mApexService).when(managerImpl).waitForApexService();
+        mApexManager = managerImpl;
     }
 
     @Test
@@ -216,11 +220,11 @@
     }
 
     @Test
-    public void testAbortActiveSession_remoteException() throws RemoteException {
-        doThrow(RemoteException.class).when(mApexService).abortActiveSession();
+    public void testRevertActiveSessions_remoteException() throws RemoteException {
+        doThrow(RemoteException.class).when(mApexService).revertActiveSessions();
 
         try {
-            assertThat(mApexManager.abortActiveSession()).isFalse();
+            assertThat(mApexManager.revertActiveSessions()).isFalse();
         } catch (Exception e) {
             throw new AssertionError("ApexManager should not raise Exception");
         }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 039c2b4..da34e1b 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -76,39 +76,7 @@
     }
 
     @Test(expected = SecurityException.class)
-    public void testSuggestTelephonyTime_withoutPermission() {
-        doThrow(new SecurityException("Mock"))
-                .when(mMockContext).enforceCallingPermission(anyString(), any());
-        TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion();
-
-        try {
-            mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
-            fail();
-        } finally {
-            verify(mMockContext).enforceCallingPermission(
-                    eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
-                    anyString());
-        }
-    }
-
-    @Test
-    public void testSuggestTelephonyTimeZone() throws Exception {
-        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
-
-        TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion();
-        mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
-        mTestHandler.assertTotalMessagesEnqueued(1);
-
-        verify(mMockContext).enforceCallingPermission(
-                eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
-                anyString());
-
-        mTestHandler.waitForMessagesToBeProcessed();
-        mStubbedTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion);
-    }
-
-    @Test(expected = SecurityException.class)
-    public void testSuggestManualTime_withoutPermission() {
+    public void testSuggestManualTimeZone_withoutPermission() {
         doThrow(new SecurityException("Mock"))
                 .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
         ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
@@ -139,6 +107,38 @@
         mStubbedTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(timeZoneSuggestion);
     }
 
+    @Test(expected = SecurityException.class)
+    public void testSuggestTelephonyTimeZone_withoutPermission() {
+        doThrow(new SecurityException("Mock"))
+                .when(mMockContext).enforceCallingPermission(anyString(), any());
+        TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion();
+
+        try {
+            mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
+            fail();
+        } finally {
+            verify(mMockContext).enforceCallingPermission(
+                    eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
+                    anyString());
+        }
+    }
+
+    @Test
+    public void testSuggestTelephonyTimeZone() throws Exception {
+        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+        TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion();
+        mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
+
+        verify(mMockContext).enforceCallingPermission(
+                eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
+                anyString());
+
+        mTestHandler.waitForMessagesToBeProcessed();
+        mStubbedTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion);
+    }
+
     @Test
     public void testDump() {
         when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
@@ -165,6 +165,10 @@
         mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneDetectionChangedCalled();
     }
 
+    private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
+        return new ManualTimeZoneSuggestion("TestZoneId");
+    }
+
     private static TelephonyTimeZoneSuggestion createTelephonyTimeZoneSuggestion() {
         int slotIndex = 1234;
         return new TelephonyTimeZoneSuggestion.Builder(slotIndex)
@@ -174,26 +178,22 @@
                 .build();
     }
 
-    private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
-        return new ManualTimeZoneSuggestion("TestZoneId");
-    }
-
     private static class StubbedTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
 
         // Call tracking.
-        private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
         private ManualTimeZoneSuggestion mLastManualSuggestion;
+        private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
         private boolean mHandleAutoTimeZoneDetectionChangedCalled;
         private boolean mDumpCalled;
 
         @Override
-        public void suggestTelephonyTimeZone(TelephonyTimeZoneSuggestion timeZoneSuggestion) {
-            mLastTelephonySuggestion = timeZoneSuggestion;
+        public void suggestManualTimeZone(ManualTimeZoneSuggestion timeZoneSuggestion) {
+            mLastManualSuggestion = timeZoneSuggestion;
         }
 
         @Override
-        public void suggestManualTimeZone(ManualTimeZoneSuggestion timeZoneSuggestion) {
-            mLastManualSuggestion = timeZoneSuggestion;
+        public void suggestTelephonyTimeZone(TelephonyTimeZoneSuggestion timeZoneSuggestion) {
+            mLastTelephonySuggestion = timeZoneSuggestion;
         }
 
         @Override
@@ -207,18 +207,18 @@
         }
 
         void resetCallTracking() {
-            mLastTelephonySuggestion = null;
             mLastManualSuggestion = null;
+            mLastTelephonySuggestion = null;
             mHandleAutoTimeZoneDetectionChangedCalled = false;
             mDumpCalled = false;
         }
 
-        void verifySuggestTelephonyTimeZoneCalled(TelephonyTimeZoneSuggestion expectedSuggestion) {
-            assertEquals(expectedSuggestion, mLastTelephonySuggestion);
+        void verifySuggestManualTimeZoneCalled(ManualTimeZoneSuggestion expectedSuggestion) {
+            assertEquals(expectedSuggestion, mLastManualSuggestion);
         }
 
-        public void verifySuggestManualTimeZoneCalled(ManualTimeZoneSuggestion expectedSuggestion) {
-            assertEquals(expectedSuggestion, mLastManualSuggestion);
+        void verifySuggestTelephonyTimeZoneCalled(TelephonyTimeZoneSuggestion expectedSuggestion) {
+            assertEquals(expectedSuggestion, mLastTelephonySuggestion);
         }
 
         void verifyHandleAutoTimeZoneDetectionChangedCalled() {
@@ -229,5 +229,4 @@
             assertTrue(mDumpCalled);
         }
     }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index ba30967..30bb12e 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -128,43 +128,69 @@
     }
 
     @Test
-    public void testFirstPlausibleTelephonySuggestionAcceptedWhenTimeZoneUninitialized() {
+    public void testTelephonySuggestionsWhenTimeZoneUninitialized() {
+        assertTrue(TELEPHONY_SCORE_LOW < TELEPHONY_SCORE_USAGE_THRESHOLD);
+        assertTrue(TELEPHONY_SCORE_HIGH >= TELEPHONY_SCORE_USAGE_THRESHOLD);
         SuggestionTestCase testCase = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
                 QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, TELEPHONY_SCORE_LOW);
-        TelephonyTimeZoneSuggestion lowQualitySuggestion =
-                testCase.createSuggestion(SLOT_INDEX1, "America/New_York");
+        SuggestionTestCase testCase2 = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+                QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
 
-        // The device time zone setting is left uninitialized.
         Script script = new Script()
                 .initializeAutoTimeZoneDetection(true);
 
-        // The very first suggestion will be taken.
-        script.suggestTelephonyTimeZone(lowQualitySuggestion)
-                .verifyTimeZoneSetAndReset(lowQualitySuggestion);
+        // A low quality suggestions will not be taken: The device time zone setting is left
+        // uninitialized.
+        {
+            TelephonyTimeZoneSuggestion lowQualitySuggestion =
+                    testCase.createSuggestion(SLOT_INDEX1, "America/New_York");
 
-        // Assert internal service state.
-        QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
-                new QualifiedTelephonyTimeZoneSuggestion(
-                        lowQualitySuggestion, testCase.expectedScore);
-        assertEquals(expectedScoredSuggestion,
-                mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
-        assertEquals(expectedScoredSuggestion,
-                mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
+            script.suggestTelephonyTimeZone(lowQualitySuggestion)
+                    .verifyTimeZoneNotSet();
 
-        // Another low quality suggestion will be ignored now that the setting is initialized.
-        TelephonyTimeZoneSuggestion lowQualitySuggestion2 =
-                testCase.createSuggestion(SLOT_INDEX1, "America/Los_Angeles");
-        script.suggestTelephonyTimeZone(lowQualitySuggestion2)
-                .verifyTimeZoneNotSet();
+            // Assert internal service state.
+            QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
+                    new QualifiedTelephonyTimeZoneSuggestion(
+                            lowQualitySuggestion, testCase.expectedScore);
+            assertEquals(expectedScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+            assertEquals(expectedScoredSuggestion,
+                    mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
+        }
 
-        // Assert internal service state.
-        QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion2 =
-                new QualifiedTelephonyTimeZoneSuggestion(
-                        lowQualitySuggestion2, testCase.expectedScore);
-        assertEquals(expectedScoredSuggestion2,
-                mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
-        assertEquals(expectedScoredSuggestion2,
-                mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
+        // A good quality suggestion will be used.
+        {
+            TelephonyTimeZoneSuggestion goodQualitySuggestion =
+                    testCase2.createSuggestion(SLOT_INDEX1, "Europe/London");
+            script.suggestTelephonyTimeZone(goodQualitySuggestion)
+                    .verifyTimeZoneSetAndReset(goodQualitySuggestion);
+
+            // Assert internal service state.
+            QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
+                    new QualifiedTelephonyTimeZoneSuggestion(
+                            goodQualitySuggestion, testCase2.expectedScore);
+            assertEquals(expectedScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+            assertEquals(expectedScoredSuggestion,
+                    mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
+        }
+
+        // A low quality suggestion will be accepted, but not used to set the device time zone.
+        {
+            TelephonyTimeZoneSuggestion lowQualitySuggestion2 =
+                    testCase.createSuggestion(SLOT_INDEX1, "America/Los_Angeles");
+            script.suggestTelephonyTimeZone(lowQualitySuggestion2)
+                    .verifyTimeZoneNotSet();
+
+            // Assert internal service state.
+            QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion =
+                    new QualifiedTelephonyTimeZoneSuggestion(
+                            lowQualitySuggestion2, testCase.expectedScore);
+            assertEquals(expectedScoredSuggestion,
+                    mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1));
+            assertEquals(expectedScoredSuggestion,
+                    mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
+        }
     }
 
     /**
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 2077ecb..96c69af 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -197,7 +197,8 @@
         final String PREFIX = "Launcher: ComponentInfo{";
         final String POSTFIX = "}";
         final List<String> result = runShortcutCommandForSuccess(
-                instrumentation, "get-default-launcher");
+                instrumentation, "get-default-launcher --user "
+                + instrumentation.getContext().getUserId());
         for (String s : result) {
             if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
                 return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 32263e1..3bc0861 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -1292,6 +1292,22 @@
     }
 
     @Test
+    public void testLightsCheckCurrentUser() {
+        final Notification n = new Builder(getContext(), "test")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
+        int userId = mUser.getIdentifier() + 10;
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid,
+                mPid, n, UserHandle.of(userId), null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn,
+                new NotificationChannel("test", "test", IMPORTANCE_HIGH));
+
+        mService.buzzBeepBlinkLocked(r);
+        verifyNeverLights();
+        assertFalse(r.isInterruptive());
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
+    }
+
+    @Test
     public void testListenerHintCall() throws Exception {
         NotificationRecord r = getCallRecord(1, true);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index f390ae6..10b41d2 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -122,6 +122,7 @@
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenPolicy;
+import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
@@ -391,7 +392,7 @@
                 mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
                 mGroupHelper, mAm, mAppUsageStats,
                 mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
-                mAppOpsManager, mUm);
+                mAppOpsManager, mUm, mock(TelephonyManager.class));
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         mService.setAudioManager(mAudioManager);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index f37ff11..501161e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -47,6 +47,7 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
@@ -140,7 +141,7 @@
                     mock(UsageStatsManagerInternal.class),
                     mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class),
                     mock(UriGrantsManagerInternal.class),
-                    mock(AppOpsManager.class), mUm);
+                    mock(AppOpsManager.class), mUm, mock(TelephonyManager.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index bde0ef6..ff27b9b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -28,7 +28,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
@@ -54,8 +54,11 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 
+import android.app.ActivityManager;
+import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.os.UserHandle;
@@ -82,8 +85,9 @@
     @Before
     public void setUp() throws Exception {
         mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
-        mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */));
+        mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        spyOn(mStack);
         mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
     }
 
@@ -1078,6 +1082,37 @@
         assertTrue(listener.mChanged);
     }
 
+    @Test
+    public void testNavigateUpTo() {
+        final ActivityStartController controller = mock(ActivityStartController.class);
+        final ActivityStarter starter = new ActivityStarter(controller,
+                mService, mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
+        doReturn(controller).when(mService).getActivityStartController();
+        spyOn(starter);
+        doReturn(ActivityManager.START_SUCCESS).when(starter).execute();
+
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setTask(mTask).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(mTask)
+                .setUid(firstActivity.getUid() + 1).build();
+        doReturn(starter).when(controller).obtainStarter(eq(firstActivity.intent), anyString());
+
+        final IApplicationThread thread = secondActivity.app.getThread();
+        secondActivity.app.setThread(null);
+        // This should do nothing from a non-attached caller.
+        assertFalse(mStack.navigateUpToLocked(secondActivity /* source record */,
+                firstActivity.intent /* destIntent */, 0 /* resultCode */, null /* resultData */));
+
+        secondActivity.app.setThread(thread);
+        assertTrue(mStack.navigateUpToLocked(secondActivity /* source record */,
+                firstActivity.intent /* destIntent */, 0 /* resultCode */, null /* resultData */));
+        // The firstActivity uses default launch mode, so the activities between it and itself will
+        // be finished.
+        assertTrue(secondActivity.finishing);
+        assertTrue(firstActivity.finishing);
+        // The calling uid of the new activity should be the current real caller.
+        assertEquals(secondActivity.getUid(), starter.getCallingUid());
+    }
+
     private void verifyShouldSleepActivities(boolean focusedStack,
             boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
         final ActivityDisplay display = mock(ActivityDisplay.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 84bdecb..f94f002 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -290,6 +290,7 @@
             aInfo.applicationInfo.packageName = mComponent.getPackageName();
             aInfo.applicationInfo.uid = mUid;
             aInfo.packageName = mComponent.getPackageName();
+            aInfo.name = mComponent.getClassName();
             if (mTargetActivity != null) {
                 aInfo.targetActivity = mTargetActivity;
             }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index e004cd3..f25110f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -23,6 +23,9 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.ComponentName;
 import android.graphics.Canvas;
@@ -32,9 +35,14 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
+
+import com.android.server.LocalServices;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 
 import java.io.File;
 
@@ -50,10 +58,26 @@
     TaskSnapshotLoader mLoader;
     int mTestUserId;
 
+    @BeforeClass
+    public static void setUpOnce() {
+        final UserManagerInternal userManager = mock(UserManagerInternal.class);
+        LocalServices.addService(UserManagerInternal.class, userManager);
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+    }
+
     @Before
     public void setUp() {
         final UserManager um = UserManager.get(getInstrumentation().getTargetContext());
         mTestUserId = um.getUserHandle();
+
+        final UserManagerInternal userManagerInternal =
+                LocalServices.getService(UserManagerInternal.class);
+        when(userManagerInternal.isUserUnlocked(mTestUserId)).thenReturn(true);
+
         mPersister = new TaskSnapshotPersister(mWm, userId -> FILES_DIR);
         mLoader = new TaskSnapshotLoader(mPersister);
         mPersister.start();
diff --git a/services/usb/OWNERS b/services/usb/OWNERS
index 7897a0c..8ee72b5 100644
--- a/services/usb/OWNERS
+++ b/services/usb/OWNERS
@@ -1,4 +1,6 @@
 badhri@google.com
 elaurent@google.com
 moltmann@google.com
-zhangjerry@google.com
+albertccwang@google.com
+jameswei@google.com
+howardyen@google.com
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index c5fcf67..ead90bb 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -2075,6 +2075,17 @@
 
     /**
      * Returns the child {@link Call} in a generic conference that is currently active.
+     *
+     * A "generic conference" is the mechanism used to support two simultaneous calls on a device
+     * in CDMA networks. It is effectively equivalent to having one call active and one call on hold
+     * in GSM or IMS calls. This method returns the currently active call.
+     *
+     * In a generic conference, the network exposes the conference to us as a single call, and we
+     * switch between talking to the two participants using a CDMA flash command. Since the network
+     * exposes no additional information about the call, the only way we know which caller we're
+     * currently talking to is by keeping track of the flash commands that we've sent to the
+     * network.
+     *
      * For calls that are not generic conferences, or when the generic conference has more than
      * 2 children, returns {@code null}.
      * @see Details#PROPERTY_GENERIC_CONFERENCE
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index 36c6377..c832f53 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -38,16 +38,14 @@
  *
  * <p>
  * Below is an example manifest registration for a {@code CallRedirectionService}.
- * <pre>
  * {@code
  * <service android:name="your.package.YourCallRedirectionServiceImplementation"
- *          android:permission="android.permission.BIND_REDIRECTION_SERVICE">
+ *          android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
  *      <intent-filter>
  *          <action android:name="android.telecom.CallRedirectionService"/>
  *      </intent-filter>
  * </service>
  * }
- * </pre>
  */
 public abstract class CallRedirectionService extends Service {
     /**
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index f019a9d..d960552 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -74,6 +74,7 @@
         public void onConnectionEvent(Conference c, String event, Bundle extras) {}
         public void onCallerDisplayNameChanged(
                 Conference c, String callerDisplayName, int presentation) {}
+        public void onCallDirectionChanged(Conference c, int callDirection) {}
         public void onRingbackRequested(Conference c, boolean ringback) {}
     }
 
@@ -103,7 +104,9 @@
     private int mAddressPresentation;
     private String mCallerDisplayName;
     private int mCallerDisplayNamePresentation;
+    private int mCallDirection;
     private boolean mRingbackRequested = false;
+    private boolean mIsMultiparty = true;
 
     private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
         @Override
@@ -996,8 +999,8 @@
     public void onExtrasChanged(Bundle extras) {}
 
     /**
-     * Set whether Telecom should treat this {@link Conference} as a conference call or if it
-     * should treat it as a single-party call.
+     * Set whether Telecom should treat this {@link Conference} as a multiparty conference call or
+     * if it should treat it as a single-party call.
      * This method is used as part of a workaround regarding IMS conference calls and user
      * expectation.  In IMS, once a conference is formed, the UE is connected to an IMS conference
      * server.  If all participants of the conference drop out of the conference except for one, the
@@ -1018,12 +1021,46 @@
     @TestApi
     @RequiresPermission(MODIFY_PHONE_STATE)
     public void setConferenceState(boolean isConference) {
+        mIsMultiparty = isConference;
         for (Listener l : mListeners) {
             l.onConferenceStateChanged(this, isConference);
         }
     }
 
     /**
+     * Sets the call direction of this {@link Conference}. By default, all {@link Conference}s have
+     * a direction of {@link android.telecom.Call.Details.CallDirection#DIRECTION_UNKNOWN}. The
+     * direction of a {@link Conference} is only applicable to the case where
+     * {@link #setConferenceState(boolean)} has been set to {@code false}, otherwise the direction
+     * will be ignored.
+     * @param callDirection The direction of the conference.
+     * @hide
+     */
+    @RequiresPermission(MODIFY_PHONE_STATE)
+    public final void setCallDirection(@Call.Details.CallDirection int callDirection) {
+        Log.d(this, "setDirection %d", callDirection);
+        mCallDirection = callDirection;
+        for (Listener l : mListeners) {
+            l.onCallDirectionChanged(this, callDirection);
+        }
+    }
+
+    /**
+     * Determines if the {@link Conference} is considered "multiparty" or not.  By default all
+     * conferences are considered multiparty.  A multiparty conference is one where there are
+     * multiple conference participants (other than the host) in the conference.
+     * This is tied to {@link #setConferenceState(boolean)}, which is used for some use cases to
+     * have a conference appear as if it is a standalone call, in which case the conference will
+     * no longer be multiparty.
+     * @return {@code true} if conference is treated as a conference (i.e. it is multiparty),
+     * {@code false} if it should emulate a standalone call (i.e. not multiparty).
+     * @hide
+     */
+    public boolean isMultiparty() {
+        return mIsMultiparty;
+    }
+
+    /**
      * Sets the address of this {@link Conference}.  Used when {@link #setConferenceState(boolean)}
      * is called to mark a conference temporarily as NOT a conference.
      * <p>
@@ -1071,16 +1108,16 @@
      * This is applicable in two cases:
      * <ol>
      *     <li>When {@link #setConferenceState(boolean)} is used to mark a conference as
-     *     temporarily "not a conference"; we need to present the correct address in the in-call
-     *     UI.</li>
+     *     temporarily "not a conference"; we need to present the correct address presentation in
+     *     the in-call UI.</li>
      *     <li>When the conference is not hosted on the current device, we need to know the address
-     *     information for the purpose of showing the original address to the user, as well as for
-     *     logging to the call log.</li>
+     *     presentation information for the purpose of showing the original address to the user, as
+     *     well as for logging to the call log.</li>
      * </ol>
-     * @return The address of the conference, or {@code null} if not applicable.
+     * @return The address presentation of the conference.
      * @hide
      */
-    public final int getAddressPresentation() {
+    public final @TelecomManager.Presentation int getAddressPresentation() {
         return mAddressPresentation;
     }
 
@@ -1102,6 +1139,15 @@
     }
 
     /**
+     * @return The call direction of this conference. Only applicable when
+     * {@link #setConferenceState(boolean)} is set to false.
+     * @hide
+     */
+    public final @Call.Details.CallDirection int getCallDirection() {
+        return mCallDirection;
+    }
+
+    /**
      * Sets the caller display name (CNAP) of this {@link Conference}.  Used when
      * {@link #setConferenceState(boolean)} is called to mark a conference temporarily as NOT a
      * conference.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 5f33a3d..9dfa3ac 100755
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2331,7 +2331,6 @@
      *        See {@link TelecomManager} for valid values.
      */
     public final void setAddress(Uri address, int presentation) {
-        checkImmutable();
         Log.d(this, "setAddress %s", address);
         mAddress = address;
         mAddressPresentation = presentation;
@@ -3358,6 +3357,7 @@
         private boolean mImmutable = false;
         public FailureSignalingConnection(DisconnectCause disconnectCause) {
             setDisconnected(disconnectCause);
+            mImmutable = true;
         }
 
         public void checkImmutable() {
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 0dca006..1b60e48 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1554,6 +1554,14 @@
         }
 
         @Override
+        public void onCallDirectionChanged(Conference c, int direction) {
+            String id = mIdByConference.get(c);
+            if (id != null) {
+                mAdapter.setCallDirection(id, direction);
+            }
+        }
+
+        @Override
         public void onAddressChanged(Conference c, Uri newAddress, int presentation) {
             String id = mIdByConference.get(c);
             if (id != null) {
@@ -1857,25 +1865,23 @@
         mConferenceById.put(callId, conference);
         mIdByConference.put(conference, callId);
         conference.addListener(mConferenceListener);
-        ParcelableConference parcelableConference = new ParcelableConference(
-                request.getAccountHandle(),
-                conference.getState(),
-                conference.getConnectionCapabilities(),
-                conference.getConnectionProperties(),
-                Collections.<String>emptyList(), //connectionIds
-                conference.getVideoProvider() == null ?
-                        null : conference.getVideoProvider().getInterface(),
-                conference.getVideoState(),
-                conference.getConnectTimeMillis(),
-                conference.getConnectionStartElapsedRealtimeMillis(),
-                conference.getStatusHints(),
-                conference.getExtras(),
-                conference.getAddress(),
-                conference.getAddressPresentation(),
-                conference.getCallerDisplayName(),
-                conference.getCallerDisplayNamePresentation(),
-                conference.getDisconnectCause(),
-                conference.isRingbackRequested());
+        ParcelableConference parcelableConference = new ParcelableConference.Builder(
+                request.getAccountHandle(), conference.getState())
+                .setConnectionCapabilities(conference.getConnectionCapabilities())
+                .setConnectionProperties(conference.getConnectionProperties())
+                .setVideoAttributes(conference.getVideoProvider() == null
+                                ? null : conference.getVideoProvider().getInterface(),
+                        conference.getVideoState())
+                .setConnectTimeMillis(conference.getConnectTimeMillis(),
+                        conference.getConnectionStartElapsedRealtimeMillis())
+                .setStatusHints(conference.getStatusHints())
+                .setExtras(conference.getExtras())
+                .setAddress(conference.getAddress(), conference.getAddressPresentation())
+                .setCallerDisplayName(conference.getCallerDisplayName(),
+                        conference.getCallerDisplayNamePresentation())
+                .setDisconnectCause(conference.getDisconnectCause())
+                .setRingbackRequested(conference.isRingbackRequested())
+                .build();
         if (conference.getState() != Connection.STATE_DISCONNECTED) {
             conference.setTelecomCallId(callId);
             mAdapter.setVideoProvider(callId, conference.getVideoProvider());
@@ -2476,27 +2482,34 @@
                 }
             }
             conference.setTelecomCallId(id);
-            ParcelableConference parcelableConference = new ParcelableConference(
-                    conference.getPhoneAccountHandle(),
-                    conference.getState(),
-                    conference.getConnectionCapabilities(),
-                    conference.getConnectionProperties(),
-                    connectionIds,
-                    conference.getVideoProvider() == null ?
-                            null : conference.getVideoProvider().getInterface(),
-                    conference.getVideoState(),
-                    conference.getConnectTimeMillis(),
-                    conference.getConnectionStartElapsedRealtimeMillis(),
-                    conference.getStatusHints(),
-                    conference.getExtras(),
-                    conference.getAddress(),
-                    conference.getAddressPresentation(),
-                    conference.getCallerDisplayName(),
-                    conference.getCallerDisplayNamePresentation());
+            ParcelableConference parcelableConference = new ParcelableConference.Builder(
+                    conference.getPhoneAccountHandle(), conference.getState())
+                    .setConnectionCapabilities(conference.getConnectionCapabilities())
+                    .setConnectionProperties(conference.getConnectionProperties())
+                    .setConnectionIds(connectionIds)
+                    .setVideoAttributes(conference.getVideoProvider() == null
+                                    ? null : conference.getVideoProvider().getInterface(),
+                            conference.getVideoState())
+                    .setConnectTimeMillis(conference.getConnectTimeMillis(),
+                            conference.getConnectionStartElapsedRealtimeMillis())
+                    .setStatusHints(conference.getStatusHints())
+                    .setExtras(conference.getExtras())
+                    .setAddress(conference.getAddress(), conference.getAddressPresentation())
+                    .setCallerDisplayName(conference.getCallerDisplayName(),
+                            conference.getCallerDisplayNamePresentation())
+                    .setDisconnectCause(conference.getDisconnectCause())
+                    .setRingbackRequested(conference.isRingbackRequested())
+                    .setCallDirection(conference.getCallDirection())
+                    .build();
 
             mAdapter.addConferenceCall(id, parcelableConference);
             mAdapter.setVideoProvider(id, conference.getVideoProvider());
             mAdapter.setVideoState(id, conference.getVideoState());
+            // In some instances a conference can start its life as a standalone call with just a
+            // single participant; ensure we signal to Telecom in this case.
+            if (!conference.isMultiparty()) {
+                mAdapter.setConferenceState(id, conference.isMultiparty());
+            }
 
             // Go through any child calls and set the parent.
             for (Connection connection : conference.getConnections()) {
@@ -2633,6 +2646,7 @@
      * @param request Details about the incoming call.
      * @return The {@code Connection} object to satisfy this call, or {@code null} to
      *         not handle the call.
+     * @hide
      */
     public @Nullable Conference onCreateIncomingConference(
             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
@@ -2717,6 +2731,7 @@
      * @param connectionManagerPhoneAccount See description at
      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
      * @param request The incoming connection request.
+     * @hide
      */
     public void onCreateIncomingConferenceFailed(
             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
@@ -2737,6 +2752,7 @@
      * @param connectionManagerPhoneAccount See description at
      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
      * @param request The outgoing connection request.
+     * @hide
      */
     public void onCreateOutgoingConferenceFailed(
             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
@@ -2805,6 +2821,7 @@
      * @param request Details about the outgoing call.
      * @return The {@code Conference} object to satisfy this call, or the result of an invocation
      *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+     * @hide
      */
     public @Nullable Conference onCreateOutgoingConference(
             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 8f27323..f8a6cf0 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -693,4 +693,20 @@
             }
         }
     }
+
+    /**
+     * Sets the direction of a call. Setting a new direction of an existing call is usually only
+     * applicable during single caller emulation during conferencing, see
+     * {@link Conference#setConferenceState(boolean)} for more information.
+     * @param callId The identifier of the call.
+     * @param direction The new direction of the call.
+     */
+    void setCallDirection(String callId, @Call.Details.CallDirection int direction) {
+        for (IConnectionServiceAdapter a : mAdapters) {
+            try {
+                a.setCallDirection(callId, direction, Log.getExternalSession());
+            } catch (RemoteException e) {
+            }
+        }
+    }
 }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 79ad51b..6c1ea32 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -76,6 +76,7 @@
     private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
     private static final int MSG_SET_CONFERENCE_STATE = 36;
     private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37;
+    private static final int MSG_SET_CALL_DIRECTION = 38;
 
     private final IConnectionServiceAdapter mDelegate;
 
@@ -353,7 +354,7 @@
                 case MSG_CONNECTION_SERVICE_FOCUS_RELEASED:
                     mDelegate.onConnectionServiceFocusReleased(null /*Session.Info*/);
                     break;
-                case MSG_SET_CONFERENCE_STATE:
+                case MSG_SET_CONFERENCE_STATE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         mDelegate.setConferenceState((String) args.arg1, (Boolean) args.arg2,
@@ -361,6 +362,17 @@
                     } finally {
                         args.recycle();
                     }
+                    break;
+                }
+                case MSG_SET_CALL_DIRECTION: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        mDelegate.setCallDirection((String) args.arg1, args.argi1,
+                                (Session.Info) args.arg2);
+                    } finally {
+                        args.recycle();
+                    }
+                }
             }
         }
     };
@@ -670,6 +682,16 @@
             args.arg3 = sessionInfo;
             mHandler.obtainMessage(MSG_SET_CONFERENCE_STATE, args).sendToTarget();
         }
+
+        @Override
+        public void setCallDirection(String callId, int direction,
+                Session.Info sessionInfo) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = callId;
+            args.argi1 = direction;
+            args.arg2 = sessionInfo;
+            mHandler.obtainMessage(MSG_SET_CALL_DIRECTION, args).sendToTarget();
+        }
     };
 
     public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/GatewayInfo.java b/telecomm/java/android/telecom/GatewayInfo.java
index 0faa4fd..31c24d5 100644
--- a/telecomm/java/android/telecom/GatewayInfo.java
+++ b/telecomm/java/android/telecom/GatewayInfo.java
@@ -111,7 +111,7 @@
     @Override
     public void writeToParcel(Parcel destination, int flags) {
         destination.writeString(mGatewayProviderPackageName);
-        mGatewayAddress.writeToParcel(destination, 0);
-        mOriginalAddress.writeToParcel(destination, 0);
+        Uri.writeToParcel(destination, mGatewayAddress);
+        Uri.writeToParcel(destination, mOriginalAddress);
     }
 }
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 982e5f3..72ab609 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -70,6 +70,14 @@
  * app.
  * <p>
  * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call.
+ * <h2>Becoming the Default Phone App</h2>
+ * The default dialer/phone app is one which provides the in-call user interface while the device is
+ * in a call.  A device is bundled with a system provided default dialer/phone app.  The user may
+ * choose a single app to take over this role from the system app.  An app which wishes to fulfill
+ * one this role uses the {@code android.app.role.RoleManager} to request that they fill the role.
+ * <p>
+ * An app filling the role of the default phone app provides a user interface while the device is in
+ * a call, and the device is not in car mode.
  * <p>
  * Below is an example manifest registration for an {@code InCallService}. The meta-data
  * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 415a817..182dc8b 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -629,7 +629,7 @@
             int capabilities = source.readInt();
             int properties = source.readInt();
             long connectTimeMillis = source.readLong();
-            Uri handle = source.readParcelable(classLoader);
+            Uri handle = Uri.CREATOR.createFromParcel(source);
             int handlePresentation = source.readInt();
             String callerDisplayName = source.readString();
             int callerDisplayNamePresentation = source.readInt();
@@ -711,7 +711,7 @@
         destination.writeInt(mCapabilities);
         destination.writeInt(mProperties);
         destination.writeLong(mConnectTimeMillis);
-        destination.writeParcelable(mHandle, 0);
+        Uri.writeToParcel(destination, mHandle);
         destination.writeInt(mHandlePresentation);
         destination.writeString(mCallerDisplayName);
         destination.writeInt(mCallerDisplayNamePresentation);
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index 2b9213b..b8ad9e2 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -258,27 +258,6 @@
     public static final int SIP_PHONE = 0x8;
     public static final int THIRD_PARTY_PHONE = 0x10;
 
-    /**
-     * Indicating the call source is not specified.
-     *
-     * @hide
-     */
-    public static final int CALL_SOURCE_UNSPECIFIED = 0;
-
-    /**
-     * Indicating the call is initiated via emergency dialer's dialpad.
-     *
-     * @hide
-     */
-    public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1;
-
-    /**
-     * Indicating the call is initiated via emergency dialer's shortcut button.
-     *
-     * @hide
-     */
-    public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2;
-
     public static final long MILLIS_IN_5_MINUTES = 1000 * 60 * 5;
     public static final long MILLIS_IN_1_SECOND = 1000;
 
@@ -343,7 +322,7 @@
     private List<VideoEvent> videoEvents;
 
     // The source where user initiated this call. ONE OF the CALL_SOURCE_* constants.
-    private int callSource = CALL_SOURCE_UNSPECIFIED;
+    private int callSource = TelecomManager.CALL_SOURCE_UNSPECIFIED;
 
     public ParcelableCallAnalytics(long startTimeMillis, long callDurationMillis, int callType,
             boolean isAdditionalCall, boolean isInterrupted, int callTechnologies,
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index 90b69a3..1f8aafb 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -22,6 +22,7 @@
 import android.os.Parcelable;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import com.android.internal.telecom.IVideoProvider;
@@ -32,25 +33,130 @@
  */
 public final class ParcelableConference implements Parcelable {
 
-    private PhoneAccountHandle mPhoneAccount;
-    private int mState;
-    private int mConnectionCapabilities;
-    private int mConnectionProperties;
-    private List<String> mConnectionIds;
-    private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+    public static final class Builder {
+        private final PhoneAccountHandle mPhoneAccount;
+        private final int mState;
+        private int mConnectionCapabilities;
+        private int mConnectionProperties;
+        private List<String> mConnectionIds = Collections.emptyList();
+        private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+        private IVideoProvider mVideoProvider;
+        private int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
+        private StatusHints mStatusHints;
+        private Bundle mExtras;
+        private long mConnectElapsedTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+        private Uri mAddress;
+        private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN;
+        private String mCallerDisplayName;
+        private int mCallerDisplayNamePresentation = TelecomManager.PRESENTATION_UNKNOWN;;
+        private DisconnectCause mDisconnectCause;
+        private boolean mRingbackRequested;
+        private int mCallDirection = Call.Details.DIRECTION_UNKNOWN;
+
+        public Builder(
+                PhoneAccountHandle phoneAccount,
+                int state) {
+            mPhoneAccount = phoneAccount;
+            mState = state;
+        }
+
+        public Builder setDisconnectCause(DisconnectCause cause) {
+            mDisconnectCause = cause;
+            return this;
+        }
+
+        public Builder setRingbackRequested(boolean requested) {
+            mRingbackRequested = requested;
+            return this;
+        }
+
+        public Builder setCallerDisplayName(String callerDisplayName,
+                @TelecomManager.Presentation int callerDisplayNamePresentation) {
+            mCallerDisplayName = callerDisplayName;
+            mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+            return this;
+        }
+
+        public Builder setAddress(Uri address,
+                @TelecomManager.Presentation int addressPresentation) {
+            mAddress = address;
+            mAddressPresentation = addressPresentation;
+            return this;
+        }
+
+        public Builder setExtras(Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        public Builder setStatusHints(StatusHints hints) {
+            mStatusHints = hints;
+            return this;
+        }
+
+        public Builder setConnectTimeMillis(long connectTimeMillis, long connectElapsedTimeMillis) {
+            mConnectTimeMillis = connectTimeMillis;
+            mConnectElapsedTimeMillis = connectElapsedTimeMillis;
+            return this;
+        }
+
+        public Builder setVideoAttributes(IVideoProvider provider,
+                @VideoProfile.VideoState int videoState) {
+            mVideoProvider = provider;
+            mVideoState = videoState;
+            return this;
+        }
+
+        public Builder setConnectionIds(List<String> connectionIds) {
+            mConnectionIds = connectionIds;
+            return this;
+        }
+
+        public Builder setConnectionProperties(int properties) {
+            mConnectionProperties = properties;
+            return this;
+        }
+
+        public Builder setConnectionCapabilities(int capabilities) {
+            mConnectionCapabilities = capabilities;
+            return this;
+        }
+
+        public Builder setCallDirection(int callDirection) {
+            mCallDirection = callDirection;
+            return this;
+        }
+
+        public ParcelableConference build() {
+            return new ParcelableConference(mPhoneAccount, mState, mConnectionCapabilities,
+                    mConnectionProperties, mConnectionIds, mVideoProvider, mVideoState,
+                    mConnectTimeMillis, mConnectElapsedTimeMillis, mStatusHints, mExtras, mAddress,
+                    mAddressPresentation, mCallerDisplayName, mCallerDisplayNamePresentation,
+                    mDisconnectCause, mRingbackRequested, mCallDirection);
+        }
+    }
+
+
+    private final PhoneAccountHandle mPhoneAccount;
+    private final int mState;
+    private final int mConnectionCapabilities;
+    private final int mConnectionProperties;
+    private final List<String> mConnectionIds;
+    private final long mConnectTimeMillis;
     private final IVideoProvider mVideoProvider;
     private final int mVideoState;
-    private StatusHints mStatusHints;
-    private Bundle mExtras;
-    private long mConnectElapsedTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+    private final StatusHints mStatusHints;
+    private final Bundle mExtras;
+    private final long mConnectElapsedTimeMillis;
     private final Uri mAddress;
     private final int mAddressPresentation;
     private final String mCallerDisplayName;
     private final int mCallerDisplayNamePresentation;
-    private DisconnectCause mDisconnectCause;
-    private boolean mRingbackRequested;
+    private final DisconnectCause mDisconnectCause;
+    private final boolean mRingbackRequested;
+    private final int mCallDirection;
 
-    public ParcelableConference(
+    private ParcelableConference(
             PhoneAccountHandle phoneAccount,
             int state,
             int connectionCapabilities,
@@ -67,31 +173,8 @@
             String callerDisplayName,
             int callerDisplayNamePresentation,
             DisconnectCause disconnectCause,
-            boolean ringbackRequested) {
-        this(phoneAccount, state, connectionCapabilities, connectionProperties, connectionIds,
-                videoProvider, videoState, connectTimeMillis, connectElapsedTimeMillis,
-                statusHints, extras, address, addressPresentation, callerDisplayName,
-                callerDisplayNamePresentation);
-        mDisconnectCause = disconnectCause;
-        mRingbackRequested = ringbackRequested;
-    }
-
-    public ParcelableConference(
-            PhoneAccountHandle phoneAccount,
-            int state,
-            int connectionCapabilities,
-            int connectionProperties,
-            List<String> connectionIds,
-            IVideoProvider videoProvider,
-            int videoState,
-            long connectTimeMillis,
-            long connectElapsedTimeMillis,
-            StatusHints statusHints,
-            Bundle extras,
-            Uri address,
-            int addressPresentation,
-            String callerDisplayName,
-            int callerDisplayNamePresentation) {
+            boolean ringbackRequested,
+            int callDirection) {
         mPhoneAccount = phoneAccount;
         mState = state;
         mConnectionCapabilities = connectionCapabilities;
@@ -107,8 +190,9 @@
         mAddressPresentation = addressPresentation;
         mCallerDisplayName = callerDisplayName;
         mCallerDisplayNamePresentation = callerDisplayNamePresentation;
-        mDisconnectCause = null;
-        mRingbackRequested = false;
+        mDisconnectCause = disconnectCause;
+        mRingbackRequested = ringbackRequested;
+        mCallDirection = callDirection;
     }
 
     @Override
@@ -134,6 +218,8 @@
                 .append(mRingbackRequested)
                 .append(", disconnectCause: ")
                 .append(mDisconnectCause)
+                .append(", callDirection: ")
+                .append(mCallDirection)
                 .toString();
     }
 
@@ -192,10 +278,15 @@
     public boolean isRingbackRequested() {
         return mRingbackRequested;
     }
+
     public int getHandlePresentation() {
         return mAddressPresentation;
     }
 
+    public int getCallDirection() {
+        return mCallDirection;
+    }
+
     public static final @android.annotation.NonNull Parcelable.Creator<ParcelableConference> CREATOR =
             new Parcelable.Creator<ParcelableConference> () {
         @Override
@@ -220,12 +311,13 @@
             int callerDisplayNamePresentation = source.readInt();
             DisconnectCause disconnectCause = source.readParcelable(classLoader);
             boolean isRingbackRequested = source.readInt() == 1;
+            int callDirection = source.readInt();
 
             return new ParcelableConference(phoneAccount, state, capabilities, properties,
                     connectionIds, videoCallProvider, videoState, connectTimeMillis,
                     connectElapsedTimeMillis, statusHints, extras, address, addressPresentation,
                     callerDisplayName, callerDisplayNamePresentation, disconnectCause,
-                    isRingbackRequested);
+                    isRingbackRequested, callDirection);
         }
 
         @Override
@@ -261,5 +353,6 @@
         destination.writeInt(mCallerDisplayNamePresentation);
         destination.writeParcelable(mDisconnectCause, 0);
         destination.writeInt(mRingbackRequested ? 1 : 0);
+        destination.writeInt(mCallDirection);
     }
 }
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 6ae4a08..768c8ee 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -82,8 +82,10 @@
     public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING =
             "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
 
-     /**
-     * Indicating flag for phone account whether to use voip audio mode for voip calls
+    /**
+     * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+     * indicates that all calls from this {@link PhoneAccount} should be treated as VoIP calls
+     * rather than cellular calls.
      * @hide
      */
     public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE =
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 76640e0..cad5b70 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -485,6 +485,11 @@
                 Session.Info sessionInfo) {
             // Do nothing
         }
+
+        @Override
+        public void setCallDirection(String callId, int direction, Session.Info sessionInfo) {
+            // Do nothing
+        }
     };
 
     private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 19a1021e..ce71511 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -192,13 +192,13 @@
 
     /**
      * Broadcast intent action indicating that the current default call screening app has changed.
-     *
-     * The string extra {@link #EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME} will contain the
-     * name of the Component of the previous or the new call screening app.
-     *
-     * The boolean extra {@link #EXTRA_IS_DEFAULT_CALL_SCREENING_APP} will indicate the component
-     * name in the String extra {@link #EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME} is default
-     * call screening app or not.
+     * <p>
+     * Note: This intent is NEVER actually broadcast and will be deprecated in the future.
+     * <p>
+     * An app that want to know if it holds the
+     * {@link android.app.role.RoleManager#ROLE_CALL_SCREENING} role can use
+     * {@link android.app.role.RoleManager#isRoleHeld(String)} to confirm if it holds the role or
+     * not.
      */
     public static final String ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED =
         "android.telecom.action.DEFAULT_CALL_SCREENING_APP_CHANGED";
@@ -206,6 +206,8 @@
     /**
      * Extra value used with {@link #ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED} broadcast to
      * indicate the ComponentName of the call screening app which has changed.
+     * <p>
+     * Note: This extra is NOT used and will be deprecated in the future.
      */
     public static final String EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME =
             "android.telecom.extra.DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME";
@@ -213,6 +215,8 @@
     /**
      * Extra value used with {@link #ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED} broadcast to
      * indicate whether an app is the default call screening app.
+     * <p>
+     * Note: This extra is NOT used and will be deprecated in the future.
      */
     public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP =
             "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
@@ -312,6 +316,9 @@
             "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
 
     /**
+     * A mandatory extra containing a {@link Uri} to be passed in when calling
+     * {@link #addNewUnknownCall(PhoneAccountHandle, Bundle)}. The {@link Uri} value indicates
+     * the remote handle of the new call.
      * @hide
      */
     public static final String EXTRA_UNKNOWN_CALL_HANDLE =
@@ -373,8 +380,15 @@
             "android.telecom.extra.CONNECTION_SERVICE";
 
     /**
-     * Optional extra for communicating the call technology used by a
-     * {@link com.android.internal.telephony.Connection} to Telecom
+     * Optional extra for communicating the call technology used by a {@link ConnectionService}
+     * to Telecom. Valid values are:
+     * <ul>
+     *     <li>{@link TelephonyManager#PHONE_TYPE_CDMA}</li>
+     *     <li>{@link TelephonyManager#PHONE_TYPE_GSM}</li>
+     *     <li>{@link TelephonyManager#PHONE_TYPE_IMS}</li>
+     *     <li>{@link TelephonyManager#PHONE_TYPE_THIRD_PARTY}</li>
+     *     <li>{@link TelephonyManager#PHONE_TYPE_SIP}</li>
+     * </ul>
      * @hide
      */
     public static final String EXTRA_CALL_TECHNOLOGY_TYPE =
@@ -384,7 +398,7 @@
      * Optional extra for communicating the call network technology used by a
      * {@link android.telecom.Connection} to Telecom and InCallUI.
      *
-     * @see {@code NETWORK_TYPE_*} in {@link android.telephony.TelephonyManager}.
+     * {@code NETWORK_TYPE_*} in {@link android.telephony.TelephonyManager}.
      */
     public static final String EXTRA_CALL_NETWORK_TYPE =
             "android.telecom.extra.CALL_NETWORK_TYPE";
@@ -725,15 +739,16 @@
     /**
      * The lookup key for an int that indicates the current TTY mode.
      * Valid modes are:
-     * - {@link #TTY_MODE_OFF}
-     * - {@link #TTY_MODE_FULL}
-     * - {@link #TTY_MODE_HCO}
-     * - {@link #TTY_MODE_VCO}
-     *
+     * <ul>
+     *     <li>{@link #TTY_MODE_OFF}</li>
+     *     <li>{@link #TTY_MODE_FULL}</li>
+     *     <li>{@link #TTY_MODE_HCO}</li>
+     *     <li>{@link #TTY_MODE_VCO}</li>
+     * </ul>
      * @hide
      */
     public static final String EXTRA_CURRENT_TTY_MODE =
-            "android.telecom.intent.extra.CURRENT_TTY_MODE";
+            "android.telecom.extra.CURRENT_TTY_MODE";
 
     /**
      * Broadcast intent action indicating that the TTY preferred operating mode has changed. An
@@ -753,7 +768,7 @@
      * @hide
      */
     public static final String EXTRA_TTY_PREFERRED_MODE =
-            "android.telecom.intent.extra.TTY_PREFERRED";
+            "android.telecom.extra.TTY_PREFERRED_MODE";
 
     /**
      * Broadcast intent action for letting custom component know to show the missed call
@@ -822,16 +837,37 @@
     /**
      * Optional extra for {@link #placeCall(Uri, Bundle)} containing an integer that specifies
      * the source where user initiated this call. This data is used in metrics.
-     * Valid source are:
-     * {@link ParcelableCallAnalytics#CALL_SOURCE_UNSPECIFIED},
-     * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_DIALPAD},
-     * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_SHORTCUT}.
+     * Valid sources are:
+     * {@link TelecomManager#CALL_SOURCE_UNSPECIFIED},
+     * {@link TelecomManager#CALL_SOURCE_EMERGENCY_DIALPAD},
+     * {@link TelecomManager#CALL_SOURCE_EMERGENCY_SHORTCUT}.
      *
      * @hide
      */
     public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
 
     /**
+     * Indicating the call is initiated via emergency dialer's shortcut button.
+     *
+     * @hide
+     */
+    public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2;
+
+    /**
+     * Indicating the call is initiated via emergency dialer's dialpad.
+     *
+     * @hide
+     */
+    public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1;
+
+    /**
+     * Indicating the call source is not specified.
+     *
+     * @hide
+     */
+    public static final int CALL_SOURCE_UNSPECIFIED = 0;
+
+    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 4f63e08..3fd7f949 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -132,4 +132,6 @@
     void resetConnectionTime(String callIdi, in Session.Info sessionInfo);
 
     void setConferenceState(String callId, boolean isConference, in Session.Info sessionInfo);
+
+    void setCallDirection(String callId, int direction, in Session.Info sessionInfo);
 }
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 58a7ea0..628c480 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -1,18 +1,16 @@
 set noparent
 
-tgunn@google.com
-breadley@google.com
-hallliu@google.com
-rgreenwalt@google.com
-mpq@google.com
 amitmahajan@google.com
+breadley@google.com
 fionaxu@google.com
 jackyu@google.com
+hallliu@google.com
+rgreenwalt@google.com
+tgunn@google.com
 jminjie@google.com
-satk@google.com
 shuoq@google.com
 refuhoo@google.com
-paulye@google.com
 nazaninb@google.com
 sarahchin@google.com
 dbright@google.com
+xiaotonj@google.com
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index de0221c..3c1e707 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -31,6 +31,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.util.ArrayUtils;
 import com.android.server.SystemConfig;
@@ -156,7 +157,7 @@
             for (ApplicationInfo ai : candidates) {
                 String packageName = ai.packageName;
                 String[] restrictedCarrierApps = Resources.getSystem().getStringArray(
-                        android.R.array.config_restrictedPreinstalledCarrierApps);
+                        R.array.config_restrictedPreinstalledCarrierApps);
                 boolean hasPrivileges = telephonyManager != null
                         && telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
                                 == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
diff --git a/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java
new file mode 100644
index 0000000..6c63755
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java
@@ -0,0 +1,65 @@
+/*
+ * 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.telephony;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Telephony;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * This class provides utility functions related to CellBroadcast.
+ */
+public class CellBroadcastUtils {
+    private static final String TAG = "CellBroadcastUtils";
+    private static final boolean VDBG = false;
+
+    /**
+     * Utility method to query the default CBR's package name.
+     */
+    public static String getDefaultCellBroadcastReceiverPackageName(Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        ResolveInfo resolveInfo = packageManager.resolveActivity(
+                new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION),
+                PackageManager.MATCH_SYSTEM_ONLY);
+        String packageName;
+
+        if (resolveInfo == null) {
+            Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: no package found");
+            return null;
+        }
+
+        packageName = resolveInfo.activityInfo.applicationInfo.packageName;
+
+        if (VDBG) {
+            Log.d(TAG, "getDefaultCellBroadcastReceiverPackageName: found package: " + packageName);
+        }
+
+        if (TextUtils.isEmpty(packageName) || packageManager.checkPermission(
+                android.Manifest.permission.READ_CELL_BROADCASTS, packageName)
+                == PackageManager.PERMISSION_DENIED) {
+            Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: returning null; "
+                    + "permission check failed for : " + packageName);
+            return null;
+        }
+
+        return packageName;
+    }
+}
diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
index 2201452..60cd400 100644
--- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
@@ -19,10 +19,12 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.os.Build;
-import android.text.TextUtils;
 import android.util.Log;
+import android.text.TextUtils;
 import android.util.SparseIntArray;
 
+import com.android.internal.R;
+
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
@@ -1085,10 +1087,8 @@
     private static void enableCountrySpecificEncodings() {
         Resources r = Resources.getSystem();
         // See comments in frameworks/base/core/res/res/values/config.xml for allowed values
-        sEnabledSingleShiftTables = r.getIntArray(
-                android.R.array.config_sms_enabled_single_shift_tables);
-        sEnabledLockingShiftTables = r.getIntArray(
-                android.R.array.config_sms_enabled_locking_shift_tables);
+        sEnabledSingleShiftTables = r.getIntArray(R.array.config_sms_enabled_single_shift_tables);
+        sEnabledLockingShiftTables = r.getIntArray(R.array.config_sms_enabled_locking_shift_tables);
 
         if (sEnabledSingleShiftTables.length > 0) {
             sHighestEnabledSingleShiftCode =
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 98a649f..1a049e6 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -1051,8 +1051,7 @@
     }
 
     /**
-     * Check if a package is default sms app (or equivalent, like bluetooth), and verify that
-     * packageName belongs to the caller.
+     * Check if a package is default sms app (or equivalent, like bluetooth)
      *
      * @param context context from the calling app
      * @param packageName the name of the package to be checked
@@ -1061,22 +1060,8 @@
     @UnsupportedAppUsage
     public static boolean isDefaultSmsApplication(Context context, String packageName) {
         if (packageName == null) {
-            Log.e(LOG_TAG, "isDefaultSmsApplication: packageName is null");
             return false;
         }
-        try {
-            if (Binder.getCallingUid()
-                    == context.getPackageManager().getPackageUid(packageName, 0)) {
-                Log.e(LOG_TAG, "isDefaultSmsApplication: " + packageName + " calling uid "
-                        + context.getPackageManager().getPackageUid(packageName, 0)
-                        + " does not match calling uid " + Binder.getCallingUid());
-                return false;
-            }
-        } catch (NameNotFoundException ex) {
-            Log.e(LOG_TAG, "isDefaultSmsApplication: packageName " + packageName + " not found");
-            return false;
-        }
-
         final String defaultSmsPackage = getDefaultSmsApplicationPackageName(context);
         if ((defaultSmsPackage != null && defaultSmsPackage.equals(packageName))
                 || BLUETOOTH_PACKAGE_NAME.equals(packageName)) {
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 2077800..fff6696 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -445,8 +445,9 @@
     /**
      * Returns whether the caller can read phone numbers.
      *
-     * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}, the
-     * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
+     * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}
+     * (only prior to R), the default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS
+     * can also read phone numbers.
      */
     public static boolean checkCallingOrSelfReadPhoneNumber(
             Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
@@ -459,8 +460,9 @@
     /**
      * Returns whether the caller can read phone numbers.
      *
-     * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}, the
-     * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
+     * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}
+     * (only prior to R), the default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS
+     * can also read phone numbers.
      */
     @VisibleForTesting
     public static boolean checkReadPhoneNumber(
@@ -476,13 +478,40 @@
         // NOTE(b/73308711): If an app has one of the following AppOps bits explicitly revoked, they
         // will be denied access, even if they have another permission and AppOps bit if needed.
 
-        // First, check if we can read the phone state.
+        // First, check if the SDK version is below R
+        boolean preR = false;
         try {
-            return checkReadPhoneState(
-                    context, subId, pid, uid, callingPackage, callingFeatureId,
-                    message);
-        } catch (SecurityException readPhoneStateSecurityException) {
+            ApplicationInfo info = context.getPackageManager().getApplicationInfoAsUser(
+                    callingPackage, 0, UserHandle.getUserHandleForUid(Binder.getCallingUid()));
+            preR = info.targetSdkVersion <= Build.VERSION_CODES.Q;
+        } catch (PackageManager.NameNotFoundException nameNotFoundException) {
         }
+        if (preR) {
+            // SDK < R allows READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or carrier privilege
+            try {
+                return checkReadPhoneState(
+                        context, subId, pid, uid, callingPackage, callingFeatureId, message);
+            } catch (SecurityException readPhoneStateException) {
+            }
+        } else {
+            // SDK >= R allows READ_PRIVILEGED_PHONE_STATE or carrier privilege
+            try {
+                context.enforcePermission(
+                        android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
+                // Skip checking for runtime permission since caller has privileged permission
+                return true;
+            } catch (SecurityException readPrivilegedPhoneStateException) {
+                if (SubscriptionManager.isValidSubscriptionId(subId)) {
+                    try {
+                        enforceCarrierPrivilege(context, subId, uid, message);
+                        // Skip checking for runtime permission since caller has carrier privilege
+                        return true;
+                    } catch (SecurityException carrierPrivilegeException) {
+                    }
+                }
+            }
+        }
+
         // Can be read with READ_SMS too.
         try {
             context.enforcePermission(android.Manifest.permission.READ_SMS, pid, uid, message);
diff --git a/telephony/common/com/google/android/mms/pdu/PduComposer.java b/telephony/common/com/google/android/mms/pdu/PduComposer.java
index b8b212c..5e1f556 100644
--- a/telephony/common/com/google/android/mms/pdu/PduComposer.java
+++ b/telephony/common/com/google/android/mms/pdu/PduComposer.java
@@ -745,7 +745,9 @@
             return PDU_COMPOSE_CONTENT_ERROR;
         }
 
-        // X-Mms-Report-Allowed Optional (not support)
+        // X-Mms-Report-Allowed Optional
+        appendHeader(PduHeaders.REPORT_ALLOWED);
+
         return PDU_COMPOSE_SUCCESS;
     }
 
diff --git a/telephony/common/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
index 4871434..31fe4d7 100644
--- a/telephony/common/com/google/android/mms/util/SqliteWrapper.java
+++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
@@ -60,7 +60,8 @@
     @UnsupportedAppUsage
     public static void checkSQLiteException(Context context, SQLiteException e) {
         if (isLowMemory(e)) {
-            Toast.makeText(context, android.R.string.low_memory, Toast.LENGTH_SHORT).show();
+            Toast.makeText(context, com.android.internal.R.string.low_memory,
+                    Toast.LENGTH_SHORT).show();
         } else {
             throw e;
         }
diff --git a/telephony/java/android/service/carrier/CarrierService.java b/telephony/java/android/service/carrier/CarrierService.java
index eefc1b7..d06ec11 100644
--- a/telephony/java/android/service/carrier/CarrierService.java
+++ b/telephony/java/android/service/carrier/CarrierService.java
@@ -25,6 +25,9 @@
 import android.telephony.TelephonyRegistryManager;
 import android.util.Log;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * A service that exposes carrier-specific functionality to the system.
  * <p>
@@ -156,5 +159,10 @@
                 result.send(RESULT_ERROR, null);
             }
         }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            CarrierService.this.dump(fd, pw, args);
+        }
     }
 }
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index ef11f46..ae2652e 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -31,7 +31,9 @@
 import android.telephony.TelephonyManager;
 import android.telephony.euicc.DownloadableSubscription;
 import android.telephony.euicc.EuiccInfo;
+import android.telephony.euicc.EuiccManager;
 import android.telephony.euicc.EuiccManager.OtaStatus;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.PrintWriter;
@@ -311,6 +313,64 @@
         mStubWrapper = new IEuiccServiceWrapper();
     }
 
+    /**
+     * Given a SubjectCode[5.2.6.1] and ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2), encode it to
+     * the format described in
+     * {@link android.telephony.euicc.EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE}
+     *
+     * @param subjectCode SubjectCode[5.2.6.1] from GSMA (SGP.22 v2.2)
+     * @param reasonCode  ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2)
+     * @return encoded error code described in
+     * {@link android.telephony.euicc.EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE}
+     * @throws NumberFormatException         when the Subject/Reason code contains non digits
+     * @throws IllegalArgumentException      when Subject/Reason code is null/empty
+     * @throws UnsupportedOperationException when sections has more than four layers (e.g 5.8.1.2)
+     *                                       or when an number is bigger than 15
+     */
+    public int encodeSmdxSubjectAndReasonCode(@NonNull String subjectCode,
+            @NonNull String reasonCode) {
+        final int maxSupportedSection = 3;
+        final int maxSupportedDigit = 15;
+        final int bitsPerSection = 4;
+
+        if (TextUtils.isEmpty(subjectCode) || TextUtils.isEmpty(reasonCode)) {
+            throw new IllegalArgumentException("SubjectCode/ReasonCode is empty");
+        }
+
+        final String[] subjectCodeToken = subjectCode.split("\\.");
+        final String[] reasonCodeToken = reasonCode.split("\\.");
+
+        if (subjectCodeToken.length > maxSupportedSection
+                || reasonCodeToken.length > maxSupportedSection) {
+            throw new UnsupportedOperationException("Only three nested layer is supported.");
+        }
+
+        int result = EuiccManager.OPERATION_SMDX_SUBJECT_REASON_CODE;
+
+        // Pad the 0s needed for subject code
+        result = result << (maxSupportedSection - subjectCodeToken.length) * bitsPerSection;
+
+        for (String digitString : subjectCodeToken) {
+            int num = Integer.parseInt(digitString);
+            if (num > maxSupportedDigit) {
+                throw new UnsupportedOperationException("SubjectCode exceeds " + maxSupportedDigit);
+            }
+            result = (result << bitsPerSection) + num;
+        }
+
+        // Pad the 0s needed for reason code
+        result = result << (maxSupportedSection - reasonCodeToken.length) * bitsPerSection;
+        for (String digitString : reasonCodeToken) {
+            int num = Integer.parseInt(digitString);
+            if (num > maxSupportedDigit) {
+                throw new UnsupportedOperationException("ReasonCode exceeds " + maxSupportedDigit);
+            }
+            result = (result << bitsPerSection) + num;
+        }
+
+        return result;
+    }
+
     @Override
     @CallSuper
     public void onCreate() {
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 01abb26..39a7543 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -19,6 +19,10 @@
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.hardware.radio.V1_1.GeranBands;
+import android.hardware.radio.V1_5.AccessNetwork;
+import android.hardware.radio.V1_5.EutranBands;
+import android.hardware.radio.V1_5.UtranBands;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -84,13 +88,13 @@
     public @interface RadioAccessNetworkType {}
 
     public static final class AccessNetworkType {
-        public static final int UNKNOWN = 0;
-        public static final int GERAN = 1;
-        public static final int UTRAN = 2;
-        public static final int EUTRAN = 3;
-        public static final int CDMA2000 = 4;
-        public static final int IWLAN = 5;
-        public static final int NGRAN = 6;
+        public static final int UNKNOWN = AccessNetwork.UNKNOWN;
+        public static final int GERAN = AccessNetwork.GERAN;
+        public static final int UTRAN = AccessNetwork.UTRAN;
+        public static final int EUTRAN = AccessNetwork.EUTRAN;
+        public static final int CDMA2000 = AccessNetwork.CDMA2000;
+        public static final int IWLAN = AccessNetwork.IWLAN;
+        public static final int NGRAN = AccessNetwork.NGRAN;
 
         /** @hide */
         private AccessNetworkType() {}
@@ -115,20 +119,20 @@
      * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf
      */
     public static final class GeranBand {
-        public static final int BAND_T380 = 1;
-        public static final int BAND_T410 = 2;
-        public static final int BAND_450 = 3;
-        public static final int BAND_480 = 4;
-        public static final int BAND_710 = 5;
-        public static final int BAND_750 = 6;
-        public static final int BAND_T810 = 7;
-        public static final int BAND_850 = 8;
-        public static final int BAND_P900 = 9;
-        public static final int BAND_E900 = 10;
-        public static final int BAND_R900 = 11;
-        public static final int BAND_DCS1800 = 12;
-        public static final int BAND_PCS1900 = 13;
-        public static final int BAND_ER900 = 14;
+        public static final int BAND_T380 = GeranBands.BAND_T380;
+        public static final int BAND_T410 = GeranBands.BAND_T410;
+        public static final int BAND_450 = GeranBands.BAND_450;
+        public static final int BAND_480 = GeranBands.BAND_480;
+        public static final int BAND_710 = GeranBands.BAND_710;
+        public static final int BAND_750 = GeranBands.BAND_750;
+        public static final int BAND_T810 = GeranBands.BAND_T810;
+        public static final int BAND_850 = GeranBands.BAND_850;
+        public static final int BAND_P900 = GeranBands.BAND_P900;
+        public static final int BAND_E900 = GeranBands.BAND_E900;
+        public static final int BAND_R900 = GeranBands.BAND_R900;
+        public static final int BAND_DCS1800 = GeranBands.BAND_DCS1800;
+        public static final int BAND_PCS1900 = GeranBands.BAND_PCS1900;
+        public static final int BAND_ER900 = GeranBands.BAND_ER900;
 
         /** @hide */
         private GeranBand() {}
@@ -139,28 +143,28 @@
      * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf
      */
     public static final class UtranBand {
-        public static final int BAND_1 = 1;
-        public static final int BAND_2 = 2;
-        public static final int BAND_3 = 3;
-        public static final int BAND_4 = 4;
-        public static final int BAND_5 = 5;
-        public static final int BAND_6 = 6;
-        public static final int BAND_7 = 7;
-        public static final int BAND_8 = 8;
-        public static final int BAND_9 = 9;
-        public static final int BAND_10 = 10;
-        public static final int BAND_11 = 11;
-        public static final int BAND_12 = 12;
-        public static final int BAND_13 = 13;
-        public static final int BAND_14 = 14;
+        public static final int BAND_1 = UtranBands.BAND_1;
+        public static final int BAND_2 = UtranBands.BAND_2;
+        public static final int BAND_3 = UtranBands.BAND_3;
+        public static final int BAND_4 = UtranBands.BAND_4;
+        public static final int BAND_5 = UtranBands.BAND_5;
+        public static final int BAND_6 = UtranBands.BAND_6;
+        public static final int BAND_7 = UtranBands.BAND_7;
+        public static final int BAND_8 = UtranBands.BAND_8;
+        public static final int BAND_9 = UtranBands.BAND_9;
+        public static final int BAND_10 = UtranBands.BAND_10;
+        public static final int BAND_11 = UtranBands.BAND_11;
+        public static final int BAND_12 = UtranBands.BAND_12;
+        public static final int BAND_13 = UtranBands.BAND_13;
+        public static final int BAND_14 = UtranBands.BAND_14;
         // band 15, 16, 17, 18 are reserved
-        public static final int BAND_19 = 19;
-        public static final int BAND_20 = 20;
-        public static final int BAND_21 = 21;
-        public static final int BAND_22 = 22;
+        public static final int BAND_19 = UtranBands.BAND_19;
+        public static final int BAND_20 = UtranBands.BAND_20;
+        public static final int BAND_21 = UtranBands.BAND_21;
+        public static final int BAND_22 = UtranBands.BAND_22;
         // band 23, 24 are reserved
-        public static final int BAND_25 = 25;
-        public static final int BAND_26 = 26;
+        public static final int BAND_25 = UtranBands.BAND_25;
+        public static final int BAND_26 = UtranBands.BAND_26;
 
         // Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2.
 
@@ -169,38 +173,38 @@
          * 1900 - 1920 MHz: Uplink and downlink transmission
          * 2010 - 2025 MHz: Uplink and downlink transmission
          */
-        public static final int BAND_A = 101;
+        public static final int BAND_A = UtranBands.BAND_A;
 
         /**
          * Band B
          * 1850 - 1910 MHz: Uplink and downlink transmission
          * 1930 - 1990 MHz: Uplink and downlink transmission
          */
-        public static final int BAND_B = 102;
+        public static final int BAND_B = UtranBands.BAND_B;
 
         /**
          * Band C
          * 1910 - 1930 MHz: Uplink and downlink transmission
          */
-        public static final int BAND_C = 103;
+        public static final int BAND_C = UtranBands.BAND_C;
 
         /**
          * Band D
          * 2570 - 2620 MHz: Uplink and downlink transmission
          */
-        public static final int BAND_D = 104;
+        public static final int BAND_D = UtranBands.BAND_D;
 
         /**
          * Band E
          * 2300—2400 MHz: Uplink and downlink transmission
          */
-        public static final int BAND_E = 105;
+        public static final int BAND_E = UtranBands.BAND_E;
 
         /**
          * Band F
          * 1880 - 1920 MHz: Uplink and downlink transmission
          */
-        public static final int BAND_F = 106;
+        public static final int BAND_F = UtranBands.BAND_F;
 
         /** @hide */
         private UtranBand() {}
@@ -208,57 +212,70 @@
 
     /**
      * Frequency bands for EUTRAN.
-     * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf
+     * 3GPP TS 36.101, Version 16.4.0, Table 5.5: Operating bands
+     * https://www.etsi.org/deliver/etsi_ts/136100_136199/136101/15.09.00_60/ts_136101v150900p.pdf
      */
     public static final class EutranBand {
-        public static final int BAND_1 = 1;
-        public static final int BAND_2 = 2;
-        public static final int BAND_3 = 3;
-        public static final int BAND_4 = 4;
-        public static final int BAND_5 = 5;
-        public static final int BAND_6 = 6;
-        public static final int BAND_7 = 7;
-        public static final int BAND_8 = 8;
-        public static final int BAND_9 = 9;
-        public static final int BAND_10 = 10;
-        public static final int BAND_11 = 11;
-        public static final int BAND_12 = 12;
-        public static final int BAND_13 = 13;
-        public static final int BAND_14 = 14;
-        public static final int BAND_17 = 17;
-        public static final int BAND_18 = 18;
-        public static final int BAND_19 = 19;
-        public static final int BAND_20 = 20;
-        public static final int BAND_21 = 21;
-        public static final int BAND_22 = 22;
-        public static final int BAND_23 = 23;
-        public static final int BAND_24 = 24;
-        public static final int BAND_25 = 25;
-        public static final int BAND_26 = 26;
-        public static final int BAND_27 = 27;
-        public static final int BAND_28 = 28;
-        public static final int BAND_30 = 30;
-        public static final int BAND_31 = 31;
-        public static final int BAND_33 = 33;
-        public static final int BAND_34 = 34;
-        public static final int BAND_35 = 35;
-        public static final int BAND_36 = 36;
-        public static final int BAND_37 = 37;
-        public static final int BAND_38 = 38;
-        public static final int BAND_39 = 39;
-        public static final int BAND_40 = 40;
-        public static final int BAND_41 = 41;
-        public static final int BAND_42 = 42;
-        public static final int BAND_43 = 43;
-        public static final int BAND_44 = 44;
-        public static final int BAND_45 = 45;
-        public static final int BAND_46 = 46;
-        public static final int BAND_47 = 47;
-        public static final int BAND_48 = 48;
-        public static final int BAND_65 = 65;
-        public static final int BAND_66 = 66;
-        public static final int BAND_68 = 68;
-        public static final int BAND_70 = 70;
+        public static final int BAND_1 = EutranBands.BAND_1;
+        public static final int BAND_2 = EutranBands.BAND_2;
+        public static final int BAND_3 = EutranBands.BAND_3;
+        public static final int BAND_4 = EutranBands.BAND_4;
+        public static final int BAND_5 = EutranBands.BAND_5;
+        public static final int BAND_6 = EutranBands.BAND_6;
+        public static final int BAND_7 = EutranBands.BAND_7;
+        public static final int BAND_8 = EutranBands.BAND_8;
+        public static final int BAND_9 = EutranBands.BAND_9;
+        public static final int BAND_10 = EutranBands.BAND_10;
+        public static final int BAND_11 = EutranBands.BAND_11;
+        public static final int BAND_12 = EutranBands.BAND_12;
+        public static final int BAND_13 = EutranBands.BAND_13;
+        public static final int BAND_14 = EutranBands.BAND_14;
+        public static final int BAND_17 = EutranBands.BAND_17;
+        public static final int BAND_18 = EutranBands.BAND_18;
+        public static final int BAND_19 = EutranBands.BAND_19;
+        public static final int BAND_20 = EutranBands.BAND_20;
+        public static final int BAND_21 = EutranBands.BAND_21;
+        public static final int BAND_22 = EutranBands.BAND_22;
+        public static final int BAND_23 = EutranBands.BAND_23;
+        public static final int BAND_24 = EutranBands.BAND_24;
+        public static final int BAND_25 = EutranBands.BAND_25;
+        public static final int BAND_26 = EutranBands.BAND_26;
+        public static final int BAND_27 = EutranBands.BAND_27;
+        public static final int BAND_28 = EutranBands.BAND_28;
+        public static final int BAND_30 = EutranBands.BAND_30;
+        public static final int BAND_31 = EutranBands.BAND_31;
+        public static final int BAND_33 = EutranBands.BAND_33;
+        public static final int BAND_34 = EutranBands.BAND_34;
+        public static final int BAND_35 = EutranBands.BAND_35;
+        public static final int BAND_36 = EutranBands.BAND_36;
+        public static final int BAND_37 = EutranBands.BAND_37;
+        public static final int BAND_38 = EutranBands.BAND_38;
+        public static final int BAND_39 = EutranBands.BAND_39;
+        public static final int BAND_40 = EutranBands.BAND_40;
+        public static final int BAND_41 = EutranBands.BAND_41;
+        public static final int BAND_42 = EutranBands.BAND_42;
+        public static final int BAND_43 = EutranBands.BAND_43;
+        public static final int BAND_44 = EutranBands.BAND_44;
+        public static final int BAND_45 = EutranBands.BAND_45;
+        public static final int BAND_46 = EutranBands.BAND_46;
+        public static final int BAND_47 = EutranBands.BAND_47;
+        public static final int BAND_48 = EutranBands.BAND_48;
+        public static final int BAND_49 = EutranBands.BAND_49;
+        public static final int BAND_50 = EutranBands.BAND_50;
+        public static final int BAND_51 = EutranBands.BAND_51;
+        public static final int BAND_52 = EutranBands.BAND_52;
+        public static final int BAND_53 = EutranBands.BAND_53;
+        public static final int BAND_65 = EutranBands.BAND_65;
+        public static final int BAND_66 = EutranBands.BAND_66;
+        public static final int BAND_68 = EutranBands.BAND_68;
+        public static final int BAND_70 = EutranBands.BAND_70;
+        public static final int BAND_71 = EutranBands.BAND_71;
+        public static final int BAND_72 = EutranBands.BAND_72;
+        public static final int BAND_73 = EutranBands.BAND_73;
+        public static final int BAND_74 = EutranBands.BAND_74;
+        public static final int BAND_85 = EutranBands.BAND_85;
+        public static final int BAND_87 = EutranBands.BAND_87;
+        public static final int BAND_88 = EutranBands.BAND_88;
 
         /** @hide */
         private EutranBand() {};
@@ -301,54 +318,62 @@
 
     /**
      * Frequency bands for NGRAN
+     * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810101/15.08.02_60/ts_13810101v150802p.pdf
+     * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810102/15.08.00_60/ts_13810102v150800p.pdf
      */
     public static final class NgranBands {
-        /** FR1 bands */
-        public static final int BAND_1 = 1;
-        public static final int BAND_2 = 2;
-        public static final int BAND_3 = 3;
-        public static final int BAND_5 = 5;
-        public static final int BAND_7 = 7;
-        public static final int BAND_8 = 8;
-        public static final int BAND_12 = 12;
-        public static final int BAND_14 = 14;
-        public static final int BAND_18 = 18;
-        public static final int BAND_20 = 20;
-        public static final int BAND_25 = 25;
-        public static final int BAND_28 = 28;
-        public static final int BAND_29 = 29;
-        public static final int BAND_30 = 30;
-        public static final int BAND_34 = 34;
-        public static final int BAND_38 = 38;
-        public static final int BAND_39 = 39;
-        public static final int BAND_40 = 40;
-        public static final int BAND_41 = 41;
-        public static final int BAND_48 = 48;
-        public static final int BAND_50 = 50;
-        public static final int BAND_51 = 51;
-        public static final int BAND_65 = 65;
-        public static final int BAND_66 = 66;
-        public static final int BAND_70 = 70;
-        public static final int BAND_71 = 71;
-        public static final int BAND_74 = 74;
-        public static final int BAND_75 = 75;
-        public static final int BAND_76 = 76;
-        public static final int BAND_77 = 77;
-        public static final int BAND_78 = 78;
-        public static final int BAND_79 = 79;
-        public static final int BAND_80 = 80;
-        public static final int BAND_81 = 81;
-        public static final int BAND_82 = 82;
-        public static final int BAND_83 = 83;
-        public static final int BAND_84 = 84;
-        public static final int BAND_86 = 86;
-        public static final int BAND_90 = 90;
+        /** 3GPP TS 38.101-1, Version 16.2.0, Table 5.2-1: FR1 bands */
+        public static final int BAND_1 = android.hardware.radio.V1_5.NgranBands.BAND_1;
+        public static final int BAND_2 = android.hardware.radio.V1_5.NgranBands.BAND_2;
+        public static final int BAND_3 = android.hardware.radio.V1_5.NgranBands.BAND_3;
+        public static final int BAND_5 = android.hardware.radio.V1_5.NgranBands.BAND_5;
+        public static final int BAND_7 = android.hardware.radio.V1_5.NgranBands.BAND_7;
+        public static final int BAND_8 = android.hardware.radio.V1_5.NgranBands.BAND_8;
+        public static final int BAND_12 = android.hardware.radio.V1_5.NgranBands.BAND_12;
+        public static final int BAND_14 = android.hardware.radio.V1_5.NgranBands.BAND_14;
+        public static final int BAND_18 = android.hardware.radio.V1_5.NgranBands.BAND_18;
+        public static final int BAND_20 = android.hardware.radio.V1_5.NgranBands.BAND_20;
+        public static final int BAND_25 = android.hardware.radio.V1_5.NgranBands.BAND_25;
+        public static final int BAND_28 = android.hardware.radio.V1_5.NgranBands.BAND_28;
+        public static final int BAND_29 = android.hardware.radio.V1_5.NgranBands.BAND_29;
+        public static final int BAND_30 = android.hardware.radio.V1_5.NgranBands.BAND_30;
+        public static final int BAND_34 = android.hardware.radio.V1_5.NgranBands.BAND_34;
+        public static final int BAND_38 = android.hardware.radio.V1_5.NgranBands.BAND_38;
+        public static final int BAND_39 = android.hardware.radio.V1_5.NgranBands.BAND_39;
+        public static final int BAND_40 = android.hardware.radio.V1_5.NgranBands.BAND_40;
+        public static final int BAND_41 = android.hardware.radio.V1_5.NgranBands.BAND_41;
+        public static final int BAND_48 = android.hardware.radio.V1_5.NgranBands.BAND_48;
+        public static final int BAND_50 = android.hardware.radio.V1_5.NgranBands.BAND_50;
+        public static final int BAND_51 = android.hardware.radio.V1_5.NgranBands.BAND_51;
+        public static final int BAND_65 = android.hardware.radio.V1_5.NgranBands.BAND_65;
+        public static final int BAND_66 = android.hardware.radio.V1_5.NgranBands.BAND_66;
+        public static final int BAND_70 = android.hardware.radio.V1_5.NgranBands.BAND_70;
+        public static final int BAND_71 = android.hardware.radio.V1_5.NgranBands.BAND_71;
+        public static final int BAND_74 = android.hardware.radio.V1_5.NgranBands.BAND_74;
+        public static final int BAND_75 = android.hardware.radio.V1_5.NgranBands.BAND_75;
+        public static final int BAND_76 = android.hardware.radio.V1_5.NgranBands.BAND_76;
+        public static final int BAND_77 = android.hardware.radio.V1_5.NgranBands.BAND_77;
+        public static final int BAND_78 = android.hardware.radio.V1_5.NgranBands.BAND_78;
+        public static final int BAND_79 = android.hardware.radio.V1_5.NgranBands.BAND_79;
+        public static final int BAND_80 = android.hardware.radio.V1_5.NgranBands.BAND_80;
+        public static final int BAND_81 = android.hardware.radio.V1_5.NgranBands.BAND_81;
+        public static final int BAND_82 = android.hardware.radio.V1_5.NgranBands.BAND_82;
+        public static final int BAND_83 = android.hardware.radio.V1_5.NgranBands.BAND_83;
+        public static final int BAND_84 = android.hardware.radio.V1_5.NgranBands.BAND_84;
+        public static final int BAND_86 = android.hardware.radio.V1_5.NgranBands.BAND_86;
+        public static final int BAND_89 = android.hardware.radio.V1_5.NgranBands.BAND_89;
+        public static final int BAND_90 = android.hardware.radio.V1_5.NgranBands.BAND_90;
+        public static final int BAND_91 = android.hardware.radio.V1_5.NgranBands.BAND_91;
+        public static final int BAND_92 = android.hardware.radio.V1_5.NgranBands.BAND_92;
+        public static final int BAND_93 = android.hardware.radio.V1_5.NgranBands.BAND_93;
+        public static final int BAND_94 = android.hardware.radio.V1_5.NgranBands.BAND_94;
+        public static final int BAND_95 = android.hardware.radio.V1_5.NgranBands.BAND_95;
 
-        /** FR2 bands */
-        public static final int BAND_257 = 257;
-        public static final int BAND_258 = 258;
-        public static final int BAND_260 = 260;
-        public static final int BAND_261 = 261;
+        /** 3GPP TS 38.101-2, Version 16.2.0, Table 5.2-1: FR2 bands */
+        public static final int BAND_257 = android.hardware.radio.V1_5.NgranBands.BAND_257;
+        public static final int BAND_258 = android.hardware.radio.V1_5.NgranBands.BAND_258;
+        public static final int BAND_260 = android.hardware.radio.V1_5.NgranBands.BAND_260;
+        public static final int BAND_261 = android.hardware.radio.V1_5.NgranBands.BAND_261;
 
         /**
          * NR Bands
@@ -394,7 +419,13 @@
                         BAND_83,
                         BAND_84,
                         BAND_86,
+                        BAND_89,
                         BAND_90,
+                        BAND_91,
+                        BAND_92,
+                        BAND_93,
+                        BAND_94,
+                        BAND_95,
                         BAND_257,
                         BAND_258,
                         BAND_260,
@@ -491,7 +522,13 @@
                 case BAND_83:
                 case BAND_84:
                 case BAND_86:
+                case BAND_89:
                 case BAND_90:
+                case BAND_91:
+                case BAND_92:
+                case BAND_93:
+                case BAND_94:
+                case BAND_95:
                     return FREQUENCY_RANGE_GROUP_1;
                 case BAND_257:
                 case BAND_258:
diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java
index 5d2c225..981ed450 100644
--- a/telephony/java/android/telephony/AccessNetworkUtils.java
+++ b/telephony/java/android/telephony/AccessNetworkUtils.java
@@ -34,12 +34,10 @@
             return DUPLEX_MODE_UNKNOWN;
         }
 
-        if (band >= EutranBand.BAND_68) {
+        if (band > EutranBand.BAND_88) {
             return DUPLEX_MODE_UNKNOWN;
         } else if (band >= EutranBand.BAND_65) {
             return DUPLEX_MODE_FDD;
-        } else if (band >= EutranBand.BAND_47) {
-            return DUPLEX_MODE_UNKNOWN;
         } else if (band >= EutranBand.BAND_33) {
             return DUPLEX_MODE_TDD;
         } else if (band >= EutranBand.BAND_1) {
@@ -58,17 +56,53 @@
      * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
      */
     public static int getOperatingBandForEarfcn(int earfcn) {
-        if (earfcn > 67535) {
+        if (earfcn > 70645) {
             return INVALID_BAND;
+        } else if (earfcn >= 70596) {
+            return EutranBand.BAND_88;
+        } else if (earfcn >= 70546) {
+            return EutranBand.BAND_87;
+        } else if (earfcn >= 70366) {
+            return EutranBand.BAND_85;
+        } else if (earfcn > 69465) {
+            return INVALID_BAND;
+        } else if (earfcn >= 69036) {
+            return EutranBand.BAND_74;
+        } else if (earfcn >= 68986) {
+            return EutranBand.BAND_73;
+        } else if (earfcn >= 68936) {
+            return EutranBand.BAND_72;
+        } else if (earfcn >= 68586) {
+            return EutranBand.BAND_71;
+        } else if (earfcn >= 68336) {
+            return EutranBand.BAND_70;
+        } else if (earfcn > 67835) {
+            return INVALID_BAND;
+        } else if (earfcn >= 67536) {
+            return EutranBand.BAND_68;
         } else if (earfcn >= 67366) {
             return INVALID_BAND; // band 67 only for CarrierAgg
         } else if (earfcn >= 66436) {
             return EutranBand.BAND_66;
         } else if (earfcn >= 65536) {
             return EutranBand.BAND_65;
-        } else if (earfcn > 54339) {
+        } else if (earfcn > 60254) {
             return INVALID_BAND;
-        } else if (earfcn >= 46790 /* inferred from the end range of BAND_45 */) {
+        } else if (earfcn >= 60140) {
+            return EutranBand.BAND_53;
+        } else if (earfcn >= 59140) {
+            return EutranBand.BAND_52;
+        } else if (earfcn >= 59090) {
+            return EutranBand.BAND_51;
+        } else if (earfcn >= 58240) {
+            return EutranBand.BAND_50;
+        } else if (earfcn >= 56740) {
+            return EutranBand.BAND_49;
+        } else if (earfcn >= 55240) {
+            return EutranBand.BAND_48;
+        } else if (earfcn >= 54540) {
+            return EutranBand.BAND_47;
+        } else if (earfcn >= 46790) {
             return EutranBand.BAND_46;
         } else if (earfcn >= 46590) {
             return EutranBand.BAND_45;
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 0325c36..f0ed1a3 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -571,6 +571,19 @@
     public @interface PreciseDisconnectCauses {
     }
 
+    /**
+     * Carrier Privilege Status.
+     */
+    @IntDef(prefix = { "CARRIER_PRIVILEGE_STATUS_" }, value = {
+        TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS,
+        TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS,
+        TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED,
+        TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CarrierPrivilegeStatus {
+    }
+
     @IntDef({
             Connection.AUDIO_CODEC_NONE,
             Connection.AUDIO_CODEC_AMR,
@@ -598,48 +611,6 @@
     }
 
     /**
-     * Call forwarding function status
-     */
-    @IntDef(prefix = { "STATUS_" }, value = {
-        CallForwardingInfo.STATUS_ACTIVE,
-        CallForwardingInfo.STATUS_INACTIVE,
-        CallForwardingInfo.STATUS_UNKNOWN_ERROR,
-        CallForwardingInfo.STATUS_NOT_SUPPORTED,
-        CallForwardingInfo.STATUS_FDN_CHECK_FAILURE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CallForwardingStatus {
-    }
-
-    /**
-     * Call forwarding reason types
-     */
-    @IntDef(flag = true, prefix = { "REASON_" }, value = {
-        CallForwardingInfo.REASON_UNCONDITIONAL,
-        CallForwardingInfo.REASON_BUSY,
-        CallForwardingInfo.REASON_NO_REPLY,
-        CallForwardingInfo.REASON_NOT_REACHABLE,
-        CallForwardingInfo.REASON_ALL,
-        CallForwardingInfo.REASON_ALL_CONDITIONAL
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CallForwardingReason {
-    }
-
-    /**
-     * Call waiting function status
-     */
-    @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
-        TelephonyManager.CALL_WAITING_STATUS_ACTIVE,
-        TelephonyManager.CALL_WAITING_STATUS_INACTIVE,
-        TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED,
-        TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CallWaitingStatus {
-    }
-
-    /**
      * UICC SIM Application Types
      */
     @IntDef(prefix = { "APPTYPE_" }, value = {
@@ -657,10 +628,10 @@
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "OVERRIDE_NETWORK_TYPE_", value = {
-            DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
-            DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
-            DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO,
-            DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
-            DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE})
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO,
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE})
     public @interface OverrideNetworkType {}
 }
diff --git a/telephony/java/android/telephony/DisplayInfo.aidl b/telephony/java/android/telephony/BarringInfo.aidl
similarity index 86%
copy from telephony/java/android/telephony/DisplayInfo.aidl
copy to telephony/java/android/telephony/BarringInfo.aidl
index 861b0fe..50ddf6b 100644
--- a/telephony/java/android/telephony/DisplayInfo.aidl
+++ b/telephony/java/android/telephony/BarringInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/** @hide */
 package android.telephony;
 
-parcelable DisplayInfo;
+parcelable BarringInfo;
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
new file mode 100644
index 0000000..92423a2
--- /dev/null
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -0,0 +1,398 @@
+/*
+ * 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;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides the barring configuration for a particular service type.
+ *
+ * Provides indication about the barring of a particular service for use. Certain barring types
+ * are only valid for certain technology families. Any service that does not have a barring
+ * configuration is unbarred by default.
+ */
+public final class BarringInfo implements Parcelable {
+
+    /**
+     * Barring Service Type
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "BARRING_SERVICE_TYPE_", value = {
+            BARRING_SERVICE_TYPE_CS_SERVICE,
+            BARRING_SERVICE_TYPE_PS_SERVICE,
+            BARRING_SERVICE_TYPE_CS_VOICE,
+            BARRING_SERVICE_TYPE_MO_SIGNALLING,
+            BARRING_SERVICE_TYPE_MO_DATA,
+            BARRING_SERVICE_TYPE_CS_FALLBACK,
+            BARRING_SERVICE_TYPE_MMTEL_VOICE,
+            BARRING_SERVICE_TYPE_MMTEL_VIDEO,
+            BARRING_SERVICE_TYPE_EMERGENCY,
+            BARRING_SERVICE_TYPE_SMS})
+    public @interface BarringServiceType {}
+
+    /* Applicabe to UTRAN */
+    /** Barring indicator for circuit-switched service; applicable to UTRAN */
+    public static final int BARRING_SERVICE_TYPE_CS_SERVICE =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.CS_SERVICE;
+    /** Barring indicator for packet-switched service; applicable to UTRAN */
+    public static final int BARRING_SERVICE_TYPE_PS_SERVICE =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.PS_SERVICE;
+    /** Barring indicator for circuit-switched voice service; applicable to UTRAN */
+    public static final int BARRING_SERVICE_TYPE_CS_VOICE =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.CS_VOICE;
+
+    /* Applicable to EUTRAN, NGRAN */
+    /** Barring indicator for mobile-originated signalling; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_MO_SIGNALLING =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.MO_SIGNALLING;
+    /** Barring indicator for mobile-originated data traffic; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_MO_DATA =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.MO_DATA;
+    /** Barring indicator for circuit-switched fallback for voice; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_CS_FALLBACK =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.CS_FALLBACK;
+    /** Barring indicator for MMTEL (IMS) voice; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_MMTEL_VOICE =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.MMTEL_VOICE;
+    /** Barring indicator for MMTEL (IMS) video; applicable to EUTRAN and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_MMTEL_VIDEO =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.MMTEL_VIDEO;
+
+    /* Applicable to UTRAN, EUTRAN, NGRAN */
+    /** Barring indicator for emergency services; applicable to UTRAN, EUTRAN, and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_EMERGENCY =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.EMERGENCY;
+    /** Barring indicator for SMS sending; applicable to UTRAN, EUTRAN, and NGRAN */
+    public static final int BARRING_SERVICE_TYPE_SMS =
+            android.hardware.radio.V1_5.BarringInfo.ServiceType.SMS;
+
+    //TODO: add barring constants for Operator-Specific barring codes
+
+    /** Describe the current barring configuration of a cell */
+    public static final class BarringServiceInfo implements Parcelable {
+        /**
+         * Barring Type
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = "BARRING_TYPE_", value =
+                    {BARRING_TYPE_NONE,
+                    BARRING_TYPE_UNCONDITIONAL,
+                    BARRING_TYPE_CONDITIONAL,
+                    BARRING_TYPE_UNKNOWN})
+        public @interface BarringType {}
+
+        /** Barring is inactive */
+        public static final int BARRING_TYPE_NONE =
+                android.hardware.radio.V1_5.BarringInfo.BarringType.NONE;
+        /** The service is barred */
+        public static final int BARRING_TYPE_UNCONDITIONAL =
+                android.hardware.radio.V1_5.BarringInfo.BarringType.UNCONDITIONAL;
+        /** The service may be barred based on additional factors */
+        public static final int BARRING_TYPE_CONDITIONAL =
+                android.hardware.radio.V1_5.BarringInfo.BarringType.CONDITIONAL;
+
+        /** If a modem does not report barring info, then the barring type will be UNKNOWN */
+        public static final int BARRING_TYPE_UNKNOWN = -1;
+
+        private final @BarringType int mBarringType;
+
+        private final boolean mIsConditionallyBarred;
+        private final int mConditionalBarringFactor;
+        private final int mConditionalBarringTimeSeconds;
+
+        /** @hide */
+        public BarringServiceInfo(@BarringType int type) {
+            this(type, false, 0, 0);
+        }
+
+        /** @hide */
+        @TestApi
+        public BarringServiceInfo(@BarringType int barringType, boolean isConditionallyBarred,
+                int conditionalBarringFactor, int conditionalBarringTimeSeconds) {
+            mBarringType = barringType;
+            mIsConditionallyBarred = isConditionallyBarred;
+            mConditionalBarringFactor = conditionalBarringFactor;
+            mConditionalBarringTimeSeconds = conditionalBarringTimeSeconds;
+        }
+
+        public @BarringType int getBarringType() {
+            return mBarringType;
+        }
+
+        /**
+         * @return true if the conditional barring parameters have resulted in the service being
+         *         barred; false if the service has either not been evaluated for conditional
+         *         barring or has been evaluated and isn't barred.
+         */
+        public boolean isConditionallyBarred() {
+            return mIsConditionallyBarred;
+        }
+
+        /**
+         * @return the conditional barring factor as a percentage 0-100, which is the probability of
+         *         a random device being barred for the service type.
+         */
+        public int getConditionalBarringFactor() {
+            return mConditionalBarringFactor;
+        }
+
+        /**
+         * @return the conditional barring time seconds, which is the interval between successive
+         *         evaluations for conditional barring based on the barring factor.
+         */
+        @SuppressLint("MethodNameUnits")
+        public int getConditionalBarringTimeSeconds() {
+            return mConditionalBarringTimeSeconds;
+        }
+
+        /**
+         * Return whether a service is currently barred based on the BarringInfo
+         *
+         * @return true if the service is currently being barred, otherwise false
+         */
+        public boolean isBarred() {
+            return mBarringType == BarringServiceInfo.BARRING_TYPE_UNCONDITIONAL
+                    || (mBarringType == BarringServiceInfo.BARRING_TYPE_CONDITIONAL
+                            && mIsConditionallyBarred);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mBarringType, mIsConditionallyBarred,
+                    mConditionalBarringFactor, mConditionalBarringTimeSeconds);
+        }
+
+        @Override
+        public boolean equals(Object rhs) {
+            if (!(rhs instanceof BarringServiceInfo)) return false;
+
+            BarringServiceInfo other = (BarringServiceInfo) rhs;
+            return mBarringType == other.mBarringType
+                    && mIsConditionallyBarred == other.mIsConditionallyBarred
+                    && mConditionalBarringFactor == other.mConditionalBarringFactor
+                    && mConditionalBarringTimeSeconds == other.mConditionalBarringTimeSeconds;
+        }
+
+        /** @hide */
+        public BarringServiceInfo(Parcel p) {
+            mBarringType = p.readInt();
+            mIsConditionallyBarred = p.readBoolean();
+            mConditionalBarringFactor = p.readInt();
+            mConditionalBarringTimeSeconds = p.readInt();
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mBarringType);
+            dest.writeBoolean(mIsConditionallyBarred);
+            dest.writeInt(mConditionalBarringFactor);
+            dest.writeInt(mConditionalBarringTimeSeconds);
+        }
+
+        /* @inheritDoc */
+        public static final @NonNull Parcelable.Creator<BarringServiceInfo> CREATOR =
+                new Parcelable.Creator<BarringServiceInfo>() {
+                    @Override
+                    public BarringServiceInfo createFromParcel(Parcel source) {
+                        return new BarringServiceInfo(source);
+                    }
+
+                    @Override
+                    public BarringServiceInfo[] newArray(int size) {
+                        return new BarringServiceInfo[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+    }
+
+    private static final BarringServiceInfo BARRING_SERVICE_INFO_UNKNOWN =
+            new BarringServiceInfo(BarringServiceInfo.BARRING_TYPE_UNKNOWN);
+
+    private static final BarringServiceInfo BARRING_SERVICE_INFO_UNBARRED =
+            new BarringServiceInfo(BarringServiceInfo.BARRING_TYPE_NONE);
+
+    private CellIdentity mCellIdentity;
+
+    // A SparseArray potentially mapping each BarringService type to a BarringServiceInfo config
+    // that describes the current barring status of that particular service.
+    private SparseArray<BarringServiceInfo> mBarringServiceInfos;
+
+    /** @hide */
+    @TestApi
+    @SystemApi
+    public BarringInfo() {
+        mBarringServiceInfos = new SparseArray<>();
+    }
+
+    /**
+     * Constructor for new BarringInfo instances.
+     *
+     * @hide
+     */
+    @TestApi
+    public BarringInfo(@Nullable CellIdentity barringCellId,
+            @NonNull SparseArray<BarringServiceInfo> barringServiceInfos) {
+        mCellIdentity = barringCellId;
+        mBarringServiceInfos = barringServiceInfos;
+    }
+
+    /** @hide */
+    public static BarringInfo create(
+            @NonNull android.hardware.radio.V1_5.CellIdentity halBarringCellId,
+            @NonNull List<android.hardware.radio.V1_5.BarringInfo> halBarringInfos) {
+        CellIdentity ci = CellIdentity.create(halBarringCellId);
+        SparseArray<BarringServiceInfo> serviceInfos = new SparseArray<>();
+
+        for (android.hardware.radio.V1_5.BarringInfo halBarringInfo : halBarringInfos) {
+            if (halBarringInfo.barringType
+                    == android.hardware.radio.V1_5.BarringInfo.BarringType.CONDITIONAL) {
+                if (halBarringInfo.barringTypeSpecificInfo.getDiscriminator()
+                        != android.hardware.radio.V1_5.BarringInfo.BarringTypeSpecificInfo
+                                .hidl_discriminator.conditional) {
+                    // this is an error case where the barring info is conditional but the
+                    // conditional barring fields weren't included
+                    continue;
+                }
+                android.hardware.radio.V1_5.BarringInfo.BarringTypeSpecificInfo
+                        .Conditional conditionalInfo =
+                        halBarringInfo.barringTypeSpecificInfo.conditional();
+                serviceInfos.put(
+                        halBarringInfo.serviceType, new BarringServiceInfo(
+                                halBarringInfo.barringType, // will always be CONDITIONAL here
+                                conditionalInfo.isBarred,
+                                conditionalInfo.factor,
+                                conditionalInfo.timeSeconds));
+            } else {
+                // Barring type is either NONE or UNCONDITIONAL
+                serviceInfos.put(
+                        halBarringInfo.serviceType, new BarringServiceInfo(
+                                halBarringInfo.barringType, false, 0, 0));
+            }
+        }
+        return new BarringInfo(ci, serviceInfos);
+    }
+
+    /**
+     * Get the BarringServiceInfo for a specified service.
+     *
+     * @return a BarringServiceInfo struct describing the current barring status for a service
+     */
+    public @NonNull BarringServiceInfo getBarringServiceInfo(@BarringServiceType int service) {
+        BarringServiceInfo bsi = mBarringServiceInfos.get(service);
+        // If barring is reported but not for a particular service, then we report the barring
+        // type as UNKNOWN; if the modem reports barring info but doesn't report for a particular
+        // service then we can safely assume that the service isn't barred (for instance because
+        // that particular service isn't applicable to the current RAN).
+        return (bsi != null) ? bsi : mBarringServiceInfos.size() > 0
+                ? BARRING_SERVICE_INFO_UNBARRED : BARRING_SERVICE_INFO_UNKNOWN;
+    }
+
+    /** @hide */
+    @SystemApi
+    public @NonNull BarringInfo createLocationInfoSanitizedCopy() {
+        // The only thing that would need sanitizing is the CellIdentity
+        if (mCellIdentity == null) return this;
+
+        return new BarringInfo(mCellIdentity.sanitizeLocationInfo(), mBarringServiceInfos);
+    }
+
+    /** @hide */
+    public BarringInfo(Parcel p) {
+        mCellIdentity = p.readParcelable(CellIdentity.class.getClassLoader());
+        mBarringServiceInfos = p.readSparseArray(BarringServiceInfo.class.getClassLoader());
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mCellIdentity, flags);
+        dest.writeSparseArray(mBarringServiceInfos);
+    }
+
+    public static final @NonNull Parcelable.Creator<BarringInfo> CREATOR =
+            new Parcelable.Creator<BarringInfo>() {
+                @Override
+                public BarringInfo createFromParcel(Parcel source) {
+                    return new BarringInfo(source);
+                }
+
+                @Override
+                public BarringInfo[] newArray(int size) {
+                    return new BarringInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = mCellIdentity != null ? mCellIdentity.hashCode() : 7;
+        for (int i = 0; i < mBarringServiceInfos.size(); i++) {
+            hash = hash + 15 * mBarringServiceInfos.keyAt(i);
+            hash = hash + 31 * mBarringServiceInfos.valueAt(i).hashCode();
+        }
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object rhs) {
+        if (!(rhs instanceof BarringInfo)) return false;
+
+        BarringInfo bi = (BarringInfo) rhs;
+
+        if (hashCode() != bi.hashCode()) return false;
+
+        if (mBarringServiceInfos.size() != bi.mBarringServiceInfos.size()) return false;
+
+        for (int i = 0; i < mBarringServiceInfos.size(); i++) {
+            if (mBarringServiceInfos.keyAt(i) != bi.mBarringServiceInfos.keyAt(i)) return false;
+            if (!Objects.equals(mBarringServiceInfos.valueAt(i),
+                        bi.mBarringServiceInfos.valueAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "BarringInfo {mCellIdentity=" + mCellIdentity
+               + ", mBarringServiceInfos=" + mBarringServiceInfos + "}";
+    }
+}
diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java
index 1dd7539..7e777fa 100644
--- a/telephony/java/android/telephony/CallForwardingInfo.java
+++ b/telephony/java/android/telephony/CallForwardingInfo.java
@@ -15,24 +15,24 @@
  */
 
 package android.telephony;
+
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Annotation.CallForwardingReason;
-import android.telephony.Annotation.CallForwardingStatus;
 
 import com.android.telephony.Rlog;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
  * Defines the call forwarding information.
  * @hide
  */
-@SystemApi
 public final class CallForwardingInfo implements Parcelable {
     private static final String TAG = "CallForwardingInfo";
 
@@ -41,7 +41,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_INACTIVE = 0;
 
     /**
@@ -49,7 +48,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_ACTIVE = 1;
 
     /**
@@ -58,7 +56,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_FDN_CHECK_FAILURE = 2;
 
     /**
@@ -66,7 +63,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_UNKNOWN_ERROR = 3;
 
     /**
@@ -74,7 +70,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_NOT_SUPPORTED = 4;
 
     /**
@@ -83,7 +78,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_UNCONDITIONAL = 0;
 
     /**
@@ -92,7 +86,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_BUSY = 1;
 
     /**
@@ -101,7 +94,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_NO_REPLY = 2;
 
     /**
@@ -110,7 +102,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_NOT_REACHABLE = 3;
 
     /**
@@ -120,7 +111,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_ALL = 4;
 
     /**
@@ -130,20 +120,48 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_ALL_CONDITIONAL = 5;
 
     /**
+     * Call forwarding function status
+     */
+    @IntDef(prefix = { "STATUS_" }, value = {
+        STATUS_ACTIVE,
+        STATUS_INACTIVE,
+        STATUS_UNKNOWN_ERROR,
+        STATUS_NOT_SUPPORTED,
+        STATUS_FDN_CHECK_FAILURE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallForwardingStatus {
+    }
+
+    /**
+     * Call forwarding reason types
+     */
+    @IntDef(flag = true, prefix = { "REASON_" }, value = {
+        REASON_UNCONDITIONAL,
+        REASON_BUSY,
+        REASON_NO_REPLY,
+        REASON_NOT_REACHABLE,
+        REASON_ALL,
+        REASON_ALL_CONDITIONAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallForwardingReason {
+    }
+
+    /**
      * The call forwarding status.
      */
-    private @CallForwardingStatus int mStatus;
+    private int mStatus;
 
     /**
      * The call forwarding reason indicates the condition under which calls will be forwarded.
      * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
      *            and conditions +CCFC
      */
-    private @CallForwardingReason int mReason;
+    private int mReason;
 
     /**
      * The phone number to which calls will be forwarded.
@@ -166,7 +184,6 @@
      * @param timeSeconds the timeout (in seconds) before the forwarding is attempted
      * @hide
      */
-    @SystemApi
     public CallForwardingInfo(@CallForwardingStatus int status, @CallForwardingReason int reason,
             @Nullable String number, int timeSeconds) {
         mStatus = status;
@@ -182,7 +199,6 @@
      *
      * @hide
      */
-    @SystemApi
     public @CallForwardingStatus int getStatus() {
         return mStatus;
     }
@@ -196,7 +212,6 @@
      *
      * @hide
      */
-    @SystemApi
     public @CallForwardingReason int getReason() {
         return mReason;
     }
@@ -209,7 +224,6 @@
      *
      * @hide
      */
-    @SystemApi
     @Nullable
     public String getNumber() {
         return mNumber;
@@ -227,7 +241,6 @@
      *
      * @hide
      */
-    @SystemApi
     @SuppressLint("MethodNameUnits")
     public int getTimeoutSeconds() {
         return mTimeSeconds;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 57a47d3..4d15a93 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -37,6 +37,8 @@
 import com.android.internal.telephony.ICarrierConfigLoader;
 import com.android.telephony.Rlog;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Provides access to telephony configuration values that are carrier-specific.
  */
@@ -51,6 +53,14 @@
     public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
 
     /**
+     * Extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate whether this is a
+     * rebroadcast on unlock. Defaults to {@code false} if not specified.
+     * @hide
+     */
+    public static final String EXTRA_REBROADCAST_ON_UNLOCK =
+            "android.telephony.extra.REBROADCAST_ON_UNLOCK";
+
+    /**
      * Optional extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate the
      * subscription index that the broadcast is for, if a valid one is available.
      */
@@ -1166,6 +1176,21 @@
             "support_ims_conference_event_package_bool";
 
     /**
+     * Determines whether processing of conference event package data received on a device other
+     * than the conference host is supported.
+     * <p>
+     * When a device A merges calls B and C into a conference it is considered the conference host
+     * and B and C are considered the conference peers.
+     * <p>
+     * When {@code true}, the conference peer will display the conference state if it receives
+     * conference event package data from the network.  When {@code false}, the conference peer will
+     * ignore conference event package data received from the network.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL =
+            "support_ims_conference_event_package_on_peer_bool";
+
+    /**
      * Determines whether High Definition audio property is displayed in the dialer UI.
      * If {@code false}, remove the HD audio property from the connection so that HD audio related
      * UI is not displayed. If {@code true}, keep HD audio property as it is configured.
@@ -1182,6 +1207,25 @@
             "support_ims_conference_call_bool";
 
     /**
+     * Determines whether the device will locally disconnect an IMS conference when the participant
+     * count drops to zero.  When {@code true}, it is assumed the carrier does NOT disconnect a
+     * conference when the participant count drops to zero and that the device must do this by
+     * disconnecting the conference locally.  When {@code false}, it is assumed that the carrier
+     * is responsible for disconnecting the conference when there are no longer any participants
+     * present.
+     * <p>
+     * Note: both {@link #KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL} and
+     * {@link #KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL} must be true for this configuration to
+     * have any effect.
+     * <p>
+     * Defaults to {@code false}, meaning the carrier network is responsible for disconnecting an
+     * empty IMS conference.
+     * @hide
+     */
+    public static final String KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL =
+            "local_disconnect_empty_ims_conference_bool";
+
+    /**
      * Determines whether video conference calls are supported by a carrier.  When {@code true},
      * video calls can be merged into conference calls, {@code false} otherwiwse.
      * <p>
@@ -1551,6 +1595,7 @@
     /**
      * The string is used to compare with operator name.
      * If it matches the pattern then show specific data icon.
+     * @hide
      */
     public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING =
             "show_carrier_data_icon_pattern_string";
@@ -2362,17 +2407,16 @@
      * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
      * not be used for calculating signal level. If multiple measures are set bit, the parameter
      * whose value is smallest is used to indicate the signal level.
+     * <UL>
+     *  <LI>RSRP = 1 << 0</LI>
+     *  <LI>RSRQ = 1 << 1</LI>
+     *  <LI>RSSNR = 1 << 2</LI>
+     * </UL>
+     * <p> The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP},
+     * {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
      *
-     *  RSRP = 1 << 0,
-     *  RSRQ = 1 << 1,
-     *  RSSNR = 1 << 2,
-     *
-     *  The value of this key must be bitwise OR of {@link CellSignalStrengthLte#USE_RSRP},
-     *  {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
-     *
-     * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
-     * If the key is invalid or not configured, a default value (RSRP = 1 << 0)
-     * will apply.
+     * <p> For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
+     * If the key is invalid or not configured, a default value (RSRP = 1 << 0) will apply.
      *
      * @hide
      */
@@ -2381,16 +2425,18 @@
 
     /**
      * List of 4 customized 5G SS reference signal received power (SSRSRP) thresholds.
-     *
+     * <p>
      * Reference: 3GPP TS 38.215
-     *
+     * <p>
      * 4 threshold integers must be within the boundaries [-140 dB, -44 dB], and the levels are:
-     *     "NONE: [-140, threshold1]"
-     *     "POOR: (threshold1, threshold2]"
-     *     "MODERATE: (threshold2, threshold3]"
-     *     "GOOD:  (threshold3, threshold4]"
-     *     "EXCELLENT:  (threshold4, -44]"
-     *
+     * <UL>
+     *     <LI>"NONE: [-140, threshold1]"</LI>
+     *     <LI>"POOR: (threshold1, threshold2]"</LI>
+     *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
+     *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
+     *     <LI>"EXCELLENT:  (threshold4, -44]"</LI>
+     * </UL>
+     * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
      * not configured, a default value set will apply.
      */
@@ -2399,16 +2445,18 @@
 
     /**
      * List of 4 customized 5G SS reference signal received quality (SSRSRQ) thresholds.
-     *
+     * <p>
      * Reference: 3GPP TS 38.215
-     *
+     * <p>
      * 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are:
-     *     "NONE: [-20, threshold1]"
-     *     "POOR: (threshold1, threshold2]"
-     *     "MODERATE: (threshold2, threshold3]"
-     *     "GOOD:  (threshold3, threshold4]"
-     *     "EXCELLENT:  (threshold4, -3]"
-     *
+     * <UL>
+     *     <LI>"NONE: [-20, threshold1]"</LI>
+     *     <LI>"POOR: (threshold1, threshold2]"</LI>
+     *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
+     *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
+     *     <LI>"EXCELLENT:  (threshold4, -3]"</LI>
+     * </UL>
+     * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
      * not configured, a default value set will apply.
      */
@@ -2417,17 +2465,19 @@
 
     /**
      * List of 4 customized 5G SS signal-to-noise and interference ratio (SSSINR) thresholds.
-     *
+     * <p>
      * Reference: 3GPP TS 38.215,
      *            3GPP TS 38.133 10.1.16.1
-     *
+     * <p>
      * 4 threshold integers must be within the boundaries [-23 dB, 40 dB], and the levels are:
-     *     "NONE: [-23, threshold1]"
-     *     "POOR: (threshold1, threshold2]"
-     *     "MODERATE: (threshold2, threshold3]"
-     *     "GOOD:  (threshold3, threshold4]"
-     *     "EXCELLENT:  (threshold4, 40]"
-     *
+     * <UL>
+     *     <LI>"NONE: [-23, threshold1]"</LI>
+     *     <LI>"POOR: (threshold1, threshold2]"</LI>
+     *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
+     *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
+     *     <LI>"EXCELLENT:  (threshold4, 40]"</LI>
+     * </UL>
+     * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
      * not configured, a default value set will apply.
      */
@@ -2442,19 +2492,19 @@
      * <p> If a measure is not set, signal criteria reporting from modem will not be triggered and
      * not be used for calculating signal level. If multiple measures are set bit, the parameter
      * whose value is smallest is used to indicate the signal level.
-     *
-     *  SSRSRP = 1 << 0,
-     *  SSRSRQ = 1 << 1,
-     *  SSSINR = 1 << 2,
-     *
+     * <UL>
+     *  <LI>SSRSRP = 1 << 0</LI>
+     *  <LI>SSRSRQ = 1 << 1</LI>
+     *  <LI>SSSINR = 1 << 2</LI>
+     * </UL>
      *  The value of this key must be bitwise OR of {@link CellSignalStrengthNr#USE_SSRSRP},
      *  {@link CellSignalStrengthNr#USE_SSRSRQ}, {@link CellSignalStrengthNr#USE_SSSINR}.
      *
-     * For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2).
+     * <p> For example, if both SSRSRP and SSSINR are used, the value of key is 5 (1 << 0 | 1 << 2).
      * If the key is invalid or not configured, a default value (SSRSRP = 1 << 0) will apply.
      *
-     *  Reference: 3GPP TS 38.215,
-     *             3GPP TS 38.133 10.1.16.1
+     * <p> Reference: 3GPP TS 38.215,
+     *                3GPP TS 38.133 10.1.16.1
      *
      * @hide
      */
@@ -2462,6 +2512,16 @@
             "parameters_use_for_5g_nr_signal_bar_int";
 
     /**
+     * There are two signal strengths, NR and LTE signal strength, during NR (non-standalone).
+     * Boolean indicating whether to use LTE signal strength as primary during NR (non-standalone).
+     * By default this value is true.
+     *
+     * @hide
+     */
+    public static final String KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL =
+            "signal_strength_nr_nsa_use_lte_as_primary_bool";
+
+    /**
      * String array of default bandwidth values per network type.
      * The entries should be of form "network_name:downstream,upstream", with values in Kbps.
      * @hide
@@ -2960,28 +3020,106 @@
      * UE wants to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise not
      * define.
      * The configuration is: "connected_mmwave:5G_Plus,connected:5G"
+     * @hide
      */
-    public static final String KEY_5G_ICON_CONFIGURATION_STRING =
-            "5g_icon_configuration_string";
+    public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string";
 
     /**
-     * Timeout in seconds for displaying 5G icon, default value is 0 which means the timer is
-     * disabled.
+     * This configuration allows the system UI to determine how long to continue to display 5G icons
+     * when the device switches between different 5G scenarios.
      *
-     * System UI will show the 5G icon and start a timer with the timeout from this config when the
-     * device connects to a 5G cell. System UI stops displaying 5G icon when both the device
-     * disconnects from 5G cell and the timer is expired.
+     * There are seven 5G scenarios:
+     * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
+     *    millimeter wave.
+     * 2. connected: device currently connected to 5G cell as the secondary cell but not using
+     *    millimeter wave.
+     * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability (not necessary
+     *    to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
+     *    currently in IDLE state.
+     * 4. not_restricted_rrc_con: device camped on a network that has 5G capability (not necessary
+     *    to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
+     *    currently in CONNECTED state.
+     * 5. restricted: device camped on a network that has 5G capability (not necessary to connect a
+     *    5G cell as a secondary cell) but the use of 5G is restricted.
+     * 6. legacy: device is not camped on a network that has 5G capability
+     * 7. any: any of the above scenarios
      *
-     * If 5G is reacquired during this timer, the timer is canceled and restarted when 5G is next
-     * lost. Allows us to momentarily lose 5G without blinking the icon.
+     * The configured string contains various timer rules separated by a semicolon.
+     * Each rule will have three items: prior 5G scenario, current 5G scenario, and grace period
+     * in seconds before changing the icon. When the 5G state changes from the prior to the current
+     * 5G scenario, the system UI will continue to show the icon for the prior 5G scenario (defined
+     * in {@link #KEY_5G_ICON_CONFIGURATION_STRING}) for the amount of time specified by the grace
+     * period. If the prior 5G scenario is reestablished, the timer will reset and start again if
+     * the UE changes 5G scenarios again. Defined states (5G scenarios #1-5) take precedence over
+     * 'any' (5G scenario #6), and unspecified transitions have a default grace period of 0.
+     * The order of rules in the configuration determines the priority (the first applicable timer
+     * rule will be used).
+     *
+     * Here is an example: "connected_mmwave,connected,30;connected_mmwave,any,10;connected,any,10"
+     * This configuration defines 3 timers:
+     * 1. When UE goes from 'connected_mmwave' to 'connected', system UI will continue to display
+     *    the 5G icon for 'connected_mmwave' for 30 seconds.
+     * 2. When UE goes from 'connected_mmwave' to any other state (except for connected, since
+     *    rule 1 would be used instead), system UI will continue to display the 5G icon for
+     *    'connected_mmwave' for 10 seconds.
+     * 3. When UE goes from 'connected' to any other state, system UI will continue to display the
+     *    5G icon for 'connected' for 10 seconds.
+     *
+     * @hide
      */
-    public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT =
-            "5g_icon_display_grace_period_sec_int";
+    public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING =
+            "5g_icon_display_grace_period_string";
+
+    /**
+     * This configuration extends {@link #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING} to allow the
+     * system UI to continue displaying 5G icons after the initial timer expires.
+     *
+     * There are seven 5G scenarios:
+     * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
+     *    millimeter wave.
+     * 2. connected: device currently connected to 5G cell as the secondary cell but not using
+     *    millimeter wave.
+     * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability (not necessary
+     *    to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
+     *    currently in IDLE state.
+     * 4. not_restricted_rrc_con: device camped on a network that has 5G capability (not necessary
+     *    to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
+     *    currently in CONNECTED state.
+     * 5. restricted: device camped on a network that has 5G capability (not necessary to connect a
+     *    5G cell as a secondary cell) but the use of 5G is restricted.
+     * 6. legacy: device is not camped on a network that has 5G capability
+     * 7. any: any of the above scenarios
+     *
+     * The configured string contains various timer rules separated by a semicolon.
+     * Each rule will have three items: primary 5G scenario, secondary 5G scenario, and
+     * grace period in seconds before changing the icon. When the timer for the primary 5G timer
+     * expires, the system UI will continue to show the icon for the primary 5G scenario (defined
+     * in {@link #KEY_5G_ICON_CONFIGURATION_STRING}) for the amount of time specified by the grace
+     * period. If the primary 5G scenario is reestablished, the timers will reset and the system UI
+     * will continue to display the icon for the primary 5G scenario without interruption. If the
+     * secondary 5G scenario is lost, the timer will reset and the icon will reflect the true state.
+     * Defined states (5G scenarios #1-5) take precedence over 'any' (5G scenario #6), and
+     * unspecified transitions have a default grace period of 0. The order of rules in the
+     * configuration determines the priority (the first applicable timer rule will be used).
+     *
+     * Here is an example: "connected,not_restricted_rrc_idle,30"
+     * This configuration defines a secondary timer that extends the primary 'connected' timer.
+     * When the primary 'connected' timer expires while the UE is in the 'not_restricted_rrc_idle'
+     * 5G state, system UI will continue to display the 5G icon for 'connected' for 30 seconds.
+     * If the 5G state returns to 'connected', the timer will be reset without change to the icon,
+     * and if the 5G state changes to neither 'connected' not 'not_restricted_rrc_idle', the icon
+     * will change to reflect the true state.
+     *
+     * @hide
+     */
+    public static final String KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING =
+            "5g_icon_display_secondary_grace_period_string";
 
     /**
      * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
+     * @hide
      */
-    public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long";
+    public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_ms_long";
 
     /**
      * Whether NR (non-standalone) should be unmetered for all frequencies.
@@ -3006,6 +3144,38 @@
     public static final String KEY_UNMETERED_NR_NSA_SUB6_BOOL = "unmetered_nr_nsa_sub6_bool";
 
     /**
+     * Whether NR (non-standalone) should be unmetered when the device is roaming.
+     * If false, then the values for {@link #KEY_UNMETERED_NR_NSA_BOOL},
+     * {@link #KEY_UNMETERED_NR_NSA_MMWAVE_BOOL}, {@link #KEY_UNMETERED_NR_NSA_SUB6_BOOL},
+     * and unmetered {@link SubscriptionPlan} will be ignored.
+     * @hide
+     */
+    public static final String KEY_UNMETERED_NR_NSA_WHEN_ROAMING_BOOL =
+            "unmetered_nr_nsa_when_roaming_bool";
+
+    /**
+     * Whether NR (standalone) should be unmetered for all frequencies.
+     * If either {@link #KEY_UNMETERED_NR_SA_MMWAVE_BOOL} or
+     * {@link #KEY_UNMETERED_NR_SA_SUB6_BOOL} are true, then this value will be ignored.
+     * @hide
+     */
+    public static final String KEY_UNMETERED_NR_SA_BOOL = "unmetered_nr_sa_bool";
+
+    /**
+     * Whether NR (standalone) frequencies above 6GHz (millimeter wave) should be unmetered.
+     * If this is true, then the value for {@link #KEY_UNMETERED_NR_SA_BOOL} will be ignored.
+     * @hide
+     */
+    public static final String KEY_UNMETERED_NR_SA_MMWAVE_BOOL = "unmetered_nr_sa_mmwave_bool";
+
+    /**
+     * Whether NR (standalone) frequencies below 6GHz (sub6) should be unmetered.
+     * If this is true, then the value for {@link #KEY_UNMETERED_NR_SA_BOOL} will be ignored.
+     * @hide
+     */
+    public static final String KEY_UNMETERED_NR_SA_SUB6_BOOL = "unmetered_nr_sa_sub6_bool";
+
+    /**
      * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable
      * this feature.
      * @hide
@@ -3535,6 +3705,17 @@
     public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY =
             "missed_incoming_call_sms_originator_string_array";
 
+
+    /**
+     * String array of Apn Type configurations.
+     * The entries should be of form "APN_TYPE_NAME:priority".
+     * priority is an integer that is sorted from highest to lowest.
+     * example: cbs:5
+     *
+     * @hide
+     */
+    public static final String KEY_APN_PRIORITY_STRING_ARRAY = "apn_priority_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
@@ -3716,14 +3897,16 @@
         sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
         sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
         sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
-        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
+        sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, true);
         sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
         sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false);
         sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5);
@@ -3925,10 +4108,10 @@
                 });
         sDefaults.putIntArray(KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
                 new int[] {
-                        -19, /* SIGNAL_STRENGTH_POOR */
+                        -20, /* SIGNAL_STRENGTH_POOR */
                         -17, /* SIGNAL_STRENGTH_MODERATE */
                         -14, /* SIGNAL_STRENGTH_GOOD */
-                        -12  /* SIGNAL_STRENGTH_GREAT */
+                        -11  /* SIGNAL_STRENGTH_GREAT */
                 });
         sDefaults.putIntArray(KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
                 new int[] {
@@ -3956,9 +4139,9 @@
                 // Boundaries: [-20 dB, -3 dB]
                 new int[] {
                     -16, /* SIGNAL_STRENGTH_POOR */
-                    -11, /* SIGNAL_STRENGTH_MODERATE */
+                    -12, /* SIGNAL_STRENGTH_MODERATE */
                     -9, /* SIGNAL_STRENGTH_GOOD */
-                    -7  /* SIGNAL_STRENGTH_GREAT */
+                    -6  /* SIGNAL_STRENGTH_GREAT */
                 });
         sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
                 // Boundaries: [-23 dB, 40 dB]
@@ -3970,10 +4153,11 @@
                 });
         sDefaults.putInt(KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
                 CellSignalStrengthNr.USE_SSRSRP);
+        sDefaults.putBoolean(KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL, true);
         sDefaults.putStringArray(KEY_BANDWIDTH_STRING_ARRAY, new String[]{
                 "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA-IS95A:14,14", "CDMA-IS95B:14,14",
                 "1xRTT:30,30", "EvDo-rev.0:750,48", "EvDo-rev.A:950,550", "HSDPA:4300,620",
-                "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo-rev.B:1500,550:", "eHRPD:750,48",
+                "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo-rev.B:1500,550", "eHRPD:750,48",
                 "HSPAP:13000,3400", "TD-SCDMA:115,115", "LTE:30000,15000", "NR_NSA:47000,15000",
                 "NR_NSA_MMWAVE:145000,15000", "NR_SA:145000,15000"});
         sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL, false);
@@ -3990,13 +4174,19 @@
         sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false);
         sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
         sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
-                "connected_mmwave:5G,connected:5G");
-        sDefaults.putInt(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT, 0);
+                "connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G,"
+                        + "not_restricted_rrc_con:5G");
+        sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
+        sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
         /* Default value is 1 hour. */
         sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
         sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
         sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
         sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false);
+        sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_WHEN_ROAMING_BOOL, false);
+        sDefaults.putBoolean(KEY_UNMETERED_NR_SA_BOOL, false);
+        sDefaults.putBoolean(KEY_UNMETERED_NR_SA_MMWAVE_BOOL, false);
+        sDefaults.putBoolean(KEY_UNMETERED_NR_SA_SUB6_BOOL, false);
         sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_WIFI_CALLING_ICON_IN_STATUS_BAR_BOOL, false);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
@@ -4054,9 +4244,13 @@
         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.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1));
         sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
                 new String[0]);
+        sDefaults.putStringArray(KEY_APN_PRIORITY_STRING_ARRAY, new String[] {
+                "default:0", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2",
+                "ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3"
+        });
         sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
     }
 
diff --git a/telephony/java/android/telephony/CbGeoUtils.java b/telephony/java/android/telephony/CbGeoUtils.java
index c0ae99e..806bac0 100644
--- a/telephony/java/android/telephony/CbGeoUtils.java
+++ b/telephony/java/android/telephony/CbGeoUtils.java
@@ -128,6 +128,23 @@
         public String toString() {
             return "(" + lat + "," + lng + ")";
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+
+            if (!(o instanceof LatLng)) {
+                return false;
+            }
+
+            LatLng l = (LatLng) o;
+            return lat == l.lat && lng == l.lng;
+        }
     }
 
     /**
@@ -280,6 +297,32 @@
             }
             return str;
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+
+            if (!(o instanceof Polygon)) {
+                return false;
+            }
+
+            Polygon p = (Polygon) o;
+            if (mVertices.size() != p.mVertices.size()) {
+                return false;
+            }
+            for (int i = 0; i < mVertices.size(); i++) {
+                if (!mVertices.get(i).equals(p.mVertices.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
     }
 
     /**
@@ -335,6 +378,24 @@
 
             return str;
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+
+            if (!(o instanceof Circle)) {
+                return false;
+            }
+
+            Circle c = (Circle) o;
+            return mCenter.equals(c.mCenter)
+                    && Double.compare(mRadiusMeter, c.mRadiusMeter) == 0;
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 0c2f1f3..1e5ce05 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -45,8 +45,10 @@
      */
     public static final int MCC_LENGTH = 3;
 
-    private static final int MNC_MIN_LENGTH = 2;
-    private static final int MNC_MAX_LENGTH = 3;
+    /** @hide */
+    public static final int MNC_MIN_LENGTH = 2;
+    /** @hide */
+    public static final int MNC_MAX_LENGTH = 3;
 
     // Log tag
     /** @hide */
@@ -68,6 +70,12 @@
     /** @hide */
     protected String mAlphaShort;
 
+    // For GSM, WCDMA, TDSCDMA, LTE and NR, Cell Global ID is defined in 3GPP TS 23.003.
+    // For CDMA, its defined as System Id + Network Id + Basestation Id.
+    /** @hide */
+    protected String mGlobalCellId;
+
+
     /** @hide */
     protected CellIdentity(@Nullable String tag, int type, @Nullable String mcc,
             @Nullable String mnc, @Nullable String alphal, @Nullable String alphas) {
@@ -182,6 +190,36 @@
     }
 
     /**
+     * @return Global Cell ID
+     * @hide
+     */
+    @Nullable
+    public String getGlobalCellId() {
+        return mGlobalCellId;
+    }
+
+    /**
+     * @param ci a CellIdentity to compare to the current CellIdentity.
+     * @return true if ci has the same technology and Global Cell ID; false, otherwise.
+     * @hide
+     */
+    public boolean isSameCell(@Nullable CellIdentity ci) {
+        if (ci == null) return false;
+        if (this.getClass() != ci.getClass()) return false;
+        if (this.getGlobalCellId() == null || ci.getGlobalCellId() == null) return false;
+        return TextUtils.equals(this.getGlobalCellId(), ci.getGlobalCellId());
+    }
+
+    /** @hide */
+    public @Nullable String getPlmn() {
+        if (mMccStr == null || mMncStr == null) return null;
+        return mMccStr + mMncStr;
+    }
+
+    /** @hide */
+    protected abstract void updateGlobalCellId();
+
+    /**
      * @return a CellLocation object for this CellIdentity
      * @hide
      */
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index e220b07..68c833c 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -75,6 +75,7 @@
         mBasestationId = CellInfo.UNAVAILABLE;
         mLongitude = CellInfo.UNAVAILABLE;
         mLatitude = CellInfo.UNAVAILABLE;
+        mGlobalCellId = null;
     }
 
     /**
@@ -106,6 +107,7 @@
         } else {
             mLongitude = mLatitude = CellInfo.UNAVAILABLE;
         }
+        updateGlobalCellId();
     }
 
     /** @hide */
@@ -136,6 +138,16 @@
                 mAlphaLong, mAlphaShort);
     }
 
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        if (mNetworkId == CellInfo.UNAVAILABLE || mSystemId == CellInfo.UNAVAILABLE
+                || mBasestationId == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = String.format("%04x%04x%04x", mSystemId, mNetworkId,  mBasestationId);
+    }
+
     /**
      * Take the latitude and longitude in 1/4 seconds and see if
      * the reported location is on Null Island.
@@ -267,6 +279,7 @@
         mLongitude = in.readInt();
         mLatitude = in.readInt();
 
+        updateGlobalCellId();
         if (DBG) log(toString());
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 9f2537c..849c613 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -22,11 +22,12 @@
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * CellIdentity to represent a unique GSM cell
@@ -50,7 +51,7 @@
     private final int mBsic;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     /**
      * @hide
@@ -62,7 +63,8 @@
         mCid = CellInfo.UNAVAILABLE;
         mArfcn = CellInfo.UNAVAILABLE;
         mBsic = CellInfo.UNAVAILABLE;
-        mAdditionalPlmns = Collections.emptyList();
+        mAdditionalPlmns = new ArraySet<>();
+        mGlobalCellId = null;
     }
 
     /**
@@ -81,25 +83,26 @@
      */
     public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, @Nullable String mccStr,
             @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
-            @NonNull List<String> additionalPlmns) {
+            @NonNull Collection<String> additionalPlmns) {
         super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas);
         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
         mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN);
         mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
             }
         }
+        updateGlobalCellId();
     }
 
     /** @hide */
     public CellIdentityGsm(@NonNull android.hardware.radio.V1_0.CellIdentityGsm cid) {
         this(cid.lac, cid.cid, cid.arfcn,
                 cid.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.bsic,
-                cid.mcc, cid.mnc, "", "", Collections.emptyList());
+                cid.mcc, cid.mnc, "", "", new ArraySet<>());
     }
 
     /** @hide */
@@ -107,7 +110,7 @@
         this(cid.base.lac, cid.base.cid, cid.base.arfcn,
                 cid.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.base.bsic, cid.base.mcc,
                 cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
-                Collections.emptyList());
+                new ArraySet<>());
     }
 
     /** @hide */
@@ -135,6 +138,18 @@
                 CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns);
     }
 
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = plmn + String.format("%04x%04x", mLac, mCid);
+    }
+
     /**
      * @return 3-digit Mobile Country Code, 0..999,
      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
@@ -221,8 +236,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return mAdditionalPlmns;
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     /**
@@ -296,7 +311,7 @@
         dest.writeInt(mCid);
         dest.writeInt(mArfcn);
         dest.writeInt(mBsic);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -306,8 +321,9 @@
         mCid = in.readInt();
         mArfcn = in.readInt();
         mBsic = in.readInt();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
 
+        updateGlobalCellId();
         if (DBG) log(toString());
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index a194ae3..1993550 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -23,11 +23,13 @@
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * CellIdentity is to represent a unique LTE cell
@@ -52,9 +54,11 @@
     private final int mEarfcn;
     // cell bandwidth, in kHz
     private final int mBandwidth;
+    // cell bands
+    private final int[] mBands;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     private ClosedSubscriberGroupInfo mCsgInfo;
 
@@ -68,9 +72,11 @@
         mPci = CellInfo.UNAVAILABLE;
         mTac = CellInfo.UNAVAILABLE;
         mEarfcn = CellInfo.UNAVAILABLE;
+        mBands = new int[] {};
         mBandwidth = CellInfo.UNAVAILABLE;
-        mAdditionalPlmns = Collections.emptyList();
+        mAdditionalPlmns = new ArraySet<>();
         mCsgInfo = null;
+        mGlobalCellId = null;
     }
 
     /**
@@ -85,8 +91,9 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
-        this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
-                String.valueOf(mnc), null, null, Collections.emptyList(), null);
+        this(ci, pci, tac, CellInfo.UNAVAILABLE, new int[] {}, CellInfo.UNAVAILABLE,
+                String.valueOf(mcc), String.valueOf(mnc), null, null, new ArraySet<>(),
+                null);
     }
 
     /**
@@ -105,49 +112,55 @@
      *
      * @hide
      */
-    public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth,
-            @Nullable String mccStr, @Nullable String mncStr, @Nullable String alphal,
-            @Nullable String alphas, @NonNull List<String> additionalPlmns,
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, @NonNull int[] bands,
+            int bandwidth, @Nullable String mccStr, @Nullable String mncStr,
+            @Nullable String alphal, @Nullable String alphas,
+            @NonNull Collection<String> additionalPlmns,
             @Nullable ClosedSubscriberGroupInfo csgInfo) {
         super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas);
         mCi = inRangeOrUnavailable(ci, 0, MAX_CI);
         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
         mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
+        mBands = bands;
         mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
             }
         }
         mCsgInfo = csgInfo;
+        updateGlobalCellId();
     }
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_0.CellIdentityLte cid) {
-        this(cid.ci, cid.pci, cid.tac, cid.earfcn,
-                CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", Collections.emptyList(), null);
+        this(cid.ci, cid.pci, cid.tac, cid.earfcn, new int[] {},
+                CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", new ArraySet<>(), null);
     }
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_2.CellIdentityLte cid) {
-        this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth,
-                cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
-                cid.operatorNames.alphaShort, Collections.emptyList(), null);
+        this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, new int[] {},
+                cid.bandwidth, cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
+                cid.operatorNames.alphaShort, new ArraySet<>(), null);
     }
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_5.CellIdentityLte cid) {
         this(cid.base.base.ci, cid.base.base.pci, cid.base.base.tac, cid.base.base.earfcn,
-                cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc,
-                cid.base.operatorNames.alphaLong, cid.base.operatorNames.alphaShort,
-                cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null
-                        ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
+                cid.bands.stream().mapToInt(Integer::intValue).toArray(), cid.base.bandwidth,
+                cid.base.base.mcc, cid.base.base.mnc, cid.base.operatorNames.alphaLong,
+                cid.base.operatorNames.alphaShort, cid.additionalPlmns,
+                cid.optionalCsgInfo.getDiscriminator()
+                        == android.hardware.radio.V1_5.OptionalCsgInfo.hidl_discriminator.csgInfo
+                                ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo())
+                                        : null);
     }
 
     private CellIdentityLte(@NonNull CellIdentityLte cid) {
-        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
+        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBands, cid.mBandwidth, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
     }
 
@@ -155,7 +168,7 @@
     @Override
     public @NonNull CellIdentityLte sanitizeLocationInfo() {
         return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
-                CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                CellInfo.UNAVAILABLE, mBands, CellInfo.UNAVAILABLE,
                 mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
     }
 
@@ -163,6 +176,18 @@
         return new CellIdentityLte(this);
     }
 
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mCi == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = plmn + String.format("%07x", mCi);
+    }
+
     /**
      * @return 3-digit Mobile Country Code, 0..999,
      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
@@ -220,12 +245,11 @@
      *
      * Reference: 3GPP TS 36.101 section 5.5
      *
-     * @return List of band number or empty list if not available.
+     * @return Array of band number or empty array if not available.
      */
     @NonNull
-    public List<Integer> getBands() {
-        // Todo: Add actual support
-        return Collections.emptyList();
+    public int[] getBands() {
+        return Arrays.copyOf(mBands, mBands.length);
     }
 
     /**
@@ -270,8 +294,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return mAdditionalPlmns;
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     /**
@@ -307,8 +331,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mCi, mPci, mTac,
-                mAdditionalPlmns.hashCode(), mCsgInfo, super.hashCode());
+        return Objects.hash(mCi, mPci, mTac, mEarfcn, Arrays.hashCode(mBands),
+                mBandwidth, mAdditionalPlmns.hashCode(), mCsgInfo, super.hashCode());
     }
 
     @Override
@@ -326,6 +350,7 @@
                 && mPci == o.mPci
                 && mTac == o.mTac
                 && mEarfcn == o.mEarfcn
+                && Arrays.equals(mBands, o.mBands)
                 && mBandwidth == o.mBandwidth
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
@@ -341,6 +366,7 @@
         .append(" mPci=").append(mPci)
         .append(" mTac=").append(mTac)
         .append(" mEarfcn=").append(mEarfcn)
+        .append(" mBands=").append(mBands)
         .append(" mBandwidth=").append(mBandwidth)
         .append(" mMcc=").append(mMccStr)
         .append(" mMnc=").append(mMncStr)
@@ -360,8 +386,9 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
+        dest.writeIntArray(mBands);
         dest.writeInt(mBandwidth);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
         dest.writeParcelable(mCsgInfo, flags);
     }
 
@@ -372,9 +399,12 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mEarfcn = in.readInt();
+        mBands = in.createIntArray();
         mBandwidth = in.readInt();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
         mCsgInfo = in.readParcelable(null);
+
+        updateGlobalCellId();
         if (DBG) log(toString());
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index a0ef5aa..8dd7bdd 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -22,11 +22,13 @@
 import android.os.Parcel;
 import android.telephony.AccessNetworkConstants.NgranBands.NgranBand;
 import android.telephony.gsm.GsmCellLocation;
+import android.util.ArraySet;
 
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Information to represent a unique NR(New Radio 5G) cell.
@@ -43,10 +45,10 @@
     private final int mPci;
     private final int mTac;
     private final long mNci;
-    private final List<Integer> mBands;
+    private final int[] mBands;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     /**
      *
@@ -63,35 +65,38 @@
      *
      * @hide
      */
-    public CellIdentityNr(int pci, int tac, int nrArfcn, @NgranBand List<Integer> bands,
+    public CellIdentityNr(int pci, int tac, int nrArfcn, @NonNull @NgranBand int[] bands,
                           @Nullable String mccStr, @Nullable String mncStr, long nci,
                           @Nullable String alphal, @Nullable String alphas,
-                          @NonNull List<String> additionalPlmns) {
+                          @NonNull Collection<String> additionalPlmns) {
         super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
         mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN);
-        mBands = new ArrayList<>(bands);
+        // TODO: input validation for bands
+        mBands = bands;
         mNci = inRangeOrUnavailable(nci, 0, MAX_NCI);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
             }
         }
+        updateGlobalCellId();
     }
 
     /** @hide */
     public CellIdentityNr(@NonNull android.hardware.radio.V1_4.CellIdentityNr cid) {
-        this(cid.pci, cid.tac, cid.nrarfcn, Collections.emptyList(), cid.mcc, cid.mnc, cid.nci,
+        this(cid.pci, cid.tac, cid.nrarfcn, new int[] {}, cid.mcc, cid.mnc, cid.nci,
                 cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
-                Collections.emptyList());
+                new ArraySet<>());
     }
 
     /** @hide */
     public CellIdentityNr(@NonNull android.hardware.radio.V1_5.CellIdentityNr cid) {
-        this(cid.base.pci, cid.base.tac, cid.base.nrarfcn, cid.bands, cid.base.mcc, cid.base.mnc,
-                cid.base.nci, cid.base.operatorNames.alphaLong,
+        this(cid.base.pci, cid.base.tac, cid.base.nrarfcn,
+                cid.bands.stream().mapToInt(Integer::intValue).toArray(), cid.base.mcc,
+                cid.base.mnc, cid.base.nci, cid.base.operatorNames.alphaLong,
                 cid.base.operatorNames.alphaShort, cid.additionalPlmns);
     }
 
@@ -103,6 +108,17 @@
                 mAdditionalPlmns);
     }
 
+    /** @hide */
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mNci == CellInfo.UNAVAILABLE_LONG) return;
+
+        mGlobalCellId = plmn + String.format("%09x", mNci);
+    }
+
     /**
      * @return a CellLocation object for this CellIdentity.
      * @hide
@@ -116,18 +132,22 @@
     @Override
     public int hashCode() {
         return Objects.hash(super.hashCode(), mPci, mTac,
-                mNrArfcn, mBands.hashCode(), mNci, mAdditionalPlmns.hashCode());
+                mNrArfcn, Arrays.hashCode(mBands), mNci, mAdditionalPlmns.hashCode());
     }
 
     @Override
     public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+
         if (!(other instanceof CellIdentityNr)) {
             return false;
         }
 
         CellIdentityNr o = (CellIdentityNr) other;
         return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn
-                && mBands.equals(o.mBands) && mNci == o.mNci
+                && Arrays.equals(mBands, o.mBands) && mNci == o.mNci
                 && mAdditionalPlmns.equals(o.mAdditionalPlmns);
     }
 
@@ -160,12 +180,12 @@
      * Reference: TS 38.101-1 table 5.2-1
      * Reference: TS 38.101-2 table 5.2-1
      *
-     * @return List of band number or empty list if not available.
+     * @return Array of band number or empty array if not available.
      */
     @NgranBand
     @NonNull
-    public List<Integer> getBands() {
-        return Collections.unmodifiableList(mBands);
+    public int[] getBands() {
+        return Arrays.copyOf(mBands, mBands.length);
     }
 
     /**
@@ -212,8 +232,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return Collections.unmodifiableList(mAdditionalPlmns);
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     @Override
@@ -239,9 +259,9 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mNrArfcn);
-        dest.writeList(mBands);
+        dest.writeIntArray(mBands);
         dest.writeLong(mNci);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -250,9 +270,11 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mNrArfcn = in.readInt();
-        mBands = in.readArrayList(null);
+        mBands = in.createIntArray();
         mNci = in.readLong();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
+
+        updateGlobalCellId();
     }
 
     /** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 531487a..e74b709 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -20,11 +20,12 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
+import android.util.ArraySet;
 
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * CellIdentity is to represent a unique TD-SCDMA cell
@@ -50,7 +51,7 @@
     private final int mUarfcn;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     private ClosedSubscriberGroupInfo mCsgInfo;
 
@@ -63,8 +64,9 @@
         mCid = CellInfo.UNAVAILABLE;
         mCpid = CellInfo.UNAVAILABLE;
         mUarfcn = CellInfo.UNAVAILABLE;
-        mAdditionalPlmns = Collections.emptyList();
+        mAdditionalPlmns = new ArraySet<>();
         mCsgInfo = null;
+        mGlobalCellId = null;
     }
 
     /**
@@ -85,19 +87,21 @@
      */
     public CellIdentityTdscdma(@Nullable String mcc, @Nullable String mnc, int lac, int cid,
             int cpid, int uarfcn, @Nullable String alphal, @Nullable String alphas,
-            @NonNull List<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo) {
+            @NonNull Collection<String> additionalPlmns,
+            @Nullable ClosedSubscriberGroupInfo csgInfo) {
         super(TAG, CellInfo.TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
         mCpid = inRangeOrUnavailable(cpid, 0, MAX_CPID);
         mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
             }
         }
         mCsgInfo = csgInfo;
+        updateGlobalCellId();
     }
 
     private CellIdentityTdscdma(@NonNull CellIdentityTdscdma cid) {
@@ -124,8 +128,11 @@
         this(cid.base.base.mcc, cid.base.base.mnc, cid.base.base.lac, cid.base.base.cid,
                 cid.base.base.cpid, cid.base.uarfcn, cid.base.operatorNames.alphaLong,
                 cid.base.operatorNames.alphaShort,
-                cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null
-                        ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
+                cid.additionalPlmns,
+                cid.optionalCsgInfo.getDiscriminator()
+                        == android.hardware.radio.V1_5.OptionalCsgInfo.hidl_discriminator.csgInfo
+                                ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo())
+                                        : null);
     }
 
     /** @hide */
@@ -140,6 +147,18 @@
         return new CellIdentityTdscdma(this);
     }
 
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = plmn + String.format("%04x%04x", mLac, mCid);
+    }
+
     /**
      * Get Mobile Country Code in string format
      * @return Mobile Country Code in string format, null if unknown
@@ -208,8 +227,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return mAdditionalPlmns;
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     /**
@@ -289,7 +308,7 @@
         dest.writeInt(mCid);
         dest.writeInt(mCpid);
         dest.writeInt(mUarfcn);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
         dest.writeParcelable(mCsgInfo, flags);
     }
 
@@ -300,8 +319,10 @@
         mCid = in.readInt();
         mCpid = in.readInt();
         mUarfcn = in.readInt();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
         mCsgInfo = in.readParcelable(null);
+
+        updateGlobalCellId();
         if (DBG) log(toString());
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 15e491b..40cb27e 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -22,11 +22,12 @@
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * CellIdentity to represent a unique UMTS cell
@@ -51,7 +52,7 @@
     private final int mUarfcn;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     @Nullable
     private final ClosedSubscriberGroupInfo mCsgInfo;
@@ -65,8 +66,9 @@
         mCid = CellInfo.UNAVAILABLE;
         mPsc = CellInfo.UNAVAILABLE;
         mUarfcn = CellInfo.UNAVAILABLE;
-        mAdditionalPlmns = Collections.emptyList();
+        mAdditionalPlmns = new ArraySet<>();
         mCsgInfo = null;
+        mGlobalCellId = null;
     }
 
     /**
@@ -86,32 +88,34 @@
      */
     public CellIdentityWcdma(int lac, int cid, int psc, int uarfcn, @Nullable String mccStr,
             @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
-            @NonNull List<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo) {
+            @NonNull Collection<String> additionalPlmns,
+            @Nullable ClosedSubscriberGroupInfo csgInfo) {
         super(TAG, CellInfo.TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
         mPsc = inRangeOrUnavailable(psc, 0, MAX_PSC);
         mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
             }
         }
         mCsgInfo = csgInfo;
+        updateGlobalCellId();
     }
 
     /** @hide */
     public CellIdentityWcdma(@NonNull android.hardware.radio.V1_0.CellIdentityWcdma cid) {
         this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", "",
-                Collections.emptyList(), null);
+                new ArraySet<>(), null);
     }
 
     /** @hide */
     public CellIdentityWcdma(@NonNull android.hardware.radio.V1_2.CellIdentityWcdma cid) {
         this(cid.base.lac, cid.base.cid, cid.base.psc, cid.base.uarfcn,
                 cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
-                cid.operatorNames.alphaShort, Collections.emptyList(), null);
+                cid.operatorNames.alphaShort, new ArraySet<>(), null);
     }
 
     /** @hide */
@@ -119,8 +123,10 @@
         this(cid.base.base.lac, cid.base.base.cid, cid.base.base.psc, cid.base.base.uarfcn,
                 cid.base.base.mcc, cid.base.base.mnc, cid.base.operatorNames.alphaLong,
                 cid.base.operatorNames.alphaShort, cid.additionalPlmns,
-                cid.optionalCsgInfo.csgInfo() != null
-                        ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
+                cid.optionalCsgInfo.getDiscriminator()
+                        == android.hardware.radio.V1_5.OptionalCsgInfo.hidl_discriminator.csgInfo
+                                ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo())
+                                        : null);
     }
 
     private CellIdentityWcdma(@NonNull CellIdentityWcdma cid) {
@@ -140,6 +146,18 @@
         return new CellIdentityWcdma(this);
     }
 
+    /** @hide */
+    @Override
+    protected void updateGlobalCellId() {
+        mGlobalCellId = null;
+        String plmn = getPlmn();
+        if (plmn == null) return;
+
+        if (mLac == CellInfo.UNAVAILABLE || mCid == CellInfo.UNAVAILABLE) return;
+
+        mGlobalCellId = plmn + String.format("%04x%04x", mLac, mCid);
+    }
+
     /**
      * @return 3-digit Mobile Country Code, 0..999,
      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
@@ -232,8 +250,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return mAdditionalPlmns;
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     /**
@@ -305,7 +323,7 @@
         dest.writeInt(mCid);
         dest.writeInt(mPsc);
         dest.writeInt(mUarfcn);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
         dest.writeParcelable(mCsgInfo, flags);
     }
 
@@ -316,8 +334,10 @@
         mCid = in.readInt();
         mPsc = in.readInt();
         mUarfcn = in.readInt();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
         mCsgInfo = in.readParcelable(null);
+
+        updateGlobalCellId();
         if (DBG) log(toString());
     }
 
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index bfa209b..b381cce 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.radio.V1_4.CellInfo.Info;
+import android.hardware.radio.V1_5.CellInfo.CellInfoRatSpecificInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,6 +29,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * Immutable cell information from a point in time.
@@ -152,7 +154,7 @@
     protected CellInfo() {
         this.mRegistered = false;
         this.mTimeStamp = Long.MAX_VALUE;
-        mCellConnectionStatus = CONNECTION_NONE;
+        this.mCellConnectionStatus = CONNECTION_NONE;
     }
 
     /** @hide */
@@ -240,27 +242,17 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return ((mRegistered ? 0 : 1) * primeNum) + ((int)(mTimeStamp / 1000) * primeNum)
-                + (mCellConnectionStatus * primeNum);
+        return Objects.hash(mCellConnectionStatus, mRegistered, mTimeStamp);
     }
 
     @Override
-    public boolean equals(Object other) {
-        if (other == null) {
-            return false;
-        }
-        if (this == other) {
-            return true;
-        }
-        try {
-            CellInfo o = (CellInfo) other;
-            return mRegistered == o.mRegistered
-                    && mTimeStamp == o.mTimeStamp
-                    && mCellConnectionStatus == o.mCellConnectionStatus;
-        } catch (ClassCastException e) {
-            return false;
-        }
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CellInfo)) return false;
+        CellInfo cellInfo = (CellInfo) o;
+        return mCellConnectionStatus == cellInfo.mCellConnectionStatus
+                && mRegistered == cellInfo.mRegistered
+                && mTimeStamp == cellInfo.mTimeStamp;
     }
 
     @Override
@@ -353,6 +345,13 @@
     }
 
     /** @hide */
+    protected CellInfo(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) {
+        this.mRegistered = ci.registered;
+        this.mTimeStamp = timeStamp;
+        this.mCellConnectionStatus = ci.connectionStatus;
+    }
+
+    /** @hide */
     public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) {
         if (ci == null) return null;
         switch(ci.cellInfoType) {
@@ -391,4 +390,24 @@
             default: return null;
         }
     }
+
+    /** @hide */
+    public static CellInfo create(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) {
+        if (ci == null) return null;
+        switch (ci.ratSpecificInfo.getDiscriminator()) {
+            case CellInfoRatSpecificInfo.hidl_discriminator.gsm:
+                return new CellInfoGsm(ci, timeStamp);
+            case CellInfoRatSpecificInfo.hidl_discriminator.cdma:
+                return new CellInfoCdma(ci, timeStamp);
+            case CellInfoRatSpecificInfo.hidl_discriminator.lte:
+                return new CellInfoLte(ci, timeStamp);
+            case CellInfoRatSpecificInfo.hidl_discriminator.wcdma:
+                return new CellInfoWcdma(ci, timeStamp);
+            case CellInfoRatSpecificInfo.hidl_discriminator.tdscdma:
+                return new CellInfoTdscdma(ci, timeStamp);
+            case CellInfoRatSpecificInfo.hidl_discriminator.nr:
+                return new CellInfoNr(ci, timeStamp);
+            default: return null;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 0edb4a4..1bef681 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -78,6 +78,15 @@
                 new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
     }
 
+    /** @hide */
+    public CellInfoCdma(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) {
+        super(ci, timeStamp);
+        final android.hardware.radio.V1_2.CellInfoCdma cic = ci.ratSpecificInfo.cdma();
+        mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma);
+        mCellSignalStrengthCdma =
+                new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
+    }
+
     /**
      * @return a {@link CellIdentityCdma} instance.
      */
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index 2dddd3f..c19521f 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -73,6 +73,14 @@
         mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
     }
 
+    /** @hide */
+    public CellInfoGsm(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) {
+        super(ci, timeStamp);
+        final android.hardware.radio.V1_5.CellInfoGsm cig = ci.ratSpecificInfo.gsm();
+        mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm);
+        mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
+    }
+
     /**
      * @return a {@link CellIdentityGsm} instance.
      */
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index a57c7cd..320925e 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -82,6 +82,15 @@
         mCellConfig = new CellConfigLte(cil.cellConfig);
     }
 
+    /** @hide */
+    public CellInfoLte(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) {
+        super(ci, timeStamp);
+        final android.hardware.radio.V1_5.CellInfoLte cil = ci.ratSpecificInfo.lte();
+        mCellIdentityLte = new CellIdentityLte(cil.cellIdentityLte);
+        mCellSignalStrengthLte = new CellSignalStrengthLte(cil.signalStrengthLte);
+        mCellConfig = new CellConfigLte();
+    }
+
     /**
      * @return a {@link CellIdentityLte} instance.
      */
diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java
index 8b41b8b..a7e79f9 100644
--- a/telephony/java/android/telephony/CellInfoNr.java
+++ b/telephony/java/android/telephony/CellInfoNr.java
@@ -53,6 +53,14 @@
         mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrength);
     }
 
+    /** @hide */
+    public CellInfoNr(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) {
+        super(ci, timeStamp);
+        final android.hardware.radio.V1_5.CellInfoNr cil = ci.ratSpecificInfo.nr();
+        mCellIdentity = new CellIdentityNr(cil.cellIdentityNr);
+        mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrengthNr);
+    }
+
     /**
      * @return a {@link CellIdentityNr} instance.
      */
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index d2cc9c6c..038c49a 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -77,6 +77,14 @@
         mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
     }
 
+    /** @hide */
+    public CellInfoTdscdma(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) {
+        super(ci, timeStamp);
+        final android.hardware.radio.V1_5.CellInfoTdscdma cit = ci.ratSpecificInfo.tdscdma();
+        mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma);
+        mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
+    }
+
     /**
      * @return a {@link CellIdentityTdscdma} instance.
      */
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index 3f792d1..c74955f 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -72,6 +72,14 @@
         mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
     }
 
+    /** @hide */
+    public CellInfoWcdma(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) {
+        super(ci, timeStamp);
+        final android.hardware.radio.V1_5.CellInfoWcdma ciw = ci.ratSpecificInfo.wcdma();
+        mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma);
+        mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
+    }
+
     /**
      * @return a {@link CellIdentityWcdma} instance.
      */
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 1c92705b..d00049c 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -314,6 +314,8 @@
 
     /**
      * Get the signal strength as dBm
+     *
+     * @return min(CDMA RSSI, EVDO RSSI) of the measured cell.
      */
     @Override
     public int getDbm() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 76d2df9..9d55f10 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -145,6 +145,8 @@
 
     /**
      * Get the signal strength as dBm.
+     *
+     * @return the RSSI of the measured cell.
      */
     @Override
     public int getDbm() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 8562df1..95fe90a 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -57,9 +57,9 @@
     // Boundaries: [-20 dB, -3 dB]
     private int[] mSsRsrqThresholds = new int[] {
             -16, /* SIGNAL_STRENGTH_POOR */
-            -11, /* SIGNAL_STRENGTH_MODERATE */
+            -12, /* SIGNAL_STRENGTH_MODERATE */
             -9, /* SIGNAL_STRENGTH_GOOD */
-            -7  /* SIGNAL_STRENGTH_GREAT */
+            -6  /* SIGNAL_STRENGTH_GREAT */
     };
 
     // Lifted from Default carrier configs and max range of SSSINR
diff --git a/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java
index e7dfe634..e926272 100644
--- a/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java
+++ b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java
@@ -102,7 +102,7 @@
         }
 
         ClosedSubscriberGroupInfo o = (ClosedSubscriberGroupInfo) other;
-        return mCsgIndicator == o.mCsgIndicator && mHomeNodebName == o.mHomeNodebName
+        return mCsgIndicator == o.mCsgIndicator && o.mHomeNodebName.equals(mHomeNodebName)
                 && mCsgIdentity == o.mCsgIdentity;
     }
 
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index 270eafe..c667165 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -208,7 +208,6 @@
      * @return {@code true} if using carrier aggregation.
      * @hide
      */
-    @SystemApi
     public boolean isUsingCarrierAggregation() {
         return mIsUsingCarrierAggregation;
     }
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index aff1391..bf21bb7 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -17,16 +17,14 @@
 package android.telephony;
 
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Describes the cause of a disconnected call. Those disconnect causes can be converted into a more
  * generic {@link android.telecom.DisconnectCause} object.
  *
- * @hide
+ * Used in {@link PhoneStateListener#onCallDisconnectCauseChanged}.
  */
-@SystemApi
 public final class DisconnectCause {
 
     /** The disconnect cause is not valid (Not received a disconnect cause) */
@@ -337,20 +335,17 @@
     /**
      * Indicates that the call is dropped due to RTCP inactivity, primarily due to media path
      * disruption.
-     * @hide
      */
     public static final int MEDIA_TIMEOUT = 77;
 
     /**
      * Indicates that an emergency call cannot be placed over WFC because the service is not
      * available in the current location.
-     * @hide
      */
     public static final int EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE = 78;
 
     /**
      * Indicates that WiFi calling service is not available in the current location.
-     * @hide
      */
     public static final int WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 79;
 
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 704e5aa..3984bd7 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -19,9 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.telephony.SubscriptionManager;
 
@@ -34,8 +32,9 @@
     private Context mContext;
 
     /**
-     * <p>Broadcast Action: Indicates that an IMS operation was rejected by the network due to it
-     * not being authorized on the network.
+     * <p>Broadcast Action: Indicates that a previously allowed IMS operation was rejected by the
+     * network due to the network returning a "forbidden" response. This may be due to a
+     * provisioning change from the network.
      * May include the {@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX} extra to also specify
      * which subscription the operation was rejected for.
      * <p class="note">
@@ -43,8 +42,6 @@
      * issues.
      * @hide
      */
-    @SystemApi
-    @TestApi
     // Moved from TelephonyIntents, need to keep backwards compatibility with OEM apps that have
     // this value hard-coded in BroadcastReceiver.
     @SuppressLint("ActionValue")
@@ -74,17 +71,17 @@
             "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
 
     /**
-     * An extra key corresponding to a String value which contains the carrier specific title to be
-     * displayed as part of the message shown to the user when there is an error registering for
-     * WiFi calling.
+     * An extra key corresponding to a {@link CharSequence} value which contains the carrier
+     * specific title to be displayed as part of the message shown to the user when there is an
+     * error registering for WiFi calling.
      */
     public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE =
             "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
 
     /**
-     * An extra key corresponding to a String value which contains the carrier specific message to
-     * be displayed as part of the message shown to the user when there is an error registering for
-     * WiFi calling.
+     * An extra key corresponding to a {@link CharSequence} value which contains the carrier
+     * specific message to  be displayed as part of the message shown to the user when there is an
+     * error registering for WiFi calling.
      */
     public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
             "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
@@ -103,10 +100,7 @@
      * @param subscriptionId The ID of the subscription that this ImsRcsManager will use.
      * @throws IllegalArgumentException if the subscription is invalid.
      * @return a ImsRcsManager instance with the specific subscription ID.
-     * @hide
      */
-    @SystemApi
-    @TestApi
     @NonNull
     public ImsRcsManager getImsRcsManager(int subscriptionId) {
         if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 45deea2..3d96fc6 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -231,6 +231,8 @@
 
     private static final String DESTINATION_SANITY_CHECK_FILE_NAME = "destinationSanityCheckFile";
 
+    private static final int MAX_SERVICE_ANNOUNCEMENT_FILE_SIZE = 10 * 1024; // 10KB
+
     private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
 
     private final Context mContext;
@@ -318,6 +320,16 @@
         return session;
     }
 
+    /**
+     * Returns the maximum size of the service announcement file that can be provided via
+     * {@link #addServiceAnnouncementFile}
+     * @return The maximum length of the byte array passed as an argument to
+     *         {@link #addServiceAnnouncementFile}.
+     */
+    public static int getMaximumServiceAnnouncementFileSize() {
+        return MAX_SERVICE_ANNOUNCEMENT_FILE_SIZE;
+    }
+
     private int bindAndInitialize() {
         mServiceConnection = new ServiceConnection() {
             @Override
@@ -424,6 +436,60 @@
     }
 
     /**
+     * Inform the middleware of a service announcement file received from a group communication
+     * server.
+     *
+     * When participating in a group call via the {@link MbmsGroupCallSession} API, applications may
+     * receive a service announcement file from the group call server that informs them of
+     * files that may be relevant to users communicating on the group call.
+     *
+     * After supplying the service announcement file received from the server to the middleware via
+     * this API, applications will receive information on the available files via
+     * {@link MbmsDownloadSessionCallback#onFileServicesUpdated}, and the available files will be
+     * downloadable via {@link MbmsDownloadSession#download} like other files published via
+     * {@link MbmsDownloadSessionCallback#onFileServicesUpdated}.
+     *
+     * Asynchronous error codes via the {@link MbmsDownloadSessionCallback#onError(int, String)}
+     * callback may include any of the errors that are not specific to the streaming use-case.
+     *
+     * May throw an {@link IllegalStateException} when the middleware has not yet been bound,
+     * or an {@link IllegalArgumentException} if the file is too large.
+     *
+     * @param fileContents The contents of the service announcement file received from the group
+     *                     call server. If the size of this array is greater than the value of
+     *                     {@link #getMaximumServiceAnnouncementFileSize()}, an
+     *                     {@link IllegalArgumentException} will be thrown.
+     */
+    public void addServiceAnnouncementFile(@NonNull byte[] fileContents) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        if (fileContents.length > MAX_SERVICE_ANNOUNCEMENT_FILE_SIZE) {
+            throw new IllegalArgumentException("File too large");
+        }
+
+        try {
+            int returnCode = downloadService.addServiceAnnouncementFile(
+                    mSubscriptionId, fileContents);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (returnCode != MbmsErrors.SUCCESS) {
+                sendErrorToApp(returnCode, null);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
      * Sets the temp file root for downloads.
      * All temp files created for the middleware to write to will be contained in the specified
      * directory. Applications that wish to specify a location only need to call this method once
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 32ffb75..93fbb00 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -25,6 +25,7 @@
 import android.os.Parcelable;
 import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.Annotation.NetworkType;
+import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -214,6 +215,9 @@
     @Nullable
     private DataSpecificRegistrationInfo mDataSpecificInfo;
 
+    @NonNull
+    private String mRplmn;
+
     /**
      * @param domain Network domain. Must be a {@link Domain}. For transport type
      * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, this must set to {@link #DOMAIN_PS}.
@@ -234,13 +238,14 @@
      * @param availableServices The list of the supported services.
      * @param cellIdentity The identity representing a unique cell or wifi AP. Set to null if the
      * information is not available.
+     * @param rplmn the registered plmn or the last plmn for attempted registration if reg failed.
      */
     private NetworkRegistrationInfo(@Domain int domain, @TransportType int transportType,
                                    @RegistrationState int registrationState,
                                    @NetworkType int accessNetworkTechnology, int rejectCause,
                                    boolean emergencyOnly,
                                    @Nullable @ServiceType List<Integer> availableServices,
-                                   @Nullable CellIdentity cellIdentity) {
+                                   @Nullable CellIdentity cellIdentity, @Nullable String rplmn) {
         mDomain = domain;
         mTransportType = transportType;
         mRegistrationState = registrationState;
@@ -253,6 +258,7 @@
         mCellIdentity = cellIdentity;
         mEmergencyOnly = emergencyOnly;
         mNrState = NR_STATE_NONE;
+        mRplmn = rplmn;
     }
 
     /**
@@ -263,11 +269,11 @@
                                    int registrationState, int accessNetworkTechnology,
                                    int rejectCause, boolean emergencyOnly,
                                    @Nullable List<Integer> availableServices,
-                                   @Nullable CellIdentity cellIdentity, boolean cssSupported,
-                                   int roamingIndicator, int systemIsInPrl,
+                                   @Nullable CellIdentity cellIdentity, @Nullable String rplmn,
+                                   boolean cssSupported, int roamingIndicator, int systemIsInPrl,
                                    int defaultRoamingIndicator) {
         this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
-                emergencyOnly, availableServices, cellIdentity);
+                emergencyOnly, availableServices, cellIdentity, rplmn);
 
         mVoiceSpecificInfo = new VoiceSpecificRegistrationInfo(cssSupported, roamingIndicator,
                 systemIsInPrl, defaultRoamingIndicator);
@@ -281,17 +287,17 @@
                                    int registrationState, int accessNetworkTechnology,
                                    int rejectCause, boolean emergencyOnly,
                                    @Nullable List<Integer> availableServices,
-                                   @Nullable CellIdentity cellIdentity, int maxDataCalls,
-                                   boolean isDcNrRestricted, boolean isNrAvailable,
-                                   boolean isEndcAvailable,
+                                   @Nullable CellIdentity cellIdentity, @Nullable String rplmn,
+                                   int maxDataCalls, boolean isDcNrRestricted,
+                                   boolean isNrAvailable, boolean isEndcAvailable,
                                    LteVopsSupportInfo lteVopsSupportInfo,
                                    boolean isUsingCarrierAggregation) {
         this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
-                emergencyOnly, availableServices, cellIdentity);
+                emergencyOnly, availableServices, cellIdentity, rplmn);
         mDataSpecificInfo = new DataSpecificRegistrationInfo(
                 maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo,
                 isUsingCarrierAggregation);
-        updateNrState(mDataSpecificInfo);
+        updateNrState();
     }
 
     private NetworkRegistrationInfo(Parcel source) {
@@ -310,6 +316,7 @@
         mDataSpecificInfo = source.readParcelable(
                 DataSpecificRegistrationInfo.class.getClassLoader());
         mNrState = source.readInt();
+        mRplmn = source.readString();
     }
 
     /**
@@ -343,6 +350,7 @@
             mDataSpecificInfo = new DataSpecificRegistrationInfo(nri.mDataSpecificInfo);
         }
         mNrState = nri.mNrState;
+        mRplmn = nri.mRplmn;
     }
 
     /**
@@ -359,6 +367,7 @@
      * Get the 5G NR connection state.
      *
      * @return the 5G NR connection state.
+     * @hide
      */
     public @NRState int getNrState() {
         return mNrState;
@@ -395,6 +404,22 @@
     }
 
     /**
+     * Get the PLMN-ID for this Network Registration, also known as the RPLMN.
+     *
+     * <p>If the device is registered, this will return the registered PLMN-ID. If registration
+     * has failed, then this will return the PLMN ID of the last attempted registration. If the
+     * device is not registered, or if is registered to a non-3GPP radio technology, then this
+     * will return null.
+     *
+     * <p>See 3GPP TS 23.122 for further information about the Registered PLMN.
+     *
+     * @return the registered PLMN-ID or null.
+     */
+    @Nullable public String getRegisteredPlmn() {
+        return mRplmn;
+    }
+
+    /**
      * @return {@code true} if registered on roaming network, {@code false} otherwise.
      */
     public boolean isRoaming() {
@@ -590,6 +615,7 @@
                 .append(" voiceSpecificInfo=").append(mVoiceSpecificInfo)
                 .append(" dataSpecificInfo=").append(mDataSpecificInfo)
                 .append(" nrState=").append(nrStateToString(mNrState))
+                .append(" rRplmn=").append(mRplmn)
                 .append("}").toString();
     }
 
@@ -597,7 +623,7 @@
     public int hashCode() {
         return Objects.hash(mDomain, mTransportType, mRegistrationState, mRoamingType,
                 mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices,
-                mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState);
+                mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn);
     }
 
     @Override
@@ -620,6 +646,7 @@
                 && Objects.equals(mCellIdentity, other.mCellIdentity)
                 && Objects.equals(mVoiceSpecificInfo, other.mVoiceSpecificInfo)
                 && Objects.equals(mDataSpecificInfo, other.mDataSpecificInfo)
+                && TextUtils.equals(mRplmn, other.mRplmn)
                 && mNrState == other.mNrState;
     }
 
@@ -641,6 +668,7 @@
         dest.writeParcelable(mVoiceSpecificInfo, 0);
         dest.writeParcelable(mDataSpecificInfo, 0);
         dest.writeInt(mNrState);
+        dest.writeString(mRplmn);
     }
 
     /**
@@ -658,12 +686,12 @@
      * DCNR is not restricted and NR is supported by the selected PLMN. Otherwise the use of 5G
      * NR is restricted.
      *
-     * @param state data specific registration state contains the 5G NR indicators.
+     * @hide
      */
-    private void updateNrState(DataSpecificRegistrationInfo state) {
+    public void updateNrState() {
         mNrState = NR_STATE_NONE;
-        if (state.isEnDcAvailable) {
-            if (!state.isDcNrRestricted && state.isNrAvailable) {
+        if (mDataSpecificInfo != null && mDataSpecificInfo.isEnDcAvailable) {
+            if (!mDataSpecificInfo.isDcNrRestricted && mDataSpecificInfo.isNrAvailable) {
                 mNrState = NR_STATE_NOT_RESTRICTED;
             } else {
                 mNrState = NR_STATE_RESTRICTED;
@@ -741,6 +769,9 @@
         @Nullable
         private CellIdentity mCellIdentity;
 
+        @NonNull
+        private String mRplmn = "";
+
         /**
          * Default constructor for Builder.
          */
@@ -855,6 +886,18 @@
         }
 
         /**
+         * Set the registered PLMN.
+         *
+         * @param rplmn the registered plmn.
+         *
+         * @return The same instance of the builder.
+         */
+        public @NonNull Builder setRegisteredPlmn(@Nullable String rplmn) {
+            mRplmn = rplmn;
+            return this;
+        }
+
+        /**
          * Build the NetworkRegistrationInfo.
          * @return the NetworkRegistrationInfo object.
          * @hide
@@ -863,7 +906,7 @@
         public @NonNull NetworkRegistrationInfo build() {
             return new NetworkRegistrationInfo(mDomain, mTransportType, mRegistrationState,
                     mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices,
-                    mCellIdentity);
+                    mCellIdentity, mRplmn);
         }
     }
 }
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 87d94bfd..c75de42 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -234,6 +234,9 @@
      * this method to facilitate the creation of {@link NetworkServiceProvider} instances. The system
      * will call this method after binding the network service for each active SIM slot id.
      *
+     * This methead is guaranteed to be invoked in {@link NetworkService}'s internal handler thread
+     * whose looper can be retrieved with {@link Looper.myLooper()} when override this method.
+     *
      * @param slotIndex SIM slot id the network service associated with.
      * @return Network service object. Null if failed to create the provider (e.g. invalid slot
      * index)
diff --git a/telephony/java/android/telephony/PinResult.java b/telephony/java/android/telephony/PinResult.java
index 683e853..d9b5de5 100644
--- a/telephony/java/android/telephony/PinResult.java
+++ b/telephony/java/android/telephony/PinResult.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,13 +31,13 @@
  *
  * @hide
  */
-@SystemApi
 public final class PinResult implements Parcelable {
     /** @hide */
     @IntDef({
             PIN_RESULT_TYPE_SUCCESS,
             PIN_RESULT_TYPE_INCORRECT,
             PIN_RESULT_TYPE_FAILURE,
+            PIN_RESULT_TYPE_ABORTED,
     })
     public @interface PinResultType {}
 
@@ -57,6 +56,11 @@
      */
     public static final int PIN_RESULT_TYPE_FAILURE = PhoneConstants.PIN_GENERAL_FAILURE;
 
+    /**
+     * Indicates that the pin attempt was aborted.
+     */
+    public static final int PIN_RESULT_TYPE_ABORTED = PhoneConstants.PIN_OPERATION_ABORTED;
+
     private static final PinResult sFailedResult =
             new PinResult(PinResult.PIN_RESULT_TYPE_FAILURE, -1);
 
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 54c22ae..a9abe89 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -95,7 +95,6 @@
      *        if there is no valid APN setting for the specific type, then this will be null
      * @hide
      */
-    @SystemApi
     public PreciseDataConnectionState(@DataState int state,
                                       @NetworkType int networkType,
                                       @ApnType int apnTypes, @NonNull String apn,
@@ -265,10 +264,10 @@
     /**
      * Return the APN Settings for this data connection.
      *
-     * Returns the ApnSetting that was used to configure this data connection.
+     * @return the ApnSetting that was used to configure this data connection.
      */
     // FIXME: This shouldn't be nullable; update once the ApnSetting is supplied correctly
-    @Nullable ApnSetting getApnSetting() {
+    public @Nullable ApnSetting getApnSetting() {
         return mApnSetting;
     }
 
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index bc84738..90ddf2c 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -260,24 +260,6 @@
         return raf;
     }
 
-    /**
-     * Returns the highest capability of the RadioAccessFamily (4G > 3G > 2G).
-     * @param raf The RadioAccessFamily that we wish to filter
-     * @return The highest radio capability
-     */
-    public static int getHighestRafCapability(int raf) {
-        if ((LTE & raf) > 0) {
-            return TelephonyManager.NETWORK_CLASS_4_G;
-        }
-        if ((EVDO|HS|WCDMA & raf) > 0) {
-            return TelephonyManager.NETWORK_CLASS_3_G;
-        }
-        if((GSM|CDMA & raf) > 0) {
-            return TelephonyManager.NETWORK_CLASS_2_G;
-        }
-        return TelephonyManager.NETWORK_CLASS_UNKNOWN;
-    }
-
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     @PrefNetworkMode
     public static int getNetworkTypeFromRaf(int raf) {
@@ -395,4 +377,34 @@
         }
         return result;
     }
+
+    /**
+     * Compare two sets of network types to see which is more capable.
+     *
+     * This algorithm first tries to see see if a set has a strict superset of RAT support for
+     * each generation, from newest to oldest; if that results in a tie, then it returns the set
+     * that supports the most RAT types.
+     */
+    public static int compare(long networkTypeBitmaskL, long networkTypeBitmaskR) {
+        final long[] prioritizedNetworkClassBitmasks = new long[] {
+            TelephonyManager.NETWORK_CLASS_BITMASK_5G,
+            TelephonyManager.NETWORK_CLASS_BITMASK_4G,
+            TelephonyManager.NETWORK_CLASS_BITMASK_3G,
+            TelephonyManager.NETWORK_CLASS_BITMASK_2G,
+        };
+
+        long lhsUnique = networkTypeBitmaskL & ~networkTypeBitmaskR;
+        long rhsUnique = networkTypeBitmaskR & ~networkTypeBitmaskL;
+
+        // See if one has a strict super-set of capabilities, generation by generation.
+        for (long classBitmask : prioritizedNetworkClassBitmasks) {
+            int result = 0;
+            if ((lhsUnique & classBitmask) != 0) ++result;
+            if ((rhsUnique & classBitmask) != 0) --result;
+            if (result != 0) return result;
+        }
+
+        // Without a clear winner, return the one that supports the most types.
+        return Long.bitCount(networkTypeBitmaskL) - Long.bitCount(networkTypeBitmaskR);
+    }
 }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index f8e4bea..5c84297 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -580,7 +580,6 @@
      *
      * @hide
      */
-    @SystemApi
     public @RegState int getDataRegistrationState() {
         return getDataRegState();
     }
@@ -689,8 +688,9 @@
      * @return true if registration indicates roaming, false otherwise
      * @hide
      */
-    @SystemApi
     public boolean getDataRoamingFromRegistration() {
+        // TODO: all callers should refactor to get roaming state directly from modem
+        // this should not be exposed as a public API
         return mIsDataRoamingFromRegistration;
     }
 
@@ -1392,15 +1392,16 @@
 
     /** @hide */
     public boolean isUsingCarrierAggregation() {
+        boolean isUsingCa = false;
         NetworkRegistrationInfo nri = getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
         if (nri != null) {
             DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
             if (dsri != null) {
-                return dsri.isUsingCarrierAggregation();
+                isUsingCa = dsri.isUsingCarrierAggregation();
             }
         }
-        return false;
+        return isUsingCa || getCellBandwidths().length > 1;
     }
 
     /** @hide */
@@ -1422,7 +1423,6 @@
      * @return the frequency range of 5G NR.
      * @hide
      */
-    @SystemApi
     public @FrequencyRange int getNrFrequencyRange() {
         return mNrFrequencyRange;
     }
@@ -1993,7 +1993,6 @@
      * @return the copied ServiceState with location info sanitized.
      * @hide
      */
-    @SystemApi
     @NonNull
     public ServiceState createLocationInfoSanitizedCopy(boolean removeCoarseLocation) {
         ServiceState state = new ServiceState(this);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 16fbd07..5f09ca2 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -83,6 +83,8 @@
     // Effectively final. Timestamp is set during construction of SignalStrength
     private long mTimestampMillis;
 
+    private boolean mLteAsPrimaryInNrNsa = true;
+
     CellSignalStrengthCdma mCdma;
     CellSignalStrengthGsm mGsm;
     CellSignalStrengthWcdma mWcdma;
@@ -190,12 +192,16 @@
     private CellSignalStrength getPrimary() {
         // This behavior is intended to replicate the legacy behavior of getLevel() by prioritizing
         // newer faster RATs for default/for display purposes.
+
+        if (mLteAsPrimaryInNrNsa) {
+            if (mLte.isValid()) return mLte;
+        }
+        if (mNr.isValid()) return mNr;
         if (mLte.isValid()) return mLte;
         if (mCdma.isValid()) return mCdma;
         if (mTdscdma.isValid()) return mTdscdma;
         if (mWcdma.isValid()) return mWcdma;
         if (mGsm.isValid()) return mGsm;
-        if (mNr.isValid()) return mNr;
         return mLte;
     }
 
@@ -270,6 +276,10 @@
 
     /** @hide */
     public void updateLevel(PersistableBundle cc, ServiceState ss) {
+        if (cc != null) {
+            mLteAsPrimaryInNrNsa = cc.getBoolean(
+                    CarrierConfigManager.KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL, true);
+        }
         mCdma.updateLevel(cc, ss);
         mGsm.updateLevel(cc, ss);
         mWcdma.updateLevel(cc, ss);
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index fe78588..7456aab 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -389,7 +389,7 @@
             String destinationAddress, String scAddress, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
         sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
-                true /* persistMessage*/, null);
+                true /* persistMessage*/, null, null);
     }
 
     /**
@@ -507,7 +507,7 @@
 
     private void sendTextMessageInternal(String destinationAddress, String scAddress,
             String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
-            boolean persistMessage, String packageName) {
+            boolean persistMessage, String packageName, String attributionTag) {
         if (TextUtils.isEmpty(destinationAddress)) {
             throw new IllegalArgumentException("Invalid destinationAddress");
         }
@@ -532,7 +532,7 @@
                 public void onSuccess(int subId) {
                     ISms iSms = getISmsServiceOrThrow();
                     try {
-                        iSms.sendTextForSubscriber(subId, packageName,
+                        iSms.sendTextForSubscriber(subId, packageName, attributionTag,
                                 destinationAddress, scAddress, text, sentIntent, deliveryIntent,
                                 persistMessage);
                     } catch (RemoteException e) {
@@ -552,7 +552,7 @@
             // visible to the user.
             ISms iSms = getISmsServiceOrThrow();
             try {
-                iSms.sendTextForSubscriber(getSubscriptionId(), packageName,
+                iSms.sendTextForSubscriber(getSubscriptionId(), packageName, attributionTag,
                         destinationAddress, scAddress, text, sentIntent, deliveryIntent,
                         persistMessage);
             } catch (RemoteException e) {
@@ -599,7 +599,7 @@
             String destinationAddress, String scAddress, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
         sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
-                false /* persistMessage */, null);
+                false /* persistMessage */, null, null);
     }
 
     private void sendTextMessageInternal(
@@ -642,7 +642,7 @@
                         ISms iSms = getISmsServiceOrThrow();
                         if (iSms != null) {
                             iSms.sendTextForSubscriberWithOptions(subId,
-                                    null, destinationAddress,
+                                    null, null, destinationAddress,
                                     scAddress,
                                     text, sentIntent, deliveryIntent, persistMessage, finalPriority,
                                     expectMore, finalValidity);
@@ -664,7 +664,7 @@
                 ISms iSms = getISmsServiceOrThrow();
                 if (iSms != null) {
                     iSms.sendTextForSubscriberWithOptions(getSubscriptionId(),
-                            null, destinationAddress,
+                            null, null, destinationAddress,
                             scAddress,
                             text, sentIntent, deliveryIntent, persistMessage, finalPriority,
                             expectMore, finalValidity);
@@ -882,7 +882,24 @@
             String destinationAddress, String scAddress, ArrayList<String> parts,
             ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
         sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
-                deliveryIntents, true /* persistMessage*/, null);
+                deliveryIntents, true /* persistMessage*/, null, null);
+    }
+
+    /**
+     * @deprecated Use {@link #sendMultipartTextMessage(String, String, List, List, List, String,
+     * String)} instead.
+     *
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    @TestApi
+    public void sendMultipartTextMessage(
+            @NonNull String destinationAddress, @NonNull String scAddress,
+            @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
+            @Nullable List<PendingIntent> deliveryIntents, @NonNull String packageName) {
+        sendMultipartTextMessage(destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
+                packageName, null);
     }
 
     /**
@@ -909,15 +926,16 @@
     public void sendMultipartTextMessage(
             @NonNull String destinationAddress, @NonNull String scAddress,
             @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
-            @Nullable List<PendingIntent> deliveryIntents, @NonNull String packageName) {
+            @Nullable List<PendingIntent> deliveryIntents, @NonNull String packageName,
+            @Nullable String attributionTag) {
         sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
-                deliveryIntents, true /* persistMessage*/, packageName);
+                deliveryIntents, true /* persistMessage*/, packageName, attributionTag);
     }
 
     private void sendMultipartTextMessageInternal(
             String destinationAddress, String scAddress, List<String> parts,
             List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
-            boolean persistMessage, String packageName) {
+            boolean persistMessage, String packageName, String attributionTag) {
         if (TextUtils.isEmpty(destinationAddress)) {
             throw new IllegalArgumentException("Invalid destinationAddress");
         }
@@ -942,7 +960,7 @@
                     public void onSuccess(int subId) {
                         try {
                             ISms iSms = getISmsServiceOrThrow();
-                            iSms.sendMultipartTextForSubscriber(subId, packageName,
+                            iSms.sendMultipartTextForSubscriber(subId, packageName, attributionTag,
                                     destinationAddress, scAddress, parts, sentIntents,
                                     deliveryIntents, persistMessage);
                         } catch (RemoteException e) {
@@ -963,8 +981,8 @@
                     ISms iSms = getISmsServiceOrThrow();
                     if (iSms != null) {
                         iSms.sendMultipartTextForSubscriber(getSubscriptionId(), packageName,
-                                destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
-                                persistMessage);
+                                attributionTag, destinationAddress, scAddress, parts, sentIntents,
+                                deliveryIntents, persistMessage);
                     }
                 } catch (RemoteException e) {
                     Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
@@ -982,7 +1000,7 @@
                 deliveryIntent = deliveryIntents.get(0);
             }
             sendTextMessageInternal(destinationAddress, scAddress, parts.get(0),
-                    sentIntent, deliveryIntent, true, packageName);
+                    sentIntent, deliveryIntent, true, packageName, attributionTag);
         }
     }
 
@@ -1012,7 +1030,7 @@
             String destinationAddress, String scAddress, List<String> parts,
             List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
         sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
-                deliveryIntents, false /* persistMessage*/, null);
+                deliveryIntents, false /* persistMessage*/, null, null);
     }
 
     /**
@@ -1174,7 +1192,7 @@
                             ISms iSms = getISmsServiceOrThrow();
                             if (iSms != null) {
                                 iSms.sendMultipartTextForSubscriberWithOptions(subId,
-                                        null, destinationAddress,
+                                        null, null, destinationAddress,
                                         scAddress, parts, sentIntents, deliveryIntents,
                                         persistMessage, finalPriority, expectMore, finalValidity);
                             }
@@ -1196,7 +1214,7 @@
                     ISms iSms = getISmsServiceOrThrow();
                     if (iSms != null) {
                         iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
-                                null, destinationAddress,
+                                null, null, destinationAddress,
                                 scAddress, parts, sentIntents, deliveryIntents,
                                 persistMessage, finalPriority, expectMore, finalValidity);
                     }
@@ -1327,9 +1345,8 @@
             public void onSuccess(int subId) {
                 try {
                     ISms iSms = getISmsServiceOrThrow();
-                    iSms.sendDataForSubscriber(subId, null,
-                            destinationAddress, scAddress, destinationPort & 0xFFFF, data,
-                            sentIntent, deliveryIntent);
+                    iSms.sendDataForSubscriber(subId, null, null, destinationAddress, scAddress,
+                            destinationPort & 0xFFFF, data, sentIntent, deliveryIntent);
                 } catch (RemoteException e) {
                     Log.e(TAG, "sendDataMessage: Couldn't send SMS - Exception: " + e.getMessage());
                     notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
@@ -1481,7 +1498,7 @@
             // it here because we do not have access to the activity context that is performing this
             // operation.
             // Requires that the calling process has the SEND_SMS permission.
-            getITelephony().enqueueSmsPickResult(null,
+            getITelephony().enqueueSmsPickResult(null, null,
                     new IIntegerConsumer.Stub() {
                         @Override
                         public void accept(int subId) {
@@ -2378,7 +2395,6 @@
      * @param sentIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is successfully sent, or failed
      * @throws IllegalArgumentException if contentUri is empty
-     * @deprecated use {@link MmsManager#sendMultimediaMessage} instead.
      */
     public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
             Bundle configOverrides, PendingIntent sentIntent) {
@@ -2413,7 +2429,6 @@
      * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is downloaded, or the download is failed
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
-     * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead.
      */
     public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
             Bundle configOverrides, PendingIntent downloadedIntent) {
@@ -2773,8 +2788,7 @@
      * </p>
      *
      * @param smsc the SMSC address string.
-     * @return true for success, false otherwise. Failure can be due to caller not having the
-     * appropriate permission, or modem returning an error.
+     * @return true for success, false otherwise. Failure can be due modem returning an error.
      */
     @SuppressAutoDoc // for carrier privileges and default SMS application.
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 52f0898..7266fc0 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -493,7 +493,10 @@
         String newMsgBody = null;
         Resources r = Resources.getSystem();
         if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
-            newMsgBody = Sms7BitEncodingTranslator.translate(text, isCdma);
+            // 7-bit ASCII table based translation is required only for CDMA single-part SMS since
+            // ENCODING_7BIT_ASCII is used for CDMA single-part SMS and ENCODING_GSM_7BIT_ALPHABET
+            // is used for CDMA multi-part SMS.
+            newMsgBody = Sms7BitEncodingTranslator.translate(text, isCdma && ted.msgCount == 1);
         }
         if (TextUtils.isEmpty(newMsgBody)) {
             newMsgBody = text;
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 832771d..dc75c58 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -226,7 +226,7 @@
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
                 roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, -1,
                 false, null, false, TelephonyManager.UNKNOWN_CARRIER_ID,
-                SubscriptionManager.PROFILE_CLASS_DEFAULT,
+                SubscriptionManager.PROFILE_CLASS_UNSET,
                 SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null, true);
     }
 
@@ -798,7 +798,7 @@
                 + " hplmns=" + Arrays.toString(mHplmns)
                 + " subscriptionType=" + mSubscriptionType
                 + " mGroupOwner=" + mGroupOwner
-                + " carrierConfigAccessRules=" + mCarrierConfigAccessRules
+                + " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules)
                 + " mAreUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled + "}";
     }
 
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d8cebfb..7f2c6c1 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -265,7 +265,8 @@
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String UNIQUE_KEY_SUBSCRIPTION_ID = SimInfo.UNIQUE_KEY_SUBSCRIPTION_ID;
+    public static final String UNIQUE_KEY_SUBSCRIPTION_ID =
+            SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID;
 
     /**
      * TelephonyProvider column name for a unique identifier for the subscription within the
@@ -274,14 +275,14 @@
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String ICC_ID = SimInfo.ICC_ID;
+    public static final String ICC_ID = SimInfo.COLUMN_ICC_ID;
 
     /**
      * TelephonyProvider column name for user SIM_SlOT_INDEX
      * <P>Type: INTEGER (int)</P>
      */
     /** @hide */
-    public static final String SIM_SLOT_INDEX = SimInfo.SIM_SLOT_INDEX;
+    public static final String SIM_SLOT_INDEX = SimInfo.COLUMN_SIM_SLOT_INDEX;
 
     /** SIM is not inserted */
     /** @hide */
@@ -300,7 +301,7 @@
      * Default value is 0.
      */
     /** @hide */
-    public static final String SUBSCRIPTION_TYPE = SimInfo.SUBSCRIPTION_TYPE;
+    public static final String SUBSCRIPTION_TYPE = SimInfo.COLUMN_SUBSCRIPTION_TYPE;
 
     /**
      * TelephonyProvider column name data_enabled_override_rules.
@@ -313,7 +314,8 @@
      *
      * @hide
      */
-    public static final String DATA_ENABLED_OVERRIDE_RULES = SimInfo.DATA_ENABLED_OVERRIDE_RULES;
+    public static final String DATA_ENABLED_OVERRIDE_RULES =
+            SimInfo.COLUMN_DATA_ENABLED_OVERRIDE_RULES;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -362,14 +364,14 @@
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String DISPLAY_NAME = SimInfo.DISPLAY_NAME;
+    public static final String DISPLAY_NAME = SimInfo.COLUMN_DISPLAY_NAME;
 
     /**
      * TelephonyProvider column name for the service provider name for the SIM.
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String CARRIER_NAME = SimInfo.CARRIER_NAME;
+    public static final String CARRIER_NAME = SimInfo.COLUMN_CARRIER_NAME;
 
     /**
      * Default name resource
@@ -383,13 +385,13 @@
      *
      * @hide
      */
-    public static final String NAME_SOURCE = SimInfo.NAME_SOURCE;
+    public static final String NAME_SOURCE = SimInfo.COLUMN_NAME_SOURCE;
 
     /**
-     * The name_source is the default, which is from the carrier id.
+     * The name_source is from the carrier id.
      * @hide
      */
-    public static final int NAME_SOURCE_DEFAULT = SimInfo.NAME_SOURCE_DEFAULT;
+    public static final int NAME_SOURCE_CARRIER_ID = SimInfo.NAME_SOURCE_CARRIER_ID;
 
     /**
      * The name_source is from SIM EF_SPN.
@@ -420,7 +422,7 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"NAME_SOURCE_"},
             value = {
-                    NAME_SOURCE_DEFAULT,
+                    NAME_SOURCE_CARRIER_ID,
                     NAME_SOURCE_SIM_SPN,
                     NAME_SOURCE_USER_INPUT,
                     NAME_SOURCE_CARRIER,
@@ -433,21 +435,21 @@
      * <P>Type: INTEGER (int)</P>
      */
     /** @hide */
-    public static final String COLOR = SimInfo.COLOR;
+    public static final String HUE = SimInfo.COLUMN_COLOR;
 
     /**
      * TelephonyProvider column name for the phone number of a SIM.
      * <P>Type: TEXT (String)</P>
      */
     /** @hide */
-    public static final String NUMBER = SimInfo.NUMBER;
+    public static final String NUMBER = SimInfo.COLUMN_NUMBER;
 
     /**
      * TelephonyProvider column name for whether data roaming is enabled.
      * <P>Type: INTEGER (int)</P>
      */
     /** @hide */
-    public static final String DATA_ROAMING = SimInfo.DATA_ROAMING;
+    public static final String DATA_ROAMING = SimInfo.COLUMN_DATA_ROAMING;
 
     /** Indicates that data roaming is enabled for a subscription */
     public static final int DATA_ROAMING_ENABLE = SimInfo.DATA_ROAMING_ENABLE;
@@ -455,63 +457,60 @@
     /** Indicates that data roaming is disabled for a subscription */
     public static final int DATA_ROAMING_DISABLE = SimInfo.DATA_ROAMING_DISABLE;
 
-    /** @hide */
-    public static final int DATA_ROAMING_DEFAULT = SimInfo.DATA_ROAMING_DEFAULT;
-
     /**
      * TelephonyProvider column name for subscription carrier id.
      * @see TelephonyManager#getSimCarrierId()
      * <p>Type: INTEGER (int) </p>
      * @hide
      */
-    public static final String CARRIER_ID = SimInfo.CARRIER_ID;
+    public static final String CARRIER_ID = SimInfo.COLUMN_CARRIER_ID;
 
     /**
      * @hide A comma-separated list of EHPLMNs associated with the subscription
      * <P>Type: TEXT (String)</P>
      */
-    public static final String EHPLMNS = SimInfo.EHPLMNS;
+    public static final String EHPLMNS = SimInfo.COLUMN_EHPLMNS;
 
     /**
      * @hide A comma-separated list of HPLMNs associated with the subscription
      * <P>Type: TEXT (String)</P>
      */
-    public static final String HPLMNS = SimInfo.HPLMNS;
+    public static final String HPLMNS = SimInfo.COLUMN_HPLMNS;
 
     /**
      * TelephonyProvider column name for the MCC associated with a SIM, stored as a string.
      * <P>Type: TEXT (String)</P>
      * @hide
      */
-    public static final String MCC_STRING = SimInfo.MCC_STRING;
+    public static final String MCC_STRING = SimInfo.COLUMN_MCC_STRING;
 
     /**
      * TelephonyProvider column name for the MNC associated with a SIM, stored as a string.
      * <P>Type: TEXT (String)</P>
      * @hide
      */
-    public static final String MNC_STRING = SimInfo.MNC_STRING;
+    public static final String MNC_STRING = SimInfo.COLUMN_MNC_STRING;
 
     /**
      * TelephonyProvider column name for the MCC associated with a SIM.
      * <P>Type: INTEGER (int)</P>
      * @hide
      */
-    public static final String MCC = SimInfo.MCC;
+    public static final String MCC = SimInfo.COLUMN_MCC;
 
     /**
      * TelephonyProvider column name for the MNC associated with a SIM.
      * <P>Type: INTEGER (int)</P>
      * @hide
      */
-    public static final String MNC = SimInfo.MNC;
+    public static final String MNC = SimInfo.COLUMN_MNC;
 
     /**
      * TelephonyProvider column name for the iso country code associated with a SIM.
      * <P>Type: TEXT (String)</P>
      * @hide
      */
-    public static final String ISO_COUNTRY_CODE = SimInfo.ISO_COUNTRY_CODE;
+    public static final String ISO_COUNTRY_CODE = SimInfo.COLUMN_ISO_COUNTRY_CODE;
 
     /**
      * TelephonyProvider column name for whether a subscription is embedded (that is, present on an
@@ -519,7 +518,7 @@
      * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
      * @hide
      */
-    public static final String IS_EMBEDDED = SimInfo.IS_EMBEDDED;
+    public static final String IS_EMBEDDED = SimInfo.COLUMN_IS_EMBEDDED;
 
     /**
      * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the
@@ -527,7 +526,7 @@
      * <P>Type: TEXT (String)</P>
      * @hide
      */
-    public static final String CARD_ID = SimInfo.CARD_ID;
+    public static final String CARD_ID = SimInfo.COLUMN_CARD_ID;
 
     /**
      * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
@@ -535,7 +534,7 @@
      * <p>TYPE: BLOB
      * @hide
      */
-    public static final String ACCESS_RULES = SimInfo.ACCESS_RULES;
+    public static final String ACCESS_RULES = SimInfo.COLUMN_ACCESS_RULES;
 
     /**
      * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
@@ -545,7 +544,7 @@
      * @hide
      */
     public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS =
-            SimInfo.ACCESS_RULES_FROM_CARRIER_CONFIGS;
+            SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS;
 
     /**
      * TelephonyProvider column name identifying whether an embedded subscription is on a removable
@@ -555,79 +554,82 @@
      * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable.
      * @hide
      */
-    public static final String IS_REMOVABLE = SimInfo.IS_REMOVABLE;
+    public static final String IS_REMOVABLE = SimInfo.COLUMN_IS_REMOVABLE;
 
     /**
      *  TelephonyProvider column name for extreme threat in CB settings
      * @hide
      */
-    public static final String CB_EXTREME_THREAT_ALERT = SimInfo.CB_EXTREME_THREAT_ALERT;
+    public static final String CB_EXTREME_THREAT_ALERT =
+            SimInfo.COLUMN_CB_EXTREME_THREAT_ALERT;
 
     /**
      * TelephonyProvider column name for severe threat in CB settings
      *@hide
      */
-    public static final String CB_SEVERE_THREAT_ALERT = SimInfo.CB_SEVERE_THREAT_ALERT;
+    public static final String CB_SEVERE_THREAT_ALERT = SimInfo.COLUMN_CB_SEVERE_THREAT_ALERT;
 
     /**
      * TelephonyProvider column name for amber alert in CB settings
      *@hide
      */
-    public static final String CB_AMBER_ALERT = SimInfo.CB_AMBER_ALERT;
+    public static final String CB_AMBER_ALERT = SimInfo.COLUMN_CB_AMBER_ALERT;
 
     /**
      * TelephonyProvider column name for emergency alert in CB settings
      *@hide
      */
-    public static final String CB_EMERGENCY_ALERT = SimInfo.CB_EMERGENCY_ALERT;
+    public static final String CB_EMERGENCY_ALERT = SimInfo.COLUMN_CB_EMERGENCY_ALERT;
 
     /**
      * TelephonyProvider column name for alert sound duration in CB settings
      *@hide
      */
-    public static final String CB_ALERT_SOUND_DURATION = SimInfo.CB_ALERT_SOUND_DURATION;
+    public static final String CB_ALERT_SOUND_DURATION =
+            SimInfo.COLUMN_CB_ALERT_SOUND_DURATION;
 
     /**
      * TelephonyProvider column name for alert reminder interval in CB settings
      *@hide
      */
-    public static final String CB_ALERT_REMINDER_INTERVAL = SimInfo.CB_ALERT_REMINDER_INTERVAL;
+    public static final String CB_ALERT_REMINDER_INTERVAL =
+            SimInfo.COLUMN_CB_ALERT_REMINDER_INTERVAL;
 
     /**
      * TelephonyProvider column name for enabling vibrate in CB settings
      *@hide
      */
-    public static final String CB_ALERT_VIBRATE = SimInfo.CB_ALERT_VIBRATE;
+    public static final String CB_ALERT_VIBRATE = SimInfo.COLUMN_CB_ALERT_VIBRATE;
 
     /**
      * TelephonyProvider column name for enabling alert speech in CB settings
      *@hide
      */
-    public static final String CB_ALERT_SPEECH = SimInfo.CB_ALERT_SPEECH;
+    public static final String CB_ALERT_SPEECH = SimInfo.COLUMN_CB_ALERT_SPEECH;
 
     /**
      * TelephonyProvider column name for ETWS test alert in CB settings
      *@hide
      */
-    public static final String CB_ETWS_TEST_ALERT = SimInfo.CB_ETWS_TEST_ALERT;
+    public static final String CB_ETWS_TEST_ALERT = SimInfo.COLUMN_CB_ETWS_TEST_ALERT;
 
     /**
      * TelephonyProvider column name for enable channel50 alert in CB settings
      *@hide
      */
-    public static final String CB_CHANNEL_50_ALERT = SimInfo.CB_CHANNEL_50_ALERT;
+    public static final String CB_CHANNEL_50_ALERT = SimInfo.COLUMN_CB_CHANNEL_50_ALERT;
 
     /**
      * TelephonyProvider column name for CMAS test alert in CB settings
      *@hide
      */
-    public static final String CB_CMAS_TEST_ALERT = SimInfo.CB_CMAS_TEST_ALERT;
+    public static final String CB_CMAS_TEST_ALERT = SimInfo.COLUMN_CB_CMAS_TEST_ALERT;
 
     /**
      * TelephonyProvider column name for Opt out dialog in CB settings
      *@hide
      */
-    public static final String CB_OPT_OUT_DIALOG = SimInfo.CB_OPT_OUT_DIALOG;
+    public static final String CB_OPT_OUT_DIALOG = SimInfo.COLUMN_CB_OPT_OUT_DIALOG;
 
     /**
      * TelephonyProvider column name for enable Volte.
@@ -636,44 +638,45 @@
      * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}.
      *@hide
      */
-    public static final String ENHANCED_4G_MODE_ENABLED = SimInfo.ENHANCED_4G_MODE_ENABLED;
+    public static final String ENHANCED_4G_MODE_ENABLED =
+            SimInfo.COLUMN_ENHANCED_4G_MODE_ENABLED;
 
     /**
      * TelephonyProvider column name for enable VT (Video Telephony over IMS)
      *@hide
      */
-    public static final String VT_IMS_ENABLED = SimInfo.VT_IMS_ENABLED;
+    public static final String VT_IMS_ENABLED = SimInfo.COLUMN_VT_IMS_ENABLED;
 
     /**
      * TelephonyProvider column name for enable Wifi calling
      *@hide
      */
-    public static final String WFC_IMS_ENABLED = SimInfo.WFC_IMS_ENABLED;
+    public static final String WFC_IMS_ENABLED = SimInfo.COLUMN_WFC_IMS_ENABLED;
 
     /**
      * TelephonyProvider column name for Wifi calling mode
      *@hide
      */
-    public static final String WFC_IMS_MODE = SimInfo.WFC_IMS_MODE;
+    public static final String WFC_IMS_MODE = SimInfo.COLUMN_WFC_IMS_MODE;
 
     /**
      * TelephonyProvider column name for Wifi calling mode in roaming
      *@hide
      */
-    public static final String WFC_IMS_ROAMING_MODE = SimInfo.WFC_IMS_ROAMING_MODE;
+    public static final String WFC_IMS_ROAMING_MODE = SimInfo.COLUMN_WFC_IMS_ROAMING_MODE;
 
     /**
      * TelephonyProvider column name for enable Wifi calling in roaming
      *@hide
      */
-    public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.WFC_IMS_ROAMING_ENABLED;
+    public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED;
 
     /**
      * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this
      * subscription.
      * @hide
      */
-    public static final String IMS_RCS_UCE_ENABLED = SimInfo.IMS_RCS_UCE_ENABLED;
+    public static final String IMS_RCS_UCE_ENABLED = SimInfo.COLUMN_IMS_RCS_UCE_ENABLED;
 
     /**
      * TelephonyProvider column name for whether a subscription is opportunistic, that is,
@@ -682,7 +685,7 @@
      * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic.
      * @hide
      */
-    public static final String IS_OPPORTUNISTIC = SimInfo.IS_OPPORTUNISTIC;
+    public static final String IS_OPPORTUNISTIC = SimInfo.COLUMN_IS_OPPORTUNISTIC;
 
     /**
      * TelephonyProvider column name for group ID. Subscriptions with same group ID
@@ -691,7 +694,7 @@
      *
      * @hide
      */
-    public static final String GROUP_UUID = SimInfo.GROUP_UUID;
+    public static final String GROUP_UUID = SimInfo.COLUMN_GROUP_UUID;
 
     /**
      * TelephonyProvider column name for group owner. It's the package name who created
@@ -699,7 +702,7 @@
      *
      * @hide
      */
-    public static final String GROUP_OWNER = SimInfo.GROUP_OWNER;
+    public static final String GROUP_OWNER = SimInfo.COLUMN_GROUP_OWNER;
 
     /**
      * TelephonyProvider column name for the profile class of a subscription
@@ -707,7 +710,7 @@
      * <P>Type: INTEGER (int)</P>
      * @hide
      */
-    public static final String PROFILE_CLASS = SimInfo.PROFILE_CLASS;
+    public static final String PROFILE_CLASS = SimInfo.COLUMN_PROFILE_CLASS;
 
     /**
      * Profile class of the subscription
@@ -719,7 +722,6 @@
             SimInfo.PROFILE_CLASS_PROVISIONING,
             SimInfo.PROFILE_CLASS_OPERATIONAL,
             SimInfo.PROFILE_CLASS_UNSET,
-            SimInfo.PROFILE_CLASS_DEFAULT
     })
     public @interface ProfileClass {}
 
@@ -765,7 +767,8 @@
      * @hide
      */
     @SystemApi
-    public static final int PROFILE_CLASS_DEFAULT = SimInfo.PROFILE_CLASS_DEFAULT;
+    @Deprecated
+    public static final int PROFILE_CLASS_DEFAULT = SimInfo.PROFILE_CLASS_UNSET;
 
     /**
      * IMSI (International Mobile Subscriber Identity).
@@ -773,19 +776,19 @@
      * @hide
      */
     //TODO: add @SystemApi
-    public static final String IMSI = SimInfo.IMSI;
+    public static final String IMSI = SimInfo.COLUMN_IMSI;
 
     /**
      * Whether uicc applications is set to be enabled or disabled. By default it's enabled.
      * @hide
      */
-    public static final String UICC_APPLICATIONS_ENABLED = SimInfo.UICC_APPLICATIONS_ENABLED;
+    public static final String UICC_APPLICATIONS_ENABLED = SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED;
 
     /**
      * Indicate which network type is allowed. By default it's enabled.
      * @hide
      */
-    public static final String ALLOWED_NETWORK_TYPES = SimInfo.ALLOWED_NETWORK_TYPES;
+    public static final String ALLOWED_NETWORK_TYPES = SimInfo.COLUMN_ALLOWED_NETWORK_TYPES;
 
     /**
      * Broadcast Action: The user has changed one of the default subs related to
@@ -945,6 +948,18 @@
             if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN");
         }
 
+        /**
+         * Callback invoked when {@link SubscriptionManager#addOnSubscriptionsChangedListener(
+         * Executor, OnSubscriptionsChangedListener)} or
+         * {@link SubscriptionManager#addOnSubscriptionsChangedListener(
+         * OnSubscriptionsChangedListener)} fails to complete due to the
+         * {@link Context#TELEPHONY_REGISTRY_SERVICE} being unavailable.
+         * @hide
+         */
+        public void onAddListenerFailed() {
+            Rlog.w(LOG_TAG, "onAddListenerFailed not overridden");
+        }
+
         private void log(String s) {
             Rlog.d(LOG_TAG, s);
         }
@@ -1025,6 +1040,12 @@
         if (telephonyRegistryManager != null) {
             telephonyRegistryManager.addOnSubscriptionsChangedListener(listener,
                     executor);
+        } else {
+            // If the telephony registry isn't available, we will inform the caller on their
+            // listener that it failed so they can try to re-register.
+            loge("addOnSubscriptionsChangedListener: pkgname=" + pkgName + " failed to be added "
+                    + " due to TELEPHONY_REGISTRY_SERVICE being unavailable.");
+            executor.execute(() -> listener.onAddListenerFailed());
         }
     }
 
@@ -2680,8 +2701,8 @@
      * @hide
      */
     @SystemApi
-    public boolean canManageSubscription(@Nullable SubscriptionInfo info,
-            @Nullable String packageName) {
+    public boolean canManageSubscription(@NonNull SubscriptionInfo info,
+            @NonNull String packageName) {
         if (info == null || info.getAllAccessRules() == null || packageName == null) {
             return false;
         }
@@ -3186,13 +3207,13 @@
      *
      * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
      *
-     * @param enabled whether uicc applications are enabled or disabled.
      * @param subscriptionId which subscription to operate on.
+     * @param enabled whether uicc applications are enabled or disabled.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
-    public void setUiccApplicationsEnabled(boolean enabled, int subscriptionId) {
+    public void setUiccApplicationsEnabled(int subscriptionId, boolean enabled) {
         if (VDBG) {
             logd("setUiccApplicationsEnabled subId= " + subscriptionId + " enable " + enabled);
         }
diff --git a/telephony/java/android/telephony/DisplayInfo.aidl b/telephony/java/android/telephony/TelephonyDisplayInfo.aidl
similarity index 95%
rename from telephony/java/android/telephony/DisplayInfo.aidl
rename to telephony/java/android/telephony/TelephonyDisplayInfo.aidl
index 861b0fe..ab41f93 100644
--- a/telephony/java/android/telephony/DisplayInfo.aidl
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.aidl
@@ -15,4 +15,4 @@
  */
 package android.telephony;
 
-parcelable DisplayInfo;
+parcelable TelephonyDisplayInfo;
diff --git a/telephony/java/android/telephony/DisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
similarity index 80%
rename from telephony/java/android/telephony/DisplayInfo.java
rename to telephony/java/android/telephony/TelephonyDisplayInfo.java
index d54bcf9..3d5c6aa 100644
--- a/telephony/java/android/telephony/DisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -25,12 +25,12 @@
 import java.util.Objects;
 
 /**
- * DisplayInfo contains telephony-related information used for display purposes only. This
+ * TelephonyDisplayInfo contains telephony-related information used for display purposes only. This
  * information is provided in accordance with carrier policy and branding preferences; it is not
  * necessarily a precise or accurate representation of the current state and should be treated
  * accordingly.
  */
-public final class DisplayInfo implements Parcelable {
+public final class TelephonyDisplayInfo implements Parcelable {
     /**
      * No override. {@link #getNetworkType()} should be used for display network
      * type.
@@ -62,8 +62,6 @@
      * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
      * capability or is currently connected to the secondary
      * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network on millimeter wave bands.
-     *
-     * @see AccessNetworkConstants.NgranBands#FREQUENCY_RANGE_GROUP_2
      */
     public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4;
 
@@ -81,13 +79,14 @@
      *
      * @hide
      */
-    public DisplayInfo(@NetworkType int networkType, @OverrideNetworkType int overrideNetworkType) {
+    public TelephonyDisplayInfo(@NetworkType int networkType,
+            @OverrideNetworkType int overrideNetworkType) {
         mNetworkType = networkType;
         mOverrideNetworkType = overrideNetworkType;
     }
 
     /** @hide */
-    public DisplayInfo(Parcel p) {
+    public TelephonyDisplayInfo(Parcel p) {
         mNetworkType = p.readInt();
         mOverrideNetworkType = p.readInt();
     }
@@ -121,16 +120,16 @@
         dest.writeInt(mOverrideNetworkType);
     }
 
-    public static final @NonNull Parcelable.Creator<DisplayInfo> CREATOR =
-            new Parcelable.Creator<DisplayInfo>() {
+    public static final @NonNull Parcelable.Creator<TelephonyDisplayInfo> CREATOR =
+            new Parcelable.Creator<TelephonyDisplayInfo>() {
                 @Override
-                public DisplayInfo createFromParcel(Parcel source) {
-                    return new DisplayInfo(source);
+                public TelephonyDisplayInfo createFromParcel(Parcel source) {
+                    return new TelephonyDisplayInfo(source);
                 }
 
                 @Override
-                public DisplayInfo[] newArray(int size) {
-                    return new DisplayInfo[size];
+                public TelephonyDisplayInfo[] newArray(int size) {
+                    return new TelephonyDisplayInfo[size];
                 }
             };
 
@@ -143,7 +142,7 @@
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
-        DisplayInfo that = (DisplayInfo) o;
+        TelephonyDisplayInfo that = (TelephonyDisplayInfo) o;
         return mNetworkType == that.mNetworkType
                 && mOverrideNetworkType == that.mOverrideNetworkType;
     }
@@ -153,7 +152,14 @@
         return Objects.hash(mNetworkType, mOverrideNetworkType);
     }
 
-    private static String overrideNetworkTypeToString(@OverrideNetworkType int type) {
+    /**
+     * Convert override network type to string.
+     *
+     * @param type Override network type
+     * @return Override network type in string format
+     * @hide
+     */
+    public static String overrideNetworkTypeToString(@OverrideNetworkType int type) {
         switch (type) {
             case OVERRIDE_NETWORK_TYPE_NONE: return "NONE";
             case OVERRIDE_NETWORK_TYPE_LTE_CA: return "LTE_CA";
@@ -166,7 +172,7 @@
 
     @Override
     public String toString() {
-        return "DisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType)
-                + ", override=" + overrideNetworkTypeToString(mOverrideNetworkType);
+        return "TelephonyDisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType)
+                + ", override=" + overrideNetworkTypeToString(mOverrideNetworkType) + "}";
     }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bfe9152..7d1398b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -53,6 +53,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -69,13 +70,13 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.CallForwardingReason;
 import android.telephony.Annotation.CallState;
-import android.telephony.Annotation.CallWaitingStatus;
+import android.telephony.Annotation.CarrierPrivilegeStatus;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SimActivationState;
 import android.telephony.Annotation.UiccAppType;
+import android.telephony.CallForwardingInfo.CallForwardingReason;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
 import android.telephony.data.ApnSetting;
 import android.telephony.data.ApnSetting.MvnoType;
@@ -162,7 +163,6 @@
      * into the ResultReceiver Bundle.
      * @hide
      */
-    @SystemApi
     public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
 
     /**
@@ -316,21 +316,6 @@
     };
 
     /** @hide */
-    @IntDef(prefix = {"MODEM_COUNT_"},
-            value = {
-                    MODEM_COUNT_NO_MODEM,
-                    MODEM_COUNT_SINGLE_MODEM,
-                    MODEM_COUNT_DUAL_MODEM,
-                    MODEM_COUNT_TRI_MODEM
-            })
-    public @interface ModemCount {}
-
-    public static final int MODEM_COUNT_NO_MODEM     = 0;
-    public static final int MODEM_COUNT_SINGLE_MODEM = 1;
-    public static final int MODEM_COUNT_DUAL_MODEM   = 2;
-    public static final int MODEM_COUNT_TRI_MODEM    = 3;
-
-    /** @hide */
     @UnsupportedAppUsage
     public TelephonyManager(Context context) {
       this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
@@ -438,22 +423,22 @@
      * Returns 2 for Dual standby mode (Dual SIM functionality).
      * Returns 3 for Tri standby mode (Tri SIM functionality).
      */
-    public @ModemCount int getActiveModemCount() {
+    public int getActiveModemCount() {
         int modemCount = 1;
         switch (getMultiSimConfiguration()) {
             case UNKNOWN:
-                modemCount = MODEM_COUNT_SINGLE_MODEM;
+                modemCount = 1;
                 // check for voice and data support, 0 if not supported
                 if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
-                    modemCount = MODEM_COUNT_NO_MODEM;
+                    modemCount = 0;
                 }
                 break;
             case DSDS:
             case DSDA:
-                modemCount = MODEM_COUNT_DUAL_MODEM;
+                modemCount = 2;
                 break;
             case TSTS:
-                modemCount = MODEM_COUNT_TRI_MODEM;
+                modemCount = 3;
                 break;
         }
         return modemCount;
@@ -466,7 +451,7 @@
      * dual-SIM capable device operating in single SIM mode (only one logical modem is turned on),
      * {@link #getActiveModemCount} returns 1 while this API returns 2.
      */
-    public @ModemCount int getSupportedModemCount() {
+    public int getSupportedModemCount() {
         return TelephonyProperties.max_active_modems().orElse(getActiveModemCount());
     }
 
@@ -1023,14 +1008,16 @@
             "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
 
     /**
-     * Broadcast intent that indicates multi-SIM configuration is changed. For example, it changed
+     * Broadcast action to be received by Broadcast receivers.
+     *
+     * Indicates multi-SIM configuration is changed. For example, it changed
      * from single SIM capable to dual-SIM capable (DSDS or DSDA) or triple-SIM mode.
      *
      * It doesn't indicate how many subscriptions are actually active, or which states SIMs are,
      * or that all steps during multi-SIM change are done. To know those information you still need
      * to listen to SIM_STATE changes or active subscription changes.
      *
-     * See extra of {@link #EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED} for updated value.
+     * See extra of {@link #EXTRA_ACTIVE_SIM_SUPPORTED_COUNT} for updated value.
      */
     public static final String ACTION_MULTI_SIM_CONFIG_CHANGED =
             "android.telephony.action.MULTI_SIM_CONFIG_CHANGED";
@@ -1040,6 +1027,8 @@
      * The number of active SIM supported by current multi-SIM config. It's not related to how many
      * SIM/subscriptions are currently active.
      *
+     * Same value will be returned by {@link #getActiveModemCount()}.
+     *
      * For single SIM mode, it's 1.
      * For DSDS or DSDA mode, it's 2.
      * For triple-SIM mode, it's 3.
@@ -1048,8 +1037,8 @@
      *
      * type: integer
      */
-    public static final String EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED =
-            "android.telephony.extra.NUM_OF_ACTIVE_SIM_SUPPORTED";
+    public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT =
+            "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
 
     /**
      * @hide
@@ -1264,7 +1253,6 @@
      * <p>Note: this is a protected intent that can only be sent by the system.
      * @hide
      */
-    @SystemApi
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_SERVICE_PROVIDERS_UPDATED =
             "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
@@ -1274,7 +1262,6 @@
      * whether the PLMN should be shown.
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
 
     /**
@@ -1282,7 +1269,6 @@
      * the operator name of the registered network.
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
 
     /**
@@ -1290,7 +1276,6 @@
      * whether the PLMN should be shown.
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
 
     /**
@@ -1298,7 +1283,6 @@
      * the service provider name.
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_SPN = "android.telephony.extra.SPN";
 
     /**
@@ -1306,7 +1290,6 @@
      * the service provider name for data service.
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
 
     /**
@@ -1427,7 +1410,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE =
             "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
 
@@ -1447,7 +1429,6 @@
      * to indicate there's no need to re-select any default subscription.
      * @hide
      */
-    @SystemApi
     public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE = 0;
 
     /**
@@ -1455,7 +1436,6 @@
      * to indicate there's a need to select default data subscription.
      * @hide
      */
-    @SystemApi
     public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA = 1;
 
     /**
@@ -1463,7 +1443,6 @@
      * to indicate there's a need to select default voice call subscription.
      * @hide
      */
-    @SystemApi
     public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE = 2;
 
     /**
@@ -1471,7 +1450,6 @@
      * to indicate there's a need to select default sms subscription.
      * @hide
      */
-    @SystemApi
     public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS = 3;
 
     /**
@@ -1481,7 +1459,6 @@
      * which subscription should be the default subscription.
      * @hide
      */
-    @SystemApi
     public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4;
 
     /**
@@ -1491,7 +1468,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE =
             "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
 
@@ -1508,7 +1484,6 @@
      * to indicate there's no SIM combination warning.
      * @hide
      */
-    @SystemApi
     public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0;
 
     /**
@@ -1516,7 +1491,6 @@
      * to indicate two active SIMs are both CDMA hence there might be functional limitation.
      * @hide
      */
-    @SystemApi
     public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1;
 
     /**
@@ -1527,7 +1501,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_SIM_COMBINATION_NAMES =
             "android.telephony.extra.SIM_COMBINATION_NAMES";
 
@@ -2300,7 +2273,12 @@
     public static final int PHONE_TYPE_CDMA = PhoneConstants.PHONE_TYPE_CDMA;
     /** Phone is via SIP. */
     public static final int PHONE_TYPE_SIP = PhoneConstants.PHONE_TYPE_SIP;
-    /** Phone is via IMS. */
+
+    /**
+     * Phone is via IMS.
+     *
+     * @hide
+     */
     public static final int PHONE_TYPE_IMS = PhoneConstants.PHONE_TYPE_IMS;
 
     /**
@@ -2308,7 +2286,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int PHONE_TYPE_THIRD_PARTY = PhoneConstants.PHONE_TYPE_THIRD_PARTY;
 
     /**
@@ -2705,39 +2682,22 @@
     /**
      * Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current
      * registered operator or the cell nearby, if available.
-     * <p>
-     * The ISO-3166 country code is provided in lowercase 2 character format.
-     * <p>
-     * Note: In multi-sim, this returns a shared emergency network country iso from other
-     * subscription if the subscription used to create the TelephonyManager doesn't camp on
-     * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
-     * slot.
+     *
      * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
      * if on a CDMA network).
      * <p>
      * @return the lowercase 2 character ISO-3166 country code, or empty string if not available.
      */
     public String getNetworkCountryIso() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony == null) return "";
-            return telephony.getNetworkCountryIsoForPhone(getPhoneId(),
-                    null /* no permission check */, null);
-        } catch (RemoteException ex) {
-            return "";
-        }
+        return getNetworkCountryIso(getSlotIndex());
     }
 
     /**
      * Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current
-     * registered operator or the cell nearby, if available.
-     * <p>
-     * The ISO-3166 country code is provided in lowercase 2 character format.
-     * <p>
-     * Note: In multi-sim, this returns a shared emergency network country iso from other
-     * subscription if the subscription used to create the TelephonyManager doesn't camp on
-     * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
-     * slot.
+     * registered operator or the cell nearby, if available. This is same as
+     * {@link #getNetworkCountryIso()} but allowing specifying the SIM slot index. This is used for
+     * accessing network country info from the SIM slot that does not have SIM inserted.
+     *
      * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
      * if on a CDMA network).
      * <p>
@@ -2748,27 +2708,34 @@
      *
      * @throws IllegalArgumentException when the slotIndex is invalid.
      *
-     * {@hide}
      */
-    @SystemApi
-    @TestApi
     @NonNull
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getNetworkCountryIso(int slotIndex) {
         try {
-            if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+            if (slotIndex != SubscriptionManager.DEFAULT_SIM_SLOT_INDEX
+                    && !SubscriptionManager.isValidSlotIndex(slotIndex)) {
                 throw new IllegalArgumentException("invalid slot index " + slotIndex);
             }
 
             ITelephony telephony = getITelephony();
             if (telephony == null) return "";
-            return telephony.getNetworkCountryIsoForPhone(slotIndex, getOpPackageName(),
-                    getFeatureId());
+            return telephony.getNetworkCountryIsoForPhone(slotIndex);
         } catch (RemoteException ex) {
             return "";
         }
     }
 
+    /**
+     * @hide
+     * @deprecated Use {@link #getNetworkCountryIso(int)} instead.
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getNetworkCountryIso(int)} instead.")
+    public String getNetworkCountryIsoForPhone(int phoneId) {
+        return getNetworkCountryIso(phoneId);
+    }
+
     /*
      * When adding a network type to the list below, make sure to add the correct icon to
      * MobileSignalController.mapIconSets() as well as NETWORK_TYPES
@@ -3013,64 +2980,6 @@
     }
 
     /**
-     * Network Class Definitions.
-     * Do not change this order, it is used for sorting during emergency calling in
-     * {@link TelephonyConnectionService#getFirstPhoneForEmergencyCall()}. Any newer technologies
-     * should be added after the current definitions.
-     */
-    /** Unknown network class. {@hide} */
-    public static final int NETWORK_CLASS_UNKNOWN = 0;
-    /** Class of broadly defined "2G" networks. {@hide} */
-    @UnsupportedAppUsage
-    public static final int NETWORK_CLASS_2_G = 1;
-    /** Class of broadly defined "3G" networks. {@hide} */
-    @UnsupportedAppUsage
-    public static final int NETWORK_CLASS_3_G = 2;
-    /** Class of broadly defined "4G" networks. {@hide} */
-    @UnsupportedAppUsage
-    public static final int NETWORK_CLASS_4_G = 3;
-    /** Class of broadly defined "5G" networks. {@hide} */
-    public static final int NETWORK_CLASS_5_G = 4;
-
-    /**
-     * Return general class of network type, such as "3G" or "4G". In cases
-     * where classification is contentious, this method is conservative.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static int getNetworkClass(int networkType) {
-        switch (networkType) {
-            case NETWORK_TYPE_GPRS:
-            case NETWORK_TYPE_GSM:
-            case NETWORK_TYPE_EDGE:
-            case NETWORK_TYPE_CDMA:
-            case NETWORK_TYPE_1xRTT:
-            case NETWORK_TYPE_IDEN:
-                return NETWORK_CLASS_2_G;
-            case NETWORK_TYPE_UMTS:
-            case NETWORK_TYPE_EVDO_0:
-            case NETWORK_TYPE_EVDO_A:
-            case NETWORK_TYPE_HSDPA:
-            case NETWORK_TYPE_HSUPA:
-            case NETWORK_TYPE_HSPA:
-            case NETWORK_TYPE_EVDO_B:
-            case NETWORK_TYPE_EHRPD:
-            case NETWORK_TYPE_HSPAP:
-            case NETWORK_TYPE_TD_SCDMA:
-                return NETWORK_CLASS_3_G;
-            case NETWORK_TYPE_LTE:
-            case NETWORK_TYPE_IWLAN:
-            case NETWORK_TYPE_LTE_CA:
-                return NETWORK_CLASS_4_G;
-            case NETWORK_TYPE_NR:
-                return NETWORK_CLASS_5_G;
-            default:
-                return NETWORK_CLASS_UNKNOWN;
-        }
-    }
-
-    /**
      * Returns a string representation of the radio technology (network type)
      * currently in use on the device.
      * @return the name of the radio technology
@@ -3852,19 +3761,19 @@
     }
 
     /**
-     * Return if the current radio has global mode enabled, meaning it supports
-     * both 3GPP and 3GPP2 radio technologies at the same time.
+     * Return if the current radio can support both 3GPP and 3GPP2 radio technologies at the same
+     * time. This is also known as global mode, which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
-     * @return {@code true} if global mode is enabled
-     *         {@code false} if global mode is not enabled or unknown
+     * @return {@code true} if 3GPP and 3GPP2 radio technologies can be supported at the same time
+     *         {@code false} if not supported or unknown
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isGlobalModeEnabled() {
+    public boolean isLteCdmaEvdoGsmWcdmaEnabled() {
         return getLteOnCdmaMode(getSubId()) == PhoneConstants.LTE_ON_CDMA_TRUE;
     }
 
@@ -4318,14 +4227,18 @@
 
     /**
      * Returns the phone number string for line 1, for example, the MSISDN
-     * for a GSM phone. Return null if it is unavailable.
+     * for a GSM phone for a particular subscription. Return null if it is unavailable.
+     * <p>
+     * The default SMS app can also use this.
      *
      * <p>Requires Permission:
-     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE},
      *     {@link android.Manifest.permission#READ_SMS READ_SMS},
      *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
      *     that the caller is the default SMS app,
-     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+     *     for any API level.
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *     for apps targeting SDK API level 29 and below.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
     @RequiresPermission(anyOf = {
@@ -4343,6 +4256,15 @@
      * <p>
      * The default SMS app can also use this.
      *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+     *     for any API level.
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *     for apps targeting SDK API level 29 and below.
+     *
      * @param subId whose phone number for line 1 is returned
      * @hide
      */
@@ -4521,25 +4443,50 @@
     }
 
     /**
-     * Returns the MSISDN string.
-     * for a GSM phone. Return null if it is unavailable.
+     * Returns the MSISDN string for a GSM phone. Return null if it is unavailable.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+     *     for any API level.
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *     for apps targeting SDK API level 29 and below.
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_SMS,
+            android.Manifest.permission.READ_PHONE_NUMBERS
+    })
     @UnsupportedAppUsage
     public String getMsisdn() {
         return getMsisdn(getSubId());
     }
 
     /**
-     * Returns the MSISDN string.
-     * for a GSM phone. Return null if it is unavailable.
+     * Returns the MSISDN string for a GSM phone. Return null if it is unavailable.
      *
      * @param subId for which msisdn is returned
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges})
+     *     for any API level.
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *     for apps targeting SDK API level 29 and below.
+     *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_SMS,
+            android.Manifest.permission.READ_PHONE_NUMBERS
+    })
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getMsisdn(int subId) {
         try {
@@ -4877,7 +4824,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 telephony.sendVisualVoicemailSmsForSubscriber(
-                        mContext.getOpPackageName(), subId, number, port, text, sentIntent);
+                        mContext.getOpPackageName(), null, subId, number, port, text, sentIntent);
             }
         } catch (RemoteException ex) {
         }
@@ -5270,8 +5217,8 @@
      *      not present or not loaded
      * @hide
      */
+    @UnsupportedAppUsage
     @Nullable
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String[] getIsimImpu() {
         try {
@@ -5568,6 +5515,10 @@
      * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
      * {@link SecurityException} will be thrown otherwise.
      *
+     * This API should be used sparingly -- large numbers of listeners will cause system
+     * instability. If a process has registered too many listeners without unregistering them, it
+     * may encounter an {@link IllegalStateException} when trying to register more listeners.
+     *
      * @param listener The {@link PhoneStateListener} object to register
      *                 (or unregister)
      * @param events The telephony state(s) of interest to the listener,
@@ -7692,6 +7643,17 @@
             RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
 
     /**
+     * The default preferred network mode constant.
+     *
+     * <p> This constant is used in case of nothing is set in
+     * TelephonyProperties#default_network().
+     *
+     * @hide
+     */
+    public static final int DEFAULT_PREFERRED_NETWORK_MODE =
+            RILConstants.PREFERRED_NETWORK_MODE;
+
+    /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
@@ -7935,21 +7897,19 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param operatorNumeric the PLMN ID of the network to select.
-     * @param ran the initial suggested radio access network type.
-     *         If registration fails, the RAN is not available after, the RAN is not within the
-     *         network types specified by {@link #setPreferredNetworkTypeBitmask}, or the value is
-     *         {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
-     *         the next best RAN for network registration.
      * @param persistSelection whether the selection will persist until reboot.
      *         If true, only allows attaching to the selected PLMN until reboot; otherwise,
      *         attach to the chosen PLMN and resume normal network selection next time.
+     * @param ran the initial suggested radio access network type.
+     *         If registration fails, the RAN is not available after, the RAN is not within the
+     *         network types specified by the preferred network types, or the value is
+     *         {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
+     *         the next best RAN for network registration.
      * @return {@code true} on success; {@code false} on any failure.
-     * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    @SystemApi
     public boolean setNetworkSelectionModeManual(@NonNull String operatorNumeric,
-            @AccessNetworkConstants.RadioAccessNetworkType int ran, boolean persistSelection) {
+            boolean persistSelection, @AccessNetworkConstants.RadioAccessNetworkType int ran) {
         return setNetworkSelectionModeManual(new OperatorInfo("" /* operatorAlphaLong */,
                 "" /* operatorAlphaShort */, operatorNumeric, ran), persistSelection);
     }
@@ -8647,13 +8607,9 @@
         return false;
     }
 
-    /**
-     * @deprecated use {@link #supplyPinReportPinResult(String pin)} instead.
-     *
-     * @hide */
+    /** @hide */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    @Deprecated
     public int[] supplyPinReportResult(String pin) {
         try {
             ITelephony telephony = getITelephony();
@@ -8665,13 +8621,9 @@
         return new int[0];
     }
 
-    /**
-     * @deprecated use {@link #supplyPukReportPinResult(String puk, String pin)} instead.
-     *
-     * @hide */
+    /** @hide */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    @Deprecated
     public int[] supplyPukReportResult(String puk, String pin) {
         try {
             ITelephony telephony = getITelephony();
@@ -8691,7 +8643,6 @@
      *
      * @hide
      */
-    @SystemApi
     @Nullable
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public PinResult supplyPinReportPinResult(@NonNull String pin) {
@@ -8716,7 +8667,6 @@
      *
      * @hide
      */
-    @SystemApi
     @Nullable
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public PinResult supplyPukReportPinResult(@NonNull String puk, @NonNull String pin) {
@@ -8908,7 +8858,10 @@
     }
 
     /**
-     * Shut down all the live radios over all the slot index.
+     * Shut down all the live radios over all the slot indexes.
+     *
+     * <p>To know when the radio has completed powering off, use
+     * {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}.
      *
      * @hide
      */
@@ -8921,7 +8874,8 @@
                 telephony.shutdownMobileRadios();
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#shutdownMobileRadios", e);
+            Log.e(TAG, "Error calling ITelephony#shutdownAllRadios", e);
+            e.rethrowAsRuntimeException();
         }
     }
 
@@ -8940,7 +8894,8 @@
                 return telephony.needMobileRadioShutdown();
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#needMobileRadioShutdown", e);
+            Log.e(TAG, "Error calling ITelephony#isAnyRadioPoweredOn", e);
+            e.rethrowAsRuntimeException();
         }
         return false;
     }
@@ -9170,7 +9125,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @CdmaRoamingMode int getCdmaRoamingMode() {
         int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
@@ -9199,7 +9153,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) {
         try {
@@ -9225,19 +9178,16 @@
     /** Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
      * @hide
      */
-    @SystemApi
     public static final int CDMA_SUBSCRIPTION_UNKNOWN  = -1;
 
     /** Used for CDMA subscription mode: RUIM/SIM (default)
      * @hide
      */
-    @SystemApi
     public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
 
     /** Used for CDMA subscription mode: NV -> non-volatile memory
      * @hide
      */
-    @SystemApi
     public static final int CDMA_SUBSCRIPTION_NV       = 1;
 
     /** @hide */
@@ -9256,7 +9206,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) {
         try {
@@ -9694,14 +9643,12 @@
      * Powers down the SIM. SIM must be up prior.
      * @hide
      */
-    @SystemApi
     public static final int CARD_POWER_DOWN = 0;
 
     /**
      * Powers up the SIM normally. SIM must be down prior.
      * @hide
      */
-    @SystemApi
     public static final int CARD_POWER_UP = 1;
 
     /**
@@ -9719,7 +9666,6 @@
      * is NOT persistent across boots. On reboot, SIM will power up normally.
      * @hide
      */
-    @SystemApi
     public static final int CARD_POWER_UP_PASS_THROUGH = 2;
 
     /**
@@ -9813,32 +9759,12 @@
     }
 
     /**
-     * Get baseband version for the default phone using the legacy approach.
-     * This change was added in P, to ensure backward compatiblity.
-     *
-     * @return baseband version.
-     * @hide
-     */
-    private String getBasebandVersionLegacy(int phoneId) {
-        if (SubscriptionManager.isValidPhoneId(phoneId)) {
-            String prop = "gsm.version.baseband"
-                    + ((phoneId == 0) ? "" : Integer.toString(phoneId));
-            return SystemProperties.get(prop);
-        }
-        return null;
-    }
-
-    /**
      * Get baseband version by phone id.
      *
      * @return baseband version.
      * @hide
      */
     public String getBasebandVersionForPhone(int phoneId) {
-        String version = getBasebandVersionLegacy(phoneId);
-        if (version != null && !version.isEmpty()) {
-            setBasebandVersionForPhone(phoneId, version);
-        }
         return getTelephonyProperty(phoneId, TelephonyProperties.baseband_version(), "");
     }
 
@@ -10235,7 +10161,6 @@
      * {@link #MODEM_ACTIVITY_RESULT_KEY}.
      * @hide
      */
-    @SystemApi
     public void requestModemActivityInfo(@NonNull ResultReceiver result) {
         try {
             ITelephony service = getITelephony();
@@ -10389,7 +10314,7 @@
      * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
      * carrier with a canonical integer a.k.a. carrier id. The carrier ID is an Android
      * platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
-     * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+     * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
      *
      * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
      * as an Android platform-wide identifier for carriers.
@@ -10451,7 +10376,7 @@
      *
      * <p>For carriers without fine-grained specific carrier ids, return {@link #getSimCarrierId()}
      * <p>Specific carrier ids are defined in the same way as carrier id
-     * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+     * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
      * except each with a "parent" id linking to its top-level carrier id.
      *
      * @return Returns fine-grained carrier id of the current subscription.
@@ -10500,7 +10425,7 @@
      * This is used for fallback when configurations/logic for exact carrier id
      * {@link #getSimCarrierId()} are not found.
      *
-     * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+     * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
      * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
      * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
      * by default. After carrier id table update, a new carrier id was assigned. If apps don't
@@ -10527,7 +10452,7 @@
       * used for fallback when configurations/logic for exact carrier id {@link #getSimCarrierId()}
       * are not found.
       *
-      * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+      * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a>
       * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier
       * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id
       * by default. After carrier id table update, a new carrier id was assigned. If apps don't
@@ -11074,7 +10999,6 @@
      * @param isEnabled {@code true} for enabling; {@code false} for disabling.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setAlwaysReportSignalStrength(boolean isEnabled) {
         try {
@@ -11109,21 +11033,21 @@
     }
 
     /**
-     * Checks whether cellular data connection is enabled in the device.
+     * Checks whether cellular data connection is allowed in the device.
      *
-     * Whether cellular data connection is enabled, meaning upon request whether will try to setup
-     * metered data connection considering all factors below:
-     * 1) User turned on data setting {@link #isDataEnabled}.
-     * 2) Carrier allows data to be on.
-     * 3) Network policy.
-     * And possibly others.
-     *
-     * @return {@code true} if the overall data connection is capable; {@code false} if not.
+     * <p>Whether cellular data connection is allowed considers all factors below:
+     * <UL>
+     *   <LI>User turned on data setting {@link #isDataEnabled}.</LI>
+     *   <LI>Carrier allows data to be on.</LI>
+     *   <LI>Network policy.</LI>
+     *   <LI>And possibly others.</LI>
+     * </UL>
+     * @return {@code true} if the overall data connection is allowed; {@code false} if not.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isDataConnectionEnabled() {
+    public boolean isDataConnectionAllowed() {
         boolean retVal = false;
         try {
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -11131,8 +11055,7 @@
             if (telephony != null)
                 retVal = telephony.isDataEnabled(subId);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error isDataConnectionEnabled", e);
-        } catch (NullPointerException e) {
+            Log.e(TAG, "Error isDataConnectionAllowed", e);
         }
         return retVal;
     }
@@ -11147,6 +11070,8 @@
      * PackageManager.FEATURE_TELEPHONY system feature, which is available
      * on any device with a telephony radio, even if the device is
      * voice-only.
+     *
+     * @hide
      */
     public boolean isDataCapable() {
         if (mContext == null) return true;
@@ -11166,14 +11091,6 @@
      */
     public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF        = 2;
 
-    /** @hide */
-    @IntDef(prefix = { "INDICATION_UPDATE_MODE_" }, value = {
-            INDICATION_UPDATE_MODE_NORMAL,
-            INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface IndicationUpdateMode{}
-
     /**
      * The indication for signal strength update.
      * @hide
@@ -11204,51 +11121,6 @@
      */
     public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG       = 0x10;
 
-    /** @hide */
-    @IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = {
-            INDICATION_FILTER_SIGNAL_STRENGTH,
-            INDICATION_FILTER_FULL_NETWORK_STATE,
-            INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED,
-            INDICATION_FILTER_LINK_CAPACITY_ESTIMATE,
-            INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface IndicationFilters{}
-
-    /**
-     * Sets radio indication update mode. This can be used to control the behavior of indication
-     * update from modem to Android frameworks. For example, by default several indication updates
-     * are turned off when screen is off, but in some special cases (e.g. carkit is connected but
-     * screen is off) we want to turn on those indications even when the screen is off.
-     *
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     *
-     * @param filters Indication filters. Should be a bitmask of INDICATION_FILTER_XXX.
-     * @see #INDICATION_FILTER_SIGNAL_STRENGTH
-     * @see #INDICATION_FILTER_FULL_NETWORK_STATE
-     * @see #INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
-     * @param updateMode The voice activation state
-     * @see #INDICATION_UPDATE_MODE_NORMAL
-     * @see #INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setRadioIndicationUpdateMode(@IndicationFilters int filters,
-                                             @IndicationUpdateMode int updateMode) {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                telephony.setRadioIndicationUpdateMode(getSubId(), filters, updateMode);
-            }
-        } catch (RemoteException ex) {
-            // This could happen if binder process crashes.
-            if (!isSystemProcess()) {
-                ex.rethrowAsRuntimeException();
-            }
-        }
-    }
-
     /**
      * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
      * plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config
@@ -11555,6 +11427,55 @@
     @SystemApi
     public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1));
 
+    /** @hide */
+    public static final long NETWORK_CLASS_BITMASK_2G = NETWORK_TYPE_BITMASK_GSM
+                | NETWORK_TYPE_BITMASK_GPRS
+                | NETWORK_TYPE_BITMASK_EDGE
+                | NETWORK_TYPE_BITMASK_CDMA
+                | NETWORK_TYPE_BITMASK_1xRTT;
+
+    /** @hide */
+    public static final long NETWORK_CLASS_BITMASK_3G = NETWORK_TYPE_BITMASK_EVDO_0
+            | NETWORK_TYPE_BITMASK_EVDO_A
+            | NETWORK_TYPE_BITMASK_EVDO_B
+            | NETWORK_TYPE_BITMASK_EHRPD
+            | NETWORK_TYPE_BITMASK_HSUPA
+            | NETWORK_TYPE_BITMASK_HSDPA
+            | NETWORK_TYPE_BITMASK_HSPA
+            | NETWORK_TYPE_BITMASK_HSPAP
+            | NETWORK_TYPE_BITMASK_UMTS
+            | NETWORK_TYPE_BITMASK_TD_SCDMA;
+
+    /** @hide */
+    public static final long NETWORK_CLASS_BITMASK_4G = NETWORK_TYPE_BITMASK_LTE
+            | NETWORK_TYPE_BITMASK_LTE_CA
+            | NETWORK_TYPE_BITMASK_IWLAN;
+
+    /** @hide */
+    public static final long NETWORK_CLASS_BITMASK_5G = NETWORK_TYPE_BITMASK_NR;
+
+    /** @hide */
+    public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP = NETWORK_TYPE_BITMASK_GSM
+            | NETWORK_TYPE_BITMASK_GPRS
+            | NETWORK_TYPE_BITMASK_EDGE
+            | NETWORK_TYPE_BITMASK_HSUPA
+            | NETWORK_TYPE_BITMASK_HSDPA
+            | NETWORK_TYPE_BITMASK_HSPA
+            | NETWORK_TYPE_BITMASK_HSPAP
+            | NETWORK_TYPE_BITMASK_UMTS
+            | NETWORK_TYPE_BITMASK_TD_SCDMA
+            | NETWORK_TYPE_BITMASK_LTE
+            | NETWORK_TYPE_BITMASK_LTE_CA
+            | NETWORK_TYPE_BITMASK_NR;
+
+    /** @hide */
+    public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2 = NETWORK_TYPE_BITMASK_CDMA
+            | NETWORK_TYPE_BITMASK_1xRTT
+            | NETWORK_TYPE_BITMASK_EVDO_0
+            | NETWORK_TYPE_BITMASK_EVDO_A
+            | NETWORK_TYPE_BITMASK_EVDO_B
+            | NETWORK_TYPE_BITMASK_EHRPD;
+
     /**
      * @return Modem supported radio access family bitmask
      *
@@ -11616,11 +11537,9 @@
     }
 
     /**
-     * Override the file path for testing OTA emergency number database in a file partition.
+     * Override the file path for OTA emergency number database in a file partition.
      *
-     * @param otaFilePath The test OTA emergency number database file path;
-     *                    if "RESET", recover the original database file partition.
-     *                    Format: <root file folder>@<file path>
+     * @param otaParcelFileDescriptor parcelable file descriptor for OTA emergency number database.
      *
      * <p> Requires permission:
      * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
@@ -11630,16 +11549,42 @@
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     @SystemApi
     @TestApi
-    public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String otaFilePath) {
+    public void updateOtaEmergencyNumberDbFilePath(
+            @NonNull ParcelFileDescriptor otaParcelFileDescriptor) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                telephony.updateTestOtaEmergencyNumberDbFilePath(otaFilePath);
+                telephony.updateOtaEmergencyNumberDbFilePath(otaParcelFileDescriptor);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            Log.e(TAG, "notifyOtaEmergencyNumberDatabaseInstalled RemoteException", ex);
+            Log.e(TAG, "updateOtaEmergencyNumberDbFilePath RemoteException", ex);
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Reset the file path to default for OTA emergency number database in a file partition.
+     *
+     * <p> Requires permission:
+     * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+    @SystemApi
+    @TestApi
+    public void resetOtaEmergencyNumberDbFilePath() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.resetOtaEmergencyNumberDbFilePath();
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "resetOtaEmergencyNumberDbFilePath RemoteException", ex);
             ex.rethrowAsRuntimeException();
         }
     }
@@ -11843,7 +11788,7 @@
     }
 
     /**
-     * A test API to return the emergency number db version.
+     * Returns the emergency number database version.
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
@@ -11852,6 +11797,7 @@
      */
     @TestApi
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public int getEmergencyNumberDbVersion() {
         try {
             ITelephony telephony = getITelephony();
@@ -12224,6 +12170,17 @@
             "android.telephony.extra.NETWORK_COUNTRY";
 
     /**
+     * The extra used with an {@link #ACTION_NETWORK_COUNTRY_CHANGED} to specify the
+     * last known the country code in ISO-3166-1 alpha-2 format.
+     * <p class="note">
+     * Retrieve with {@link android.content.Intent#getStringExtra(String)}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_LAST_KNOWN_NETWORK_COUNTRY =
+            "android.telephony.extra.LAST_KNOWN_NETWORK_COUNTRY";
+
+    /**
      * Indicate if the user is allowed to use multiple SIM cards at the same time to register
      * on the network (e.g. Dual Standby or Dual Active) when the device supports it, or if the
      * usage is restricted. This API is used to prevent usage of multiple SIM card, based on
@@ -12293,6 +12250,9 @@
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @IsMultiSimSupportedResult
     public int isMultiSimSupported() {
+        if (getSupportedModemCount() < 2) {
+            return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE;
+        }
         try {
             ITelephony service = getITelephony();
             if (service != null) {
@@ -12395,7 +12355,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public int getCarrierPrivilegeStatus(int uid) {
+    public @CarrierPrivilegeStatus int getCarrierPrivilegeStatus(int uid) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
@@ -12641,7 +12601,6 @@
     }
 
     /**
-<<<<<<< HEAD
      * Gets the voice call forwarding info {@link CallForwardingInfo}, given the call forward
      * reason.
      *
@@ -12661,7 +12620,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @NonNull
     public CallForwardingInfo getCallForwarding(@CallForwardingReason int callForwardingReason) {
@@ -12708,7 +12666,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo) {
         if (callForwardingInfo == null) {
@@ -12749,7 +12706,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int CALL_WAITING_STATUS_ACTIVE = 1;
 
     /**
@@ -12757,7 +12713,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int CALL_WAITING_STATUS_INACTIVE = 2;
 
     /**
@@ -12765,7 +12720,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3;
 
     /**
@@ -12773,10 +12727,24 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4;
 
     /**
+     * Call waiting function status
+     *
+     * @hide
+     */
+    @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
+        CALL_WAITING_STATUS_ACTIVE,
+        CALL_WAITING_STATUS_INACTIVE,
+        CALL_WAITING_STATUS_NOT_SUPPORTED,
+        CALL_WAITING_STATUS_UNKNOWN_ERROR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallWaitingStatus {
+    }
+
+    /**
      * Gets the status of voice call waiting function. Call waiting function enables the waiting
      * for the incoming call when it reaches the user who is busy to make another call and allows
      * users to decide whether to switch to the incoming call.
@@ -12784,7 +12752,6 @@
      * @return the status of call waiting function.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @CallWaitingStatus int getCallWaitingStatus() {
         try {
@@ -12810,7 +12777,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setCallWaitingStatus(boolean isEnable) {
         try {
@@ -12841,7 +12807,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setDataAllowedDuringVoiceCall(boolean allow) {
         try {
@@ -12870,7 +12835,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isDataAllowedInVoiceCall() {
         try {
@@ -12917,7 +12881,6 @@
      * The IccLock state or password was changed successfully.
      * @hide
      */
-    @SystemApi
     public static final int CHANGE_ICC_LOCK_SUCCESS = Integer.MAX_VALUE;
 
     /**
@@ -12930,9 +12893,9 @@
      *
      * @hide
      */
-    @SystemApi
     @WorkerThread
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
     public boolean isIccLockEnabled() {
         try {
             ITelephony telephony = getITelephony();
@@ -12967,7 +12930,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public int setIccLockEnabled(boolean enabled, @NonNull String password) {
         checkNotNull(password, "setIccLockEnabled password can't be null.");
@@ -13001,7 +12963,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public int changeIccLockPassword(@NonNull String oldPassword, @NonNull String newPassword) {
         checkNotNull(oldPassword, "changeIccLockPassword oldPassword can't be null.");
@@ -13016,4 +12977,21 @@
         }
         return 0;
     }
+
+    /**
+     * Whether device can connect to 5G network when two SIMs are active.
+     * @hide
+     * TODO b/153669716: remove or make system API.
+     */
+    public boolean canConnectTo5GInDsdsMode() {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) return true;
+        try {
+            return telephony.canConnectTo5GInDsdsMode();
+        } catch (RemoteException ex) {
+            return true;
+        } catch (NullPointerException ex) {
+            return true;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index 3e948fc..12bb366 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -36,6 +36,8 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -177,17 +179,8 @@
      *     {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
      */
     public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
-        Signature[] signatures = packageInfo.signatures;
-        SigningInfo sInfo = packageInfo.signingInfo;
-
-        if (sInfo != null) {
-            signatures = sInfo.getSigningCertificateHistory();
-            if (sInfo.hasMultipleSigners()) {
-                signatures = sInfo.getApkContentsSigners();
-            }
-        }
-
-        if (signatures == null || signatures.length == 0) {
+        List<Signature> signatures = getSignatures(packageInfo);
+        if (signatures.isEmpty()) {
             throw new IllegalArgumentException(
                     "Must use GET_SIGNING_CERTIFICATES when looking up package info");
         }
@@ -263,9 +256,29 @@
     }
 
     /**
-     * Converts a Signature into a Certificate hash usable for comparison.
+     * Gets all of the Signatures from the given PackageInfo.
+     * @hide
      */
-    private static byte[] getCertHash(Signature signature, String algo) {
+    @NonNull
+    public static List<Signature> getSignatures(PackageInfo packageInfo) {
+        Signature[] signatures = packageInfo.signatures;
+        SigningInfo signingInfo = packageInfo.signingInfo;
+
+        if (signingInfo != null) {
+            signatures = signingInfo.getSigningCertificateHistory();
+            if (signingInfo.hasMultipleSigners()) {
+                signatures = signingInfo.getApkContentsSigners();
+            }
+        }
+
+        return (signatures == null) ? Collections.EMPTY_LIST : Arrays.asList(signatures);
+    }
+
+    /**
+     * Converts a Signature into a Certificate hash usable for comparison.
+     * @hide
+     */
+    public static byte[] getCertHash(Signature signature, String algo) {
         try {
             MessageDigest md = MessageDigest.getInstance(algo);
             return md.digest(signature.toByteArray());
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 6e630e3..158ada9 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -18,7 +18,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_5.ApnTypes;
@@ -125,6 +124,15 @@
     /** Authentication type for PAP or CHAP. */
     public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
 
+    /** @hide */
+    @IntDef({
+            Telephony.Carriers.SKIP_464XLAT_DEFAULT,
+            Telephony.Carriers.SKIP_464XLAT_DISABLE,
+            Telephony.Carriers.SKIP_464XLAT_ENABLE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Skip464XlatStatus {}
+
     /**
      * APN types for data connections.  These are usage categories for an APN
      * entry.  One APN entry may support multiple APN types, eg, a single APN
@@ -138,7 +146,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_ALL_STRING = "*";
 
     /**
@@ -146,7 +153,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_DEFAULT_STRING = "default";
 
 
@@ -155,7 +161,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_MMS_STRING = "mms";
 
 
@@ -164,7 +169,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_SUPL_STRING = "supl";
 
     /**
@@ -172,7 +176,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_DUN_STRING = "dun";
 
     /**
@@ -180,7 +183,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_HIPRI_STRING = "hipri";
 
     /**
@@ -188,7 +190,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_FOTA_STRING = "fota";
 
     /**
@@ -196,7 +197,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_IMS_STRING = "ims";
 
     /**
@@ -204,7 +204,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_CBS_STRING = "cbs";
 
     /**
@@ -212,7 +211,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_IA_STRING = "ia";
 
     /**
@@ -221,7 +219,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_EMERGENCY_STRING = "emergency";
 
     /**
@@ -229,7 +226,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_MCX_STRING = "mcx";
 
     /**
@@ -237,7 +233,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String TYPE_XCAP_STRING = "xcap";
 
 
@@ -741,7 +736,7 @@
      * @return SKIP_464XLAT_DEFAULT, SKIP_464XLAT_DISABLE or SKIP_464XLAT_ENABLE
      * @hide
      */
-    @Carriers.Skip464XlatStatus
+    @Skip464XlatStatus
     public int getSkip464Xlat() {
         return mSkip464Xlat;
     }
@@ -1412,7 +1407,6 @@
      * @return comma delimited list of APN types.
      * @hide
      */
-    @SystemApi
     @NonNull
     public static String getApnTypesStringFromBitmask(int apnTypeBitmask) {
         List<String> types = new ArrayList<>();
@@ -2061,7 +2055,7 @@
          * @param skip464xlat skip464xlat for this APN
          * @hide
          */
-        public Builder setSkip464Xlat(@Carriers.Skip464XlatStatus int skip464xlat) {
+        public Builder setSkip464Xlat(@Skip464XlatStatus int skip464xlat) {
             this.mSkip464Xlat = skip464xlat;
             return this;
         }
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a116c07..242c2e9 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -80,7 +80,6 @@
     private final int mMtu;
     private final int mMtuV4;
     private final int mMtuV6;
-    private final int mVersion;
 
     /**
      * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -126,9 +125,7 @@
                 ? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
         mPcscfAddresses = (pcscfAddresses == null)
                 ? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
-        mMtu = mtu;
-        mMtuV4 = mMtuV6 = 0;
-        mVersion = 0;
+        mMtu = mMtuV4 = mMtuV6 = mtu;
     }
 
     /** @hide */
@@ -136,7 +133,7 @@
             @LinkStatus int linkStatus, @ProtocolType int protocolType,
             @Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
             @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
-            @Nullable List<InetAddress> pcscfAddresses, int mtuV4, int mtuV6, int version) {
+            @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6) {
         mCause = cause;
         mSuggestedRetryTime = suggestedRetryTime;
         mId = id;
@@ -151,10 +148,9 @@
                 ? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
         mPcscfAddresses = (pcscfAddresses == null)
                 ? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
-        mMtu = 0;
+        mMtu = mtu;
         mMtuV4 = mtuV4;
         mMtuV6 = mtuV6;
-        mVersion = version;
     }
 
     /** @hide */
@@ -177,7 +173,6 @@
         mMtu = source.readInt();
         mMtuV4 = source.readInt();
         mMtuV6 = source.readInt();
-        mVersion = source.readInt();
     }
 
     /**
@@ -247,7 +242,7 @@
      */
     @Deprecated
     public int getMtu() {
-        return mVersion < 5 ? mMtu : 0;
+        return mMtu;
     }
 
     /**
@@ -256,7 +251,7 @@
      * Zero or negative values means network has either not sent a value or sent an invalid value.
      */
     public int getMtuV4() {
-        return mVersion < 5 ? 0 : mMtuV4;
+        return mMtuV4;
     }
 
     /**
@@ -264,7 +259,7 @@
      * Zero or negative values means network has either not sent a value or sent an invalid value.
      */
     public int getMtuV6() {
-        return mVersion < 5 ? 0 : mMtuV6;
+        return mMtuV6;
     }
 
     @NonNull
@@ -282,10 +277,9 @@
            .append(" dnses=").append(mDnsAddresses)
            .append(" gateways=").append(mGatewayAddresses)
            .append(" pcscf=").append(mPcscfAddresses)
-           .append(" mtu=").append(mMtu)
-           .append(" mtuV4=").append(mMtuV4)
-           .append(" mtuV6=").append(mMtuV6)
-           .append(" version=").append(mVersion)
+           .append(" mtu=").append(getMtu())
+           .append(" mtuV4=").append(getMtuV4())
+           .append(" mtuV6=").append(getMtuV6())
            .append("}");
         return sb.toString();
     }
@@ -315,15 +309,14 @@
                 && mPcscfAddresses.containsAll(other.mPcscfAddresses)
                 && mMtu == other.mMtu
                 && mMtuV4 == other.mMtuV4
-                && mMtuV6 == other.mMtuV6
-                && mVersion == other.mVersion;
+                && mMtuV6 == other.mMtuV6;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
                 mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
-                mMtu, mMtuV4, mMtuV6, mVersion);
+                mMtu, mMtuV4, mMtuV6);
     }
 
     @Override
@@ -346,7 +339,6 @@
         dest.writeInt(mMtu);
         dest.writeInt(mMtuV4);
         dest.writeInt(mMtuV6);
-        dest.writeInt(mVersion);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -403,8 +395,6 @@
 
         private int mMtuV6;
 
-        private int mVersion;
-
         /**
          * Default constructor for Builder.
          */
@@ -563,29 +553,14 @@
         }
 
         /**
-         * Set the IRadio version for this DataCallResponse
-         * @hide
-         */
-        public @NonNull Builder setVersion(int version) {
-            mVersion = version;
-            return this;
-        }
-
-        /**
          * Build the DataCallResponse.
          *
          * @return the DataCallResponse object.
          */
         public @NonNull DataCallResponse build() {
-            if (mVersion >= 5) {
-                return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
-                        mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
-                        mPcscfAddresses, mMtuV4, mMtuV6, mVersion);
-            } else {
-                return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
-                        mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
-                        mPcscfAddresses, mMtu);
-            }
+            return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
+                    mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
+                    mPcscfAddresses, mMtu, mMtuV4, mMtuV6);
         }
     }
 }
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 6c4e7ce..f56bbe1 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -458,6 +458,9 @@
      * this method to facilitate the creation of {@link DataServiceProvider} instances. The system
      * will call this method after binding the data service for each active SIM slot id.
      *
+     * This methead is guaranteed to be invoked in {@link DataService}'s internal handler thread
+     * whose looper can be retrieved with {@link Looper.myLooper()} when override this method.
+     *
      * @param slotIndex SIM slot id the data service associated with.
      * @return Data service object. Null if failed to create the provider (e.g. invalid slot index)
      */
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index ccd28f4..5500d63 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -249,13 +249,69 @@
      * Key for an extra set on {@link PendingIntent} result callbacks providing a detailed result
      * code.
      *
-     * <p>This code is an implementation detail of the embedded subscription manager and is only
-     * intended for logging or debugging purposes.
+     * <p>The value of this key is an integer and contains two portions. The first byte is
+     * OperationCode and the reaming three bytes is the ErrorCode.
+     *
+     * OperationCode is the first byte of the result code and is a categorization which defines what
+     * type of operation took place when an error occurred. e.g {@link #OPERATION_DOWNLOAD} means
+     * the error is related to download.Since the OperationCode only uses at most one byte, the
+     * maximum allowed quantity is 255(0xFF).
+     *
+     * ErrorCode is the remaining three bytes of the result code, and it denotes what happened.
+     * e.g a combination of {@link #OPERATION_DOWNLOAD} and {@link #ERROR_TIME_OUT} will suggest the
+     * download operation has timed out. The only exception here is
+     * {@link #OPERATION_SMDX_SUBJECT_REASON_CODE}, where instead of ErrorCode, SubjectCode[5.2.6.1
+     * from GSMA (SGP.22 v2.2) and ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2) are encoded. @see
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE} and
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE}
+     *
+     * In the case where ErrorCode contains a value of 0, it means it's an unknown error. E.g Intent
+     * only contains {@link #OPERATION_DOWNLOAD} and ErrorCode is 0 implies this is an unknown
+     * Download error.
+     *
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE
      */
     public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE =
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
 
     /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing a
+     * OperationCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE},
+     * value will be an int.
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_OPERATION_CODE";
+
+    /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing a
+     * ErrorCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE},
+     * value will be an int.
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_ERROR_CODE";
+
+    /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing a
+     * SubjectCode[5.2.6.1] from GSMA (SGP.22 v2.2) decoded from
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+     * The value of this extra will be a String.
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE";
+
+    /**
+     * Key for an extra set on {@link PendingIntent} result callbacks providing a
+     * ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2) decoded from
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+     * The value of this extra will be a String.
+     */
+    public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE =
+            "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE";
+
+    /**
      * Key for an extra set on {@code #getDownloadableSubscriptionMetadata} PendingIntent result
      * callbacks providing the downloadable subscription metadata.
      */
@@ -494,6 +550,259 @@
     @SystemApi
     public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
 
+    /**
+     * List of OperationCode corresponding to {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}'s
+     * value, an integer. @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"OPERATION_"}, value = {
+            OPERATION_SYSTEM,
+            OPERATION_SIM_SLOT,
+            OPERATION_EUICC_CARD,
+            OPERATION_SWITCH,
+            OPERATION_DOWNLOAD,
+            OPERATION_METADATA,
+            OPERATION_EUICC_GSMA,
+            OPERATION_APDU,
+            OPERATION_SMDX,
+            OPERATION_HTTP,
+            OPERATION_SMDX_SUBJECT_REASON_CODE,
+    })
+    public @interface OperationCode {
+    }
+
+    /**
+     * Internal system error.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_SYSTEM = 1;
+
+    /**
+     * SIM slot error. Failed to switch slot, failed to access the physical slot etc.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_SIM_SLOT = 2;
+
+    /**
+     * eUICC card error.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_EUICC_CARD = 3;
+
+    /**
+     * Generic switching profile error
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_SWITCH = 4;
+
+    /**
+     * Download profile error.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_DOWNLOAD = 5;
+
+    /**
+     * Subscription's metadata error
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_METADATA = 6;
+
+    /**
+     * eUICC returned an error defined in GSMA (SGP.22 v2.2) while running one of the ES10x
+     * functions.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_EUICC_GSMA = 7;
+
+    /**
+     * The exception of failing to execute an APDU command. It can be caused by an error
+     * happening on opening the basic or logical channel, or the response of the APDU command is
+     * not success (0x9000).
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_APDU = 8;
+
+    /**
+     * SMDX(SMDP/SMDS) error
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_SMDX = 9;
+
+    /**
+     * SubjectCode[5.2.6.1] and ReasonCode[5.2.6.2] error from GSMA (SGP.22 v2.2)
+     * When {@link #OPERATION_SMDX_SUBJECT_REASON_CODE} is used as the
+     * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}, the remaining three bytes of the integer
+     * result from {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} will be used to stored the
+     * SubjectCode and ReasonCode from the GSMA spec and NOT ErrorCode.
+     *
+     * The encoding will follow the format of:
+     * 1. The first byte of the result will be 255(0xFF).
+     * 2. Remaining three bytes(24 bits) will be split into six sections, 4 bits in each section.
+     * 3. A SubjectCode/ReasonCode will take 12 bits each.
+     * 4. The maximum number can be represented per section is 15, as that is the maximum number
+     * allowed to be stored into 4 bits
+     * 5. Maximum supported nested category from GSMA is three layers. E.g 8.11.1.2 is not
+     * supported.
+     *
+     * E.g given SubjectCode(8.11.1) and ReasonCode(5.1)
+     *
+     * Base10:  0       10      8       11      1       0       5       1
+     * Base2:   0000    1010    1000    1011    0001    0000    0101    0001
+     * Base16:  0       A       8       B       1       0       5       1
+     *
+     * Thus the integer stored in {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} is
+     * 0xA8B1051(176885841)
+     *
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_SMDX_SUBJECT_REASON_CODE = 10;
+
+    /**
+     * HTTP error
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int OPERATION_HTTP = 11;
+
+    /**
+     * List of ErrorCode corresponding to {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ERROR_"}, value = {
+            ERROR_CARRIER_LOCKED,
+            ERROR_INVALID_ACTIVATION_CODE,
+            ERROR_INVALID_CONFIRMATION_CODE,
+            ERROR_INCOMPATIBLE_CARRIER,
+            ERROR_EUICC_INSUFFICIENT_MEMORY,
+            ERROR_TIME_OUT,
+            ERROR_EUICC_MISSING,
+            ERROR_UNSUPPORTED_VERSION,
+            ERROR_SIM_MISSING,
+            ERROR_INSTALL_PROFILE,
+            ERROR_DISALLOWED_BY_PPR,
+            ERROR_ADDRESS_MISSING,
+            ERROR_CERTIFICATE_ERROR,
+            ERROR_NO_PROFILES_AVAILABLE,
+            ERROR_CONNECTION_ERROR,
+            ERROR_INVALID_RESPONSE,
+            ERROR_OPERATION_BUSY,
+    })
+    public @interface ErrorCode{}
+
+    /**
+     * Operation such as downloading/switching to another profile failed due to device being
+     * carrier locked.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_CARRIER_LOCKED = 10000;
+
+    /**
+     * The activation code(SGP.22 v2.2 section[4.1]) is invalid.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_INVALID_ACTIVATION_CODE = 10001;
+
+    /**
+     * The confirmation code(SGP.22 v2.2 section[4.7]) is invalid.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_INVALID_CONFIRMATION_CODE = 10002;
+
+    /**
+     * The profile's carrier is incompatible with the LPA.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_INCOMPATIBLE_CARRIER = 10003;
+
+    /**
+     * There is no more space available on the eUICC for new profiles.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_EUICC_INSUFFICIENT_MEMORY = 10004;
+
+    /**
+     * Timed out while waiting for an operation to complete. i.e restart, disable,
+     * switch reset etc.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_TIME_OUT = 10005;
+
+    /**
+     * eUICC is missing or defective on the device.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_EUICC_MISSING = 10006;
+
+    /**
+     * The eUICC card(hardware) version is incompatible with the software
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_UNSUPPORTED_VERSION = 10007;
+
+    /**
+     * No SIM card is available in the device.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_SIM_MISSING = 10008;
+
+    /**
+     * Failure to load the profile onto the eUICC card. e.g
+     * 1. iccid of the profile already exists on the eUICC.
+     * 2. GSMA(.22 v2.2) Profile Install Result - installFailedDueToDataMismatch
+     * 3. operation was interrupted
+     * 4. SIMalliance error in PEStatus(SGP.22 v2.2 section 2.5.6.1)
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_INSTALL_PROFILE = 10009;
+
+    /**
+     * Failed to load profile onto eUICC due to Profile Poicly Rules.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_DISALLOWED_BY_PPR = 10010;
+
+
+    /**
+     * Address is missing e.g SMDS/SMDP address is missing.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_ADDRESS_MISSING = 10011;
+
+    /**
+     * Certificate needed for authentication is not valid or missing. E.g  SMDP/SMDS authentication
+     * failed.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_CERTIFICATE_ERROR = 10012;
+
+
+    /**
+     * No profiles available.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_NO_PROFILES_AVAILABLE = 10013;
+
+    /**
+     * Failure to create a connection.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_CONNECTION_ERROR = 10014;
+
+    /**
+     * Response format is invalid. e.g SMDP/SMDS response contains invalid json, header or/and ASN1.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_INVALID_RESPONSE = 10015;
+
+    /**
+     * The operation is currently busy, try again later.
+     * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
+     */
+    public static final int ERROR_OPERATION_BUSY = 10016;
+
     private final Context mContext;
     private int mCardId;
 
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index a7d553b..1e8fdce 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -182,6 +181,25 @@
      *      {@link ImsCallProfile#DIALSTRING_USSD}
      */
     public static final String EXTRA_DIALSTRING = "dialstring";
+    /**
+     * This extra holds call fail cause because of which redial is attempted.
+     * see {@link android.telephony.ims.ImsReasonInfo} {@code CODE_*}
+     * for possible values this extra can hold.
+     *
+     * @hide
+     */
+    public static final String EXTRA_RETRY_CALL_FAIL_REASON =
+            "android.telephony.ims.extra.RETRY_CALL_FAIL_REASON";
+    /**
+     * This extra holds call network type on which lower layers
+     * may try attempting redial.
+     * See {@link TelephonyManager} {@code NETWORK_TYPE_*}
+     * for possible values this extra can hold.
+     *
+     * @hide
+     */
+    public static final String EXTRA_RETRY_CALL_FAIL_NETWORKTYPE =
+            "android.telephony.ims.extra.RETRY_CALL_FAIL_NETWORKTYPE";
 
     /**
      * Values for EXTRA_OIR / EXTRA_CNAP
@@ -702,11 +720,16 @@
      * @return A {@link Bundle} containing proprietary call extras that were not set by the
      * platform.
      */
-    public @Nullable Bundle getProprietaryCallExtras() {
+    public @NonNull Bundle getProprietaryCallExtras() {
         if (mCallExtras == null) {
-            return null;
+            return new Bundle();
         }
-        return mCallExtras.getBundle(EXTRA_OEM_EXTRAS);
+        Bundle proprietaryExtras = mCallExtras.getBundle(EXTRA_OEM_EXTRAS);
+        if (proprietaryExtras == null) {
+            return new Bundle();
+        }
+        // Make a copy so users do not accidentally change this copy of the extras.
+        return new Bundle(proprietaryExtras);
     }
 
     public ImsStreamMediaProfile getMediaProfile() {
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index 025721c..d21a051 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -58,7 +58,7 @@
         try {
             mListener.callSessionProgressing(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -71,7 +71,7 @@
         try {
             mListener.callSessionInitiated(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -85,7 +85,7 @@
         try {
             mListener.callSessionInitiatedFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -98,7 +98,7 @@
         try {
             mListener.callSessionTerminated(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -115,7 +115,7 @@
         try {
             mListener.callSessionHeld(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -128,7 +128,7 @@
         try {
             mListener.callSessionHoldFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -141,7 +141,7 @@
         try {
             mListener.callSessionHoldReceived(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -155,7 +155,7 @@
         try {
             mListener.callSessionResumed(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -169,7 +169,7 @@
         try {
             mListener.callSessionResumeFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -182,7 +182,7 @@
         try {
             mListener.callSessionResumeReceived(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -201,7 +201,7 @@
             mListener.callSessionMergeStarted(newSession != null ?
                             newSession.getServiceImpl() : null, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -216,7 +216,7 @@
         try {
             mListener.callSessionMergeStarted(newSession, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -232,7 +232,7 @@
             mListener.callSessionMergeComplete(newSession != null ?
                     newSession.getServiceImpl() : null);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -247,7 +247,7 @@
         try {
             mListener.callSessionMergeComplete(newSession);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -260,7 +260,7 @@
         try {
             mListener.callSessionMergeFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -273,7 +273,7 @@
         try {
             mListener.callSessionUpdated(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -286,7 +286,7 @@
         try {
             mListener.callSessionUpdateFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -299,7 +299,7 @@
         try {
             mListener.callSessionUpdateReceived(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -319,7 +319,7 @@
             mListener.callSessionConferenceExtended(
                     newSession != null ? newSession.getServiceImpl() : null, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -333,7 +333,7 @@
         try {
             mListener.callSessionConferenceExtended(newSession, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -347,7 +347,7 @@
         try {
             mListener.callSessionConferenceExtendFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -364,7 +364,7 @@
             mListener.callSessionConferenceExtendReceived(newSession != null
                     ? newSession.getServiceImpl() : null, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -379,7 +379,7 @@
         try {
             mListener.callSessionConferenceExtendReceived(newSession, profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -391,7 +391,7 @@
         try {
             mListener.callSessionInviteParticipantsRequestDelivered();
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -407,7 +407,7 @@
         try {
             mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -419,7 +419,7 @@
         try {
             mListener.callSessionRemoveParticipantsRequestDelivered();
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -435,7 +435,7 @@
         try {
             mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -448,7 +448,7 @@
         try {
             mListener.callSessionConferenceStateUpdated(state);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -465,7 +465,7 @@
         try {
             mListener.callSessionUssdMessageReceived(mode, ussdMessage);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -501,7 +501,7 @@
         try {
             mListener.callSessionMayHandover(srcNetworkType, targetNetworkType);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -537,7 +537,7 @@
         try {
             mListener.callSessionHandover(srcNetworkType, targetNetworkType, reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -570,7 +570,7 @@
         try {
             mListener.callSessionHandoverFailed(srcNetworkType, targetNetworkType, reasonInfo);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -587,7 +587,7 @@
         try {
             mListener.callSessionTtyModeReceived(mode);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -600,7 +600,7 @@
         try {
             mListener.callSessionMultipartyStateChanged(isMultiParty);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -614,7 +614,7 @@
         try {
             mListener.callSessionSuppServiceReceived(suppSrvNotification);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -628,7 +628,7 @@
         try {
             mListener.callSessionRttModifyRequestReceived(callProfile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -641,7 +641,7 @@
         try {
             mListener.callSessionRttModifyResponseReceived(status);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -654,7 +654,7 @@
         try {
             mListener.callSessionRttMessageReceived(rttMessage);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -667,7 +667,7 @@
         try {
             mListener.callSessionRttAudioIndicatorChanged(profile);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
         }
     }
 
@@ -680,7 +680,34 @@
         try {
             mListener.callQualityChanged(callQuality);
         } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies the result of transfer request.
+     * @hide
+     */
+    public void callSessionTransferred() {
+        try {
+            mListener.callSessionTransferred();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies the result of transfer request.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing a reason for the
+     * session transfer failure
+     * @hide
+     */
+    public void callSessionTransferFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionTransferFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
         }
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index 643f452..1c3d58d 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -47,11 +47,12 @@
     public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1;
 
     /**
-     * This device or carrier configuration does not support IMS for this subscription.
+     * This device or carrier configuration does not support this feature for this subscription.
      * <p>
-     * This is a permanent configuration error and there should be no retry. Usually this is
-     * because {@link PackageManager#FEATURE_TELEPHONY_IMS} is not available
-     * or the device has no ImsService implementation to service this request.
+     * This is a permanent configuration error and there should be no retry until the subscription
+     * changes if this operation is denied due to a carrier configuration. If this is due to a
+     * device configuration, the feature {@link PackageManager#FEATURE_TELEPHONY_IMS} is not
+     * available or the device has no ImsService implementation to service this request.
      */
     public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2;
 
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 494009f..872f49d 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -21,7 +21,6 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SuppressLint;
@@ -125,7 +124,7 @@
          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
          */
         @Override
-        public void onUnregistered(@Nullable ImsReasonInfo info) {
+        public void onUnregistered(@NonNull ImsReasonInfo info) {
         }
 
         /**
@@ -137,13 +136,12 @@
         @Override
         public void onTechnologyChangeFailed(
                 @AccessNetworkConstants.TransportType int imsTransportType,
-                @Nullable ImsReasonInfo info) {
+                @NonNull ImsReasonInfo info) {
         }
     }
 
     /**
-     * Receives IMS capability status updates from the ImsService. This information is also
-     * available via the {@see #isAvailable(int, int)} method below.
+     * Receives IMS capability status updates from the ImsService.
      *
      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
      * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
@@ -196,8 +194,6 @@
          * If unavailable, the feature is not able to support the unavailable capability at this
          * time.
          *
-         * This information can also be queried using the {@see #isAvailable(int, int)} API.
-         *
          * @param capabilities The new availability of the capabilities.
          */
         public void onCapabilitiesStatusChanged(
@@ -295,8 +291,15 @@
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
         c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
         try {
-            getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+            iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -332,8 +335,15 @@
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
         c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
         try {
-            getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+            iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
         } catch (ServiceSpecificException e) {
             throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException | IllegalStateException e) {
@@ -362,8 +372,14 @@
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
+            iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -388,8 +404,14 @@
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
+            iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -410,8 +432,14 @@
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+            iTelephony.getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
                 @Override
                 public void accept(int result) {
                     executor.execute(() -> stateCallback.accept(result));
@@ -444,8 +472,14 @@
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().getImsMmTelRegistrationTransportType(mSubId,
+            iTelephony.getImsMmTelRegistrationTransportType(mSubId,
                     new IIntegerConsumer.Stub() {
                         @Override
                         public void accept(int result) {
@@ -460,8 +494,7 @@
     /**
      * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
      * availability updates for the subscription specified in
-     * {@link ImsManager#getImsMmTelManager(int)}. The method {@see #isAvailable(int, int)}
-     * can also be used to query this information at any time.
+     * {@link ImsManager#getImsMmTelManager(int)}.
      *
      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
      * subscription changed events and call
@@ -507,8 +540,15 @@
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
         c.setExecutor(executor);
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+        }
+
         try {
-            getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
+            iTelephony.registerMmTelCapabilityCallback(mSubId, c.getBinder());
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -554,8 +594,14 @@
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
+            iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -590,7 +636,6 @@
      * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
-     * @see #setAdvancedCallingSettingEnabled(boolean)
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user's setting for advanced calling is enabled, false otherwise.
@@ -600,8 +645,13 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public boolean isAdvancedCallingSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+            return iTelephony.isAdvancedCallingSettingEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -641,8 +691,13 @@
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi @TestApi
     public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled);
+            iTelephony.setAdvancedCallingSettingEnabled(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -681,8 +736,13 @@
     @SystemApi @TestApi
     public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isCapable(mSubId, capability, imsRegTech);
+            return iTelephony.isCapable(mSubId, capability, imsRegTech);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -710,8 +770,13 @@
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isAvailable(mSubId, capability, imsRegTech);
+            return iTelephony.isAvailable(mSubId, capability, imsRegTech);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -745,6 +810,13 @@
         if (executor == null) {
             throw new IllegalArgumentException("Must include a non-null Executor.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
         try {
             getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
                 @Override
@@ -782,15 +854,19 @@
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user’s “Video Calling” setting is currently enabled.
-     * @see #setVtSettingEnabled(boolean)
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     public boolean isVtSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isVtSettingEnabled(mSubId);
+            return iTelephony.isVtSettingEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -814,8 +890,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVtSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVtSettingEnabled(mSubId, isEnabled);
+            iTelephony.setVtSettingEnabled(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -847,15 +928,19 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
-     * @see #setVoWiFiSettingEnabled(boolean)
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public boolean isVoWiFiSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isVoWiFiSettingEnabled(mSubId);
+            return iTelephony.isVoWiFiSettingEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -880,8 +965,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled);
+            iTelephony.setVoWiFiSettingEnabled(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -915,15 +1005,19 @@
      * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
      * if disabled.
-     * @see #setVoWiFiRoamingSettingEnabled(boolean)
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public boolean isVoWiFiRoamingSettingEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+            return iTelephony.isVoWiFiRoamingSettingEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -949,8 +1043,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
+            iTelephony.setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -981,8 +1080,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
+            iTelephony.setVoWiFiNonPersistent(mSubId, isCapable, mode);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1019,15 +1123,19 @@
      * - {@link #WIFI_MODE_WIFI_ONLY}
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
-     * @see #setVoWiFiSettingEnabled(boolean)
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public @WiFiCallingMode int getVoWiFiModeSetting() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().getVoWiFiModeSetting(mSubId);
+            return iTelephony.getVoWiFiModeSetting(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1055,8 +1163,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiModeSetting(mSubId, mode);
+            iTelephony.setVoWiFiModeSetting(mSubId, mode);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1086,8 +1199,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+            return iTelephony.getVoWiFiRoamingModeSetting(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1117,8 +1235,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
+            iTelephony.setVoWiFiRoamingModeSetting(mSubId, mode);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1146,8 +1269,13 @@
     @SystemApi @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRttCapabilitySetting(boolean isEnabled) {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
+            iTelephony.setRttCapabilitySetting(mSubId, isEnabled);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1179,7 +1307,6 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
-     * @see android.telecom.TelecomManager#getCurrentTtyMode
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@@ -1187,8 +1314,13 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
     public boolean isTtyOverVolteEnabled() {
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+
         try {
-            return getITelephony().isTtyOverVolteEnabled(mSubId);
+            return iTelephony.isTtyOverVolteEnabled(mSubId);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
                 // Rethrow as runtime error to keep API compatible.
@@ -1224,8 +1356,15 @@
         if (callback == null) {
             throw new IllegalArgumentException("Must include a non-null Consumer.");
         }
+
+        ITelephony iTelephony = getITelephony();
+        if (iTelephony == null) {
+            throw new ImsException("Could not find Telephony Service.",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
         try {
-            getITelephony().getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
+            iTelephony.getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
                 @Override
                 public void accept(int result) {
                     executor.execute(() -> callback.accept(result));
@@ -1241,9 +1380,6 @@
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(
                 ServiceManager.getService(Context.TELEPHONY_SERVICE));
-        if (binder == null) {
-            throw new RuntimeException("Could not find Telephony Service.");
-        }
         return binder;
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index a2ad21c..71bc68e 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -20,14 +20,16 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
+import android.annotation.SdkConstant;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRcsController;
@@ -46,14 +48,34 @@
  * (UCE) service, as well as managing user settings.
  *
  * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this manager.
- * @hide
  */
-@SystemApi
-@TestApi
-public class ImsRcsManager implements RegistrationManager {
+public class ImsRcsManager {
     private static final String TAG = "ImsRcsManager";
 
     /**
+     * Activity Action: Show the opt-in dialog for enabling or disabling RCS contact discovery
+     * using User Capability Exchange (UCE).
+     * <p>
+     * An application that depends on contact discovery being enabled may send this intent
+     * using {@link Context#startActivity(Intent)} to ask the user to opt-in for contacts upload for
+     * capability exchange if it is currently disabled. Whether or not this setting has been enabled
+     * can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}.
+     * <p>
+     * This intent should only be sent if the carrier supports RCS capability exchange, which can be
+     * queried using the key {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the
+     * setting will not be present.
+     * <p>
+     * Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the
+     * setting will be be shown for.
+     * <p>
+     * Output: Nothing
+     * @see RcsUceAdapter
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN =
+            "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
+
+    /**
      * Receives RCS availability status updates from the ImsService.
      *
      * @see #isAvailable(int)
@@ -145,18 +167,17 @@
      */
     @NonNull
     public RcsUceAdapter getUceAdapter() {
-        return new RcsUceAdapter(mSubId);
+        return new RcsUceAdapter(mContext, mSubId);
     }
 
     /**
-     * {@inheritDoc}
      * @hide
      */
-    @Override
+    // @Override add back to RegistrationManager interface once public.
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void registerImsRegistrationCallback(
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull RegistrationCallback c)
+            @NonNull RegistrationManager.RegistrationCallback c)
             throws ImsException {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -181,10 +202,9 @@
     }
 
     /**
-     * {@inheritDoc
      * @hide
      */
-    @Override
+    // @Override add back to RegistrationManager interface once public.
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterImsRegistrationCallback(
             @NonNull RegistrationManager.RegistrationCallback c) {
@@ -206,13 +226,12 @@
     }
 
     /**
-     * {@inheritDoc}
      * @hide
      */
-    @Override
+    // @Override add back to RegistrationManager interface once public.
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
-            @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) {
+            @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) {
         if (stateCallback == null) {
             throw new IllegalArgumentException("Must include a non-null stateCallback.");
         }
@@ -239,10 +258,8 @@
     }
 
     /**
-     * {@inheritDoc}
      * @hide
      */
-    @Override
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
             @NonNull @AccessNetworkConstants.TransportType
@@ -329,8 +346,7 @@
      * inactive subscription, it will result in a no-op.
      * @param c The RCS {@link AvailabilityCallback} to be removed.
      * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
-     * @throws ImsException if the IMS service is not available when calling this method
-     * {@link ImsRcsController#unregisterRcsAvailabilityCallback()}.
+     * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
@@ -372,8 +388,7 @@
      * rather the subscription is capable of this service over IMS.
      * @see #isAvailable(int)
      * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
-     * @throws ImsException if the IMS service is not available when calling this method
-     * {@link ImsRcsController#isCapable(int, int)}.
+     * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
@@ -406,9 +421,8 @@
      * @return true if the RCS capability is currently available for the associated subscription,
      * false otherwise. If the capability is available, IMS is registered and the service is
      * currently available over IMS.
-     * @see #isCapable(int)
-     * @throws ImsException if the IMS service is not available when calling this method
-     * {@link ImsRcsController#isAvailable(int, int)}.
+     * @see #isCapable(int, int)
+     * @throws ImsException if the IMS service is not available when calling this method.
      * See {@link ImsException#getCode()} for more information on the error codes.
      * @hide
      */
diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java
index bc124044..460a032 100644
--- a/telephony/java/android/telephony/ims/ImsUtListener.java
+++ b/telephony/java/android/telephony/ims/ImsUtListener.java
@@ -49,7 +49,8 @@
      * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_RESTRICTED}, and
      * {@link ImsSsInfo#CLIR_STATUS_TEMPORARILY_ALLOWED}.
      * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
-     * instead.
+     * instead, this key has been added for backwards compatibility with older proprietary
+     * implementations only and is being phased out.
      */
     @Deprecated
     public static final String BUNDLE_KEY_CLIR = "queryClir";
@@ -60,7 +61,8 @@
      * response. The value will be an instance of {@link ImsSsInfo}, which contains the response to
      * the query.
      * @deprecated Use {@link #onLineIdentificationSupplementaryServiceResponse(int, ImsSsInfo)}
-     * instead.
+     * instead, this key has been added for backwards compatibility with older proprietary
+     * implementations only and is being phased out.
      */
     @Deprecated
     public static final String BUNDLE_KEY_SSINFO = "imsSsInfo";
@@ -123,7 +125,7 @@
         try {
             mServiceInterface.lineIdentificationSupplementaryServiceResponse(id, configuration);
         } catch (RemoteException e) {
-            Log.w(LOG_TAG, "onLineIdentificationSupplementaryServicesResponse: remote exception");
+            e.rethrowFromSystemServer();
         }
     }
 
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 6125001..459b460 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -86,6 +86,7 @@
 
     /**
      * There is no existing configuration for the queried provisioning key.
+     * @hide
      */
     public static final int PROVISIONING_RESULT_UNKNOWN = -1;
 
@@ -121,6 +122,7 @@
      * Value is in String format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0;
 
@@ -144,6 +146,7 @@
      * Value is in String format.
      * @see #setProvisioningStringValue(int, String)
      * @see #getProvisioningStringValue(int)
+     * @hide
      */
     public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1;
 
@@ -155,6 +158,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_SESSION_TIMER_SEC = 2;
 
@@ -166,6 +170,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3;
 
@@ -177,6 +182,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4;
 
@@ -185,6 +191,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5;
 
@@ -193,6 +200,7 @@
      * Value is in boolean format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_ENABLE_SILENT_REDIAL = 6;
 
@@ -208,6 +216,7 @@
      * The value is an integer.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_T1_TIMER_VALUE_MS = 7;
 
@@ -219,6 +228,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_T2_TIMER_VALUE_MS = 8;
 
@@ -230,6 +240,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_TF_TIMER_VALUE_MS = 9;
 
@@ -242,6 +253,7 @@
      * {@link #PROVISIONING_VALUE_DISABLED} to disable VoLTE provisioning.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VOLTE_PROVISIONING_STATUS = 10;
 
@@ -254,6 +266,7 @@
      * {@link #PROVISIONING_VALUE_DISABLED} to disable VT provisioning.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VT_PROVISIONING_STATUS = 11;
 
@@ -262,6 +275,7 @@
      * Value is in String format.
      * @see #setProvisioningStringValue(int, String)
      * @see #getProvisioningStringValue(int)
+     * @hide
      */
     public static final int KEY_REGISTRATION_DOMAIN_NAME = 12;
 
@@ -271,18 +285,21 @@
      * Valid values are {@link #SMS_FORMAT_3GPP} and {@link #SMS_FORMAT_3GPP2}.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SMS_FORMAT = 13;
 
     /**
      * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP2 SMS format is used.
      * See {@link android.telephony.SmsMessage#FORMAT_3GPP2} for more information.
+     * @hide
      */
     public static final int SMS_FORMAT_3GPP2 = 0;
 
     /**
      * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP SMS format is used.
      * See {@link android.telephony.SmsMessage#FORMAT_3GPP} for more information.
+     * @hide
      */
     public static final int SMS_FORMAT_3GPP = 1;
 
@@ -291,6 +308,7 @@
      * Value is in Integer format. ON (1), OFF(0).
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SMS_OVER_IP_ENABLED = 14;
 
@@ -301,18 +319,20 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15;
 
     /**
      * An integer key associated with the carrier configured expiration time in seconds for
-     * RCS presence published offline availability in RCS presence.
+     * published offline availability in RCS presence provided, which is provided to the network.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
-    public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16;
+    public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16;
 
     /**
      * An integer key associated with whether or not capability discovery is provisioned for this
@@ -323,46 +343,55 @@
      * enabled.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17;
 
     /**
-     * An integer key associated with the period of time the capability information of each contact
-     * is cached on the device.
+     * An integer key associated with the period of time in seconds the capability information of
+     * each contact is cached on the device.
+     * <p>
+     * Seconds are used because this is usually measured in the span of days.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18;
 
     /**
      * An integer key associated with the period of time in seconds that the availability
-     * information of a contact is cached on the device.
+     * information of a contact is cached on the device, which is based on the carrier provisioning
+     * configuration from the network.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19;
 
     /**
      * An integer key associated with the carrier configured interval in seconds expected between
-     * successive capability polling attempts.
+     * successive capability polling attempts, which is based on the carrier provisioning
+     * configuration from the network.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20;
 
     /**
      * An integer key representing the minimum time allowed between two consecutive presence publish
-     * messages from the device.
+     * messages from the device in milliseconds.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21;
 
@@ -373,17 +402,19 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22;
 
     /**
      * An integer associated with the expiration timer used during the SIP subscription of a
      * Request Contained List (RCL), which is used to retrieve the RCS capabilities of the contact
-     * book.
+     * book. This timer value is sent in seconds to the network.
      * <p>
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23;
 
@@ -392,6 +423,7 @@
      * Value is in Integer format. Enable (1), Disable(0).
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24;
 
@@ -404,6 +436,7 @@
      * {@link #PROVISIONING_VALUE_DISABLED} to disable EAB provisioning.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_EAB_PROVISIONING_STATUS = 25;
 
@@ -437,6 +470,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28;
 
@@ -445,6 +479,7 @@
      * Value is in Integer format. On (1), OFF(0).
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_MOBILE_DATA_ENABLED = 29;
 
@@ -453,12 +488,14 @@
      * Value is in Integer format. Opted-in (1) Opted-out (0).
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30;
 
     /**
      * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
      * Value is in String format.
+     * @hide
      */
     public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31;
 
@@ -467,22 +504,27 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32;
 
     /**
-     * Registration retry Base Time value in seconds.
+     * Registration retry Base Time value in seconds, which is based off of the carrier
+     * configuration.
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33;
 
     /**
-     * Registration retry Max Time value in seconds.
+     * Registration retry Max Time value in seconds, which is based off of the carrier
+     * configuration.
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34;
 
@@ -491,6 +533,7 @@
      * Value is in integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
 
     public static final int KEY_RTP_SPEECH_START_PORT = 35;
@@ -500,6 +543,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RTP_SPEECH_END_PORT = 36;
 
@@ -509,6 +553,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37;
 
@@ -518,6 +563,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38;
 
@@ -527,6 +573,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39;
 
@@ -536,6 +583,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40;
 
@@ -545,6 +593,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41;
 
@@ -554,6 +603,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42;
 
@@ -563,6 +613,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43;
 
@@ -572,6 +623,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44;
 
@@ -581,6 +633,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45;
 
@@ -590,6 +643,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46;
 
@@ -598,6 +652,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47;
 
@@ -606,6 +661,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48;
 
@@ -614,6 +670,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49;
 
@@ -622,6 +679,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50;
 
@@ -630,6 +688,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51;
 
@@ -638,6 +697,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52;
 
@@ -646,12 +706,14 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53;
 
     /**
      * SMS Public Service Identity.
      * Value is in String format.
+     * @hide
      */
     public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54;
 
@@ -661,16 +723,19 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VIDEO_QUALITY = 55;
 
     /**
      * Used with {@link #KEY_VIDEO_QUALITY} to indicate low video quality.
+     * @hide
      */
     public static final int VIDEO_QUALITY_LOW = 0;
 
     /**
      * Used with {@link #KEY_VIDEO_QUALITY} to indicate high video quality.
+     * @hide
      */
     public static final int VIDEO_QUALITY_HIGH = 1;
 
@@ -680,6 +745,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_LTE_THRESHOLD_1 = 56;
 
@@ -691,6 +757,7 @@
      *
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_LTE_THRESHOLD_2 = 57;
 
@@ -702,6 +769,7 @@
      *
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_LTE_THRESHOLD_3 = 58;
 
@@ -711,6 +779,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_1X_THRESHOLD = 59;
 
@@ -722,6 +791,7 @@
      *
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_WIFI_THRESHOLD_A = 60;
 
@@ -732,6 +802,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_WIFI_THRESHOLD_B = 61;
 
@@ -741,6 +812,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_LTE_EPDG_TIMER_SEC = 62;
 
@@ -750,12 +822,14 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_WIFI_EPDG_TIMER_SEC = 63;
 
     /**
      * 1x ePDG timer (in seconds).
      * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+     * @hide
      */
     public static final int KEY_1X_EPDG_TIMER_SEC = 64;
 
@@ -764,6 +838,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_MULTIENDPOINT_ENABLED = 65;
 
@@ -772,6 +847,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RTT_ENABLED = 66;
 
@@ -923,7 +999,7 @@
      *
      * @param key An integer that represents the provisioning key, which is defined by the OEM.
      * @return an integer value for the provided key, or
-     * {@link #PROVISIONING_RESULT_UNKNOWN} if the key doesn't exist.
+     * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
      * @throws IllegalArgumentException if the key provided was invalid.
      */
     @WorkerThread
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 57b9b7a..dc36edf 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -19,8 +19,6 @@
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -37,8 +35,6 @@
  * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
  * @hide
  */
-@SystemApi
-@TestApi
 public final class RcsContactUceCapability implements Parcelable {
 
     /** Supports 1-to-1 chat */
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 6974420..01fc09a 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -31,7 +31,7 @@
 import android.os.ServiceManager;
 import android.telephony.ims.aidl.IImsRcsController;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
-import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -43,10 +43,7 @@
  * Manages RCS User Capability Exchange for the subscription specified.
  *
  * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
- * @hide
  */
-@SystemApi
-@TestApi
 public class RcsUceAdapter {
     private static final String TAG = "RcsUceAdapter";
 
@@ -139,7 +136,7 @@
      * UCE.
      * @hide
      */
-    public static final int PUBLISH_STATE_200_OK = 1;
+    public static final int PUBLISH_STATE_OK = 1;
 
     /**
      * The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
@@ -179,7 +176,7 @@
     /**@hide*/
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "PUBLISH_STATE_", value = {
-            PUBLISH_STATE_200_OK,
+            PUBLISH_STATE_OK,
             PUBLISH_STATE_NOT_PUBLISHED,
             PUBLISH_STATE_VOLTE_PROVISION_ERROR,
             PUBLISH_STATE_RCS_PROVISION_ERROR,
@@ -188,6 +185,58 @@
     })
     public @interface PublishState {}
 
+    /**
+     * An application can use {@link #registerPublishStateCallback} to register a
+     * {@link PublishStateCallback), which will notify the user when the publish state to the
+     * network changes.
+     * @hide
+     */
+    public static class PublishStateCallback {
+
+        private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub {
+
+            private final PublishStateCallback mLocalCallback;
+            private Executor mExecutor;
+
+            PublishStateBinder(PublishStateCallback c) {
+                mLocalCallback = c;
+            }
+
+            @Override
+            public void onPublishStateChanged(int publishState) {
+                if (mLocalCallback == null) return;
+
+                long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onChanged(publishState));
+                } finally {
+                    restoreCallingIdentity(callingIdentity);
+                }
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final PublishStateBinder mBinder = new PublishStateBinder(this);
+
+        /**@hide*/
+        public final IRcsUcePublishStateCallback getBinder() {
+            return mBinder;
+        }
+
+        private void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+
+        /**
+         * Notifies the callback when the publish state has changed.
+         * @param publishState The latest update to the publish state.
+         */
+        public void onChanged(@PublishState int publishState) {
+        }
+    }
 
     /**
      * Provides a one-time callback for the response to a UCE request. After this callback is called
@@ -216,6 +265,7 @@
         }
     }
 
+    private final Context mContext;
     private final int mSubId;
 
     /**
@@ -223,7 +273,8 @@
      * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class.
      * @hide
      */
-    RcsUceAdapter(int subId) {
+    RcsUceAdapter(Context context, int subId) {
+        mContext = context;
         mSubId = subId;
     }
 
@@ -291,7 +342,8 @@
         };
 
         try {
-            imsRcsController.requestCapabilities(mSubId, contactNumbers, internalCallback);
+            imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
+                    null /*featureId*/, contactNumbers, internalCallback);
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -303,7 +355,7 @@
      * Gets the last publish result from the UCE service if the device is using an RCS presence
      * server.
      * @return The last publish result from the UCE service. If the device is using SIP OPTIONS,
-     * this method will return {@link #PUBLISH_STATE_200_OK} as well.
+     * this method will return {@link #PUBLISH_STATE_OK} as well.
      * @throws ImsException if the subscription associated with this instance of
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
@@ -321,6 +373,8 @@
 
         try {
             return imsRcsController.getUcePublishState(mSubId);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#getUcePublishState", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -329,6 +383,91 @@
     }
 
     /**
+     * Registers a {@link PublishStateCallback} with the system, which will provide publish state
+     * updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
+     * <p>
+     * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription
+     * changed events and call {@link #unregisterPublishStateCallback} to clean up.
+     * <p>
+     * The registered {@link PublishStateCallback} will also receive a callback when it is
+     * registered with the current publish state.
+     *
+     * @param executor The executor the listener callback events should be run on.
+     * @param c The {@link PublishStateCallback} to be added.
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerPublishStateCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull PublishStateCallback c) throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null PublishStateCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "registerPublishStateCallback : IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        c.setExecutor(executor);
+        try {
+            imsRcsController.registerUcePublishStateCallback(mSubId, c.getBinder());
+        } catch (android.os.ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Removes an existing {@link PublishStateCallback}.
+     * <p>
+     * When the subscription associated with this callback is removed
+     * (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method
+     * is called for an inactive subscription, it will result in a no-op.
+     *
+     * @param c The callback to be unregistered.
+     * @throws ImsException if the subscription associated with this callback is valid, but
+     * the {@link ImsService} associated with the subscription is not available. This can happen if
+     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+     * reason.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterPublishStateCallback(@NonNull PublishStateCallback c)
+            throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null PublishStateCallback.");
+        }
+        IImsRcsController imsRcsController = getIImsRcsController();
+        if (imsRcsController == null) {
+            Log.e(TAG, "unregisterPublishStateCallback: IImsRcsController is null");
+            throw new ImsException("Cannot find remote IMS service",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+
+        try {
+            imsRcsController.unregisterUcePublishStateCallback(mSubId, c.getBinder());
+        } catch (android.os.ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling IImsRcsController#unregisterUcePublishStateCallback", e);
+            throw new ImsException("Remote IMS Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
      * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the
      * associated subscription.
      * <p>
@@ -341,7 +480,7 @@
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      */
-    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
     public boolean isUceSettingEnabled() throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
@@ -349,9 +488,10 @@
             throw new ImsException("Can not find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
-
         try {
-            return imsRcsController.isUceSettingEnabled(mSubId);
+            // Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this.
+            return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(),
+                    null /*featureId*/);
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -362,6 +502,10 @@
     /**
      * Change the user’s setting for whether or not UCE is enabled for the associated subscription.
      * <p>
+     * If an application Requires UCE, they may launch an Activity using the Intent
+     * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, which will ask the user if
+     * they wish to enable this feature.
+     * <p>
      * Note: This setting does not affect whether or not the device publishes its service
      * capabilities if the subscription supports presence publication.
      *
@@ -371,7 +515,10 @@
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     * @hide
      */
+    @SystemApi
+    @TestApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setUceSettingEnabled(boolean isEnabled) throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 5c86ba7..e085dec 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -196,11 +196,11 @@
         }
 
         /**
-         * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+         * Notifies the framework when the IMS Provider is unregistered from the IMS network.
          *
          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
          */
-        public void onUnregistered(@Nullable ImsReasonInfo info) {
+        public void onUnregistered(@NonNull ImsReasonInfo info) {
         }
 
         /**
@@ -211,7 +211,7 @@
          */
         public void onTechnologyChangeFailed(
                 @AccessNetworkConstants.TransportType int imsTransportType,
-                @Nullable ImsReasonInfo info) {
+                @NonNull ImsReasonInfo info) {
         }
 
         /**
@@ -270,7 +270,7 @@
      * inactive subscription, it will result in a no-op.
      *
      * @param c The {@link RegistrationCallback} to be removed.
-     * @see SubscriptionManager.OnSubscriptionsChangedListener
+     * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 6f6aa44..9e46142 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -19,6 +19,7 @@
 import android.net.Uri;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 
 import com.android.internal.telephony.IIntegerConsumer;
@@ -42,8 +43,11 @@
     boolean isAvailable(int subId, int capability);
 
     // ImsUceAdapter specific
-    void requestCapabilities(int subId, in List<Uri> contactNumbers, IRcsUceControllerCallback c);
+    void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
+            in List<Uri> contactNumbers, IRcsUceControllerCallback c);
     int getUcePublishState(int subId);
-    boolean isUceSettingEnabled(int subId);
+    boolean isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId);
     void setUceSettingEnabled(int subId, boolean isEnabled);
+    void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
+    void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
 }
diff --git a/telephony/java/android/telephony/DisplayInfo.aidl b/telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl
similarity index 67%
copy from telephony/java/android/telephony/DisplayInfo.aidl
copy to telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl
index 861b0fe..b6e8415 100644
--- a/telephony/java/android/telephony/DisplayInfo.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcsUcePublishStateCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 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.
@@ -13,6 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.telephony;
 
-parcelable DisplayInfo;
+package android.telephony.ims.aidl;
+
+/**
+ * Interface for RCS UCE publish state change callbacks.
+ *
+ * {@hide}
+ */
+oneway interface IRcsUcePublishStateCallback {
+    void onPublishStateChanged(int publishState);
+}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index a3ce1b5..9ec3f61 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -218,13 +218,7 @@
      * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and
      * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
      *
-     * The capabilities of this MmTelFeature will be set by the framework and can be queried with
-     * {@see #queryCapabilityStatus()}.
-     *
-     * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
-     * by sending a {@see #notifyCapabilitiesStatusChanged} callback to the framework. The current
-     * status can also be queried using {@see #queryCapabilityStatus()}.
-     * @see #isCapable(int)
+     * The capabilities of this MmTelFeature will be set by the framework.
      */
     public static class MmTelCapabilities extends Capabilities {
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 14a64d2..7069e0a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -175,9 +175,11 @@
      */
     public final void onDeregistered(ImsReasonInfo info) {
         updateToDisconnectedState(info);
+        // ImsReasonInfo should never be null.
+        final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
         mCallbacks.broadcastAction((c) -> {
             try {
-                c.onDeregistered(info);
+                c.onDeregistered(reasonInfo);
             } catch (RemoteException e) {
                 Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
                         "callback.");
@@ -194,9 +196,10 @@
      */
     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
             ImsReasonInfo info) {
+        final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
         mCallbacks.broadcastAction((c) -> {
             try {
-                c.onTechnologyChangeFailed(imsRadioTech, info);
+                c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
             } catch (RemoteException e) {
                 Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
                         "callback.");
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index ce9a73a..a9a33c0 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -403,8 +403,7 @@
                             message.mWrappedSmsMessage.mMessageRef,
                             STATUS_REPORT_STATUS_ERROR);
                 } else {
-                    Log.w(LOG_TAG,
-                            "onSmsStatusReportReceivedWithoutMessageRef: Invalid pdu entered.");
+                    Log.w(LOG_TAG, "onSmsStatusReportReceived: Invalid pdu entered.");
                     acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR);
                 }
             }
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index f13371c..5e0a3d8 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -338,6 +338,7 @@
 
     /**
      * Updates the configuration of the call barring for specified service class with password.
+     * @hide
      */
     public int updateCallBarringWithPassword(int cbType, int action, @Nullable String[] barrList,
             int serviceClass, @NonNull String password) {
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 52e4d33..8611d26b 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -16,8 +16,12 @@
 
 package android.telephony.mbms;
 
+import android.annotation.IntDef;
 import android.telephony.MbmsStreamingSession;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public class MbmsErrors {
     /**
      * Indicates that the middleware has sent an error code that is not defined in the version of
@@ -138,6 +142,13 @@
 
         /** Indicates the the middleware has no record of the supplied {@link FileInfo} */
         public static final int ERROR_UNKNOWN_FILE_INFO = 403;
+
+        /**
+         * Indicates that the service announcement file passed via
+         * {@link android.telephony.MbmsDownloadSession#addServiceAnnouncementFile(byte[])}
+         * is malformed.
+         */
+        public static final int ERROR_MALFORMED_SERVICE_ANNOUNCEMENT_FILE = 404;
     }
 
     /**
@@ -156,5 +167,35 @@
         public static final int ERROR_DUPLICATE_START_GROUP_CALL = 502;
     }
 
+    /** @hide */
+    @IntDef(value = {
+            SUCCESS,
+            ERROR_NO_UNIQUE_MIDDLEWARE,
+            ERROR_MIDDLEWARE_NOT_BOUND,
+            ERROR_MIDDLEWARE_LOST,
+            InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            GeneralErrors.ERROR_OUT_OF_MEMORY,
+            GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            GeneralErrors.ERROR_IN_E911,
+            GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+            StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+            StreamingErrors.ERROR_DUPLICATE_START_STREAM,
+            DownloadErrors.ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT,
+            DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST,
+            DownloadErrors.ERROR_UNKNOWN_FILE_INFO,
+            DownloadErrors.ERROR_MALFORMED_SERVICE_ANNOUNCEMENT_FILE,
+            GroupCallErrors.ERROR_UNABLE_TO_START_SERVICE,
+            GroupCallErrors.ERROR_DUPLICATE_START_GROUP_CALL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MbmsError {
+    }
+
     private MbmsErrors() {}
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index 445087fb..36136ab 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -35,6 +35,8 @@
 
     int setTempFileRootDirectory(int subId, String rootDirectoryPath);
 
+    int addServiceAnnouncementFile(int subId, in byte[] fileContents);
+
     int download(in DownloadRequest downloadRequest);
 
     int addStatusListener(in DownloadRequest downloadRequest,
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 9f22d0a..3279ce6 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -216,6 +216,28 @@
     }
 
     /**
+     * Called when the client application wishes to receive file information according to a
+     * service announcement file received from a group call server.
+     *
+     * The service announcement file is in the format of a multipart MIME file with XML parts,
+     * though no validation is performed on the contents of the {@code fileContents} argument --
+     * implementing middleware applications should perform their own validation and return
+     * {@link MbmsErrors.DownloadErrors#ERROR_MALFORMED_SERVICE_ANNOUNCEMENT_FILE} if the file is
+     * malformed.
+     *
+     * @param subscriptionId The subscription id the service announcement applies to.
+     * @param fileContents The contents of the service announcement file.
+     * @return {@link MbmsErrors#SUCCESS}, or
+     *         {@link MbmsErrors.DownloadErrors#ERROR_MALFORMED_SERVICE_ANNOUNCEMENT_FILE}
+     */
+    // TODO: are there any public specifications of what the file format is that I can link to?
+    @Override
+    public @MbmsErrors.MbmsError int addServiceAnnouncementFile(
+            int subscriptionId, @NonNull byte[] fileContents) {
+        return 0;
+    }
+
+    /**
      * Issues a request to download a set of files.
      *
      * The middleware should expect that {@link #setTempFileRootDirectory(int, String)} has been
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 96f77d8..d0cec52d 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -270,11 +270,12 @@
         /**
          * Requested expiration for Published Offline availability.
          * Value is in Integer format.
-         * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC}.
+         * @deprecated use
+         *     {@link ProvisioningManager#KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC}.
          */
         @Deprecated
         public static final int PUBLISH_TIMER_EXTENDED =
-                ProvisioningManager.KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC;
+                ProvisioningManager.KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC;
 
         /**
          *
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index f61d4e1..76fc4f7 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -74,7 +74,6 @@
     public static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER;
     public static final int EVENT_DATA_SETUP_COMPLETE = BASE + 0;
     public static final int EVENT_RADIO_AVAILABLE = BASE + 1;
-    public static final int EVENT_RECORDS_LOADED = BASE + 2;
     public static final int EVENT_TRY_SETUP_DATA = BASE + 3;
     public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = BASE + 6;
     public static final int EVENT_VOICE_CALL_STARTED = BASE + 7;
@@ -94,7 +93,6 @@
     public static final int EVENT_CLEAN_UP_CONNECTION = BASE + 24;
     public static final int EVENT_RESTART_RADIO = BASE + 26;
     public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 29;
-    public static final int EVENT_ICC_CHANGED = BASE + 33;
     public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
     public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36;
     public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37;
@@ -111,10 +109,10 @@
     public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
     public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50;
     public static final int EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED = BASE + 51;
-    public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52;
-    public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
-    public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
-    public static final int EVENT_UPDATE_CARRIER_CONFIGS = BASE + 55;
+    public static final int EVENT_TELEPHONY_DISPLAY_INFO_CHANGED = BASE + 52;
+    public static final int EVENT_NR_TIMER_WATCHDOG = BASE + 53;
+    public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 54;
+    public static final int EVENT_SIM_STATE_UPDATED = BASE + 55;
 
     /***** Constants *****/
 
@@ -126,4 +124,7 @@
     public static final String PROVISIONING_URL_KEY = "provisioningUrl";
     public static final String BANDWIDTH_SOURCE_MODEM_KEY = "modem";
     public static final String BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY = "carrier_config";
+    public static final String RAT_NAME_LTE = "LTE";
+    public static final String RAT_NAME_NR_NSA = "NR_NSA";
+    public static final String RAT_NAME_NR_NSA_MMWAVE = "NR_NSA_MMWAVE";
 }
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index c07a171..88da3c9 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -86,37 +86,9 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      * @param subId the subId id.
      */
-    void sendDataForSubscriber(int subId, String callingPkg, in String destAddr,
-            in String scAddr, in int destPort, in byte[] data, in PendingIntent sentIntent,
-            in PendingIntent deliveryIntent);
-
-    /**
-     * Send a data SMS. Only for use internally.
-     *
-     * @param smsc the SMSC to send the message through, or NULL for the
-     *  default SMSC
-     * @param data the body of the message to send
-     * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is sucessfully sent, or failed.
-     *  The result code will be <code>Activity.RESULT_OK<code> for success,
-     *  or one of these errors:<br>
-     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
-     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
-     *  <code>RESULT_ERROR_NULL_PDU</code><br>
-     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
-     *  the extra "errorCode" containing a radio technology specific value,
-     *  generally only useful for troubleshooting.<br>
-     *  The per-application based SMS control checks sentIntent. If sentIntent
-     *  is NULL the caller will be checked against all unknown applicaitons,
-     *  which cause smaller number of SMS to be sent in checking period.
-     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is delivered to the recipient.  The
-     *  raw pdu of the status report is in the extended data ("pdu").
-     * @param subId the subId id.
-     */
-    void sendDataForSubscriberWithSelfPermissions(int subId, String callingPkg, in String destAddr,
-            in String scAddr, in int destPort, in byte[] data, in PendingIntent sentIntent,
-            in PendingIntent deliveryIntent);
+    void sendDataForSubscriber(int subId, String callingPkg, String callingattributionTag,
+            in String destAddr, in String scAddr, in int destPort,in byte[] data,
+            in PendingIntent sentIntent, in PendingIntent deliveryIntent);
 
     /**
      * Send an SMS.
@@ -146,37 +118,9 @@
      *   by a non-default SMS app. Currently only the carrier app can set this
      *   parameter to false to skip auto message persistence.
      */
-    void sendTextForSubscriber(in int subId, String callingPkg, in String destAddr,
-            in String scAddr, in String text, in PendingIntent sentIntent,
-            in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp);
-
-    /**
-     * Send an SMS. Internal use only.
-     *
-     * @param smsc the SMSC to send the message through, or NULL for the
-     *  default SMSC
-     * @param text the body of the message to send
-     * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is sucessfully sent, or failed.
-     *  The result code will be <code>Activity.RESULT_OK<code> for success,
-     *  or one of these errors:<br>
-     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
-     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
-     *  <code>RESULT_ERROR_NULL_PDU</code><br>
-     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
-     *  the extra "errorCode" containing a radio technology specific value,
-     *  generally only useful for troubleshooting.<br>
-     *  The per-application based SMS control checks sentIntent. If sentIntent
-     *  is NULL the caller will be checked against all unknown applications,
-     *  which cause smaller number of SMS to be sent in checking period.
-     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is delivered to the recipient.  The
-     *  raw pdu of the status report is in the extended data ("pdu").
-     * @param subId the subId on which the SMS has to be sent.
-     */
-    void sendTextForSubscriberWithSelfPermissions(in int subId, String callingPkg,
+    void sendTextForSubscriber(in int subId, String callingPkg, String callingAttributionTag,
             in String destAddr, in String scAddr, in String text, in PendingIntent sentIntent,
-            in PendingIntent deliveryIntent, in boolean persistMessage);
+            in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp);
 
     /**
      * Send an SMS with options using Subscription Id.
@@ -224,10 +168,11 @@
      *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
      *  Any Other values included Negative considered as Invalid Validity Period of the message.
      */
-    void sendTextForSubscriberWithOptions(in int subId, String callingPkg, in String destAddr,
-            in String scAddr, in String text, in PendingIntent sentIntent,
-            in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp,
-            in int priority, in boolean expectMore, in int validityPeriod);
+    void sendTextForSubscriberWithOptions(in int subId, String callingPkg,
+            String callingAttributionTag, in String destAddr, in String scAddr, in String text,
+            in PendingIntent sentIntent, in PendingIntent deliveryIntent,
+            in boolean persistMessageForNonDefaultSmsApp, in int priority, in boolean expectMore,
+            in int validityPeriod);
 
     /**
      * Inject an SMS PDU into the android platform.
@@ -272,7 +217,7 @@
      *   parameter to false to skip auto message persistence.
      */
     void sendMultipartTextForSubscriber(in int subId, String callingPkg,
-            in String destinationAddress, in String scAddress,
+            String callingAttributionTag, in String destinationAddress, in String scAddress,
             in List<String> parts, in List<PendingIntent> sentIntents,
             in List<PendingIntent> deliveryIntents, in boolean persistMessageForNonDefaultSmsApp);
 
@@ -321,10 +266,10 @@
      *  Any Other values included Negative considered as Invalid Validity Period of the message.
      */
     void sendMultipartTextForSubscriberWithOptions(in int subId, String callingPkg,
-            in String destinationAddress, in String scAddress, in List<String> parts,
-            in List<PendingIntent> sentIntents, in List<PendingIntent> deliveryIntents,
-            in boolean persistMessageForNonDefaultSmsApp, in int priority, in boolean expectMore,
-            in int validityPeriod);
+            String callingAttributionTag, in String destinationAddress, in String scAddress,
+            in List<String> parts, in List<PendingIntent> sentIntents,
+            in List<PendingIntent> deliveryIntents, in boolean persistMessageForNonDefaultSmsApp,
+            in int priority, in boolean expectMore, in int validityPeriod);
 
     /**
      * Enable reception of cell broadcast (SMS-CB) messages with the given
@@ -482,6 +427,7 @@
      *
      * @param subId the SIM id.
      * @param callingPkg the package name of the calling app
+     * @param callingAttributionTag the attribution tag of calling context
      * @param messageUri the URI of the stored message
      * @param scAddress is the service center address or null to use the current default SMSC
      * @param sentIntent if not NULL this <code>PendingIntent</code> is
@@ -501,8 +447,9 @@
      *  broadcast when the message is delivered to the recipient.  The
      *  raw pdu of the status report is in the extended data ("pdu").
      */
-    void sendStoredText(int subId, String callingPkg, in Uri messageUri, String scAddress,
-            in PendingIntent sentIntent, in PendingIntent deliveryIntent);
+    void sendStoredText(int subId, String callingPkg, String callingAttributionTag,
+            in Uri messageUri, String scAddress, in PendingIntent sentIntent,
+            in PendingIntent deliveryIntent);
 
     /**
      * Send a system stored multi-part text message.
@@ -514,6 +461,7 @@
      *
      * @param subId the SIM id.
      * @param callingPkg the package name of the calling app
+     * @param callingAttributeTag the attribute tag of the calling context
      * @param messageUri the URI of the stored message
      * @param scAddress is the service center address or null to use
      *   the current default SMSC
@@ -537,8 +485,8 @@
      *   to the recipient.  The raw pdu of the status report is in the
      *   extended data ("pdu").
      */
-    void sendStoredMultipartText(int subId, String callingPkg, in Uri messageUri,
-                String scAddress, in List<PendingIntent> sentIntents,
+    void sendStoredMultipartText(int subId, String callingPkg, String callingAttributeTag,
+                in Uri messageUri, String scAddress, in List<PendingIntent> sentIntents,
                 in List<PendingIntent> deliveryIntents);
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
index ddd3457..51af6de 100644
--- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java
+++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
@@ -45,38 +45,25 @@
     }
 
     @Override
-    public void sendDataForSubscriber(int subId, String callingPkg, String destAddr,
-            String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
-            PendingIntent deliveryIntent) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPkg,
+    public void sendDataForSubscriber(int subId, String callingPkg, String callingAttributionTag,
             String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
             PendingIntent deliveryIntent) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void sendTextForSubscriber(int subId, String callingPkg, String destAddr,
-            String scAddr, String text, PendingIntent sentIntent,
+    public void sendTextForSubscriber(int subId, String callingPkg, String callingAttributionTag,
+            String destAddr, String scAddr, String text, PendingIntent sentIntent,
             PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPkg,
-            String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, boolean persistMessage) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void sendTextForSubscriberWithOptions(int subId, String callingPkg, String destAddr,
-            String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, boolean persistMessageForNonDefaultSmsApp,
-            int priority, boolean expectMore, int validityPeriod) {
+    public void sendTextForSubscriberWithOptions(int subId, String callingPkg,
+            String callingAttributionTag, String destAddr, String scAddr, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessageForNonDefaultSmsApp, int priority, boolean expectMore,
+            int validityPeriod) {
         throw new UnsupportedOperationException();
     }
 
@@ -88,7 +75,7 @@
 
     @Override
     public void sendMultipartTextForSubscriber(int subId, String callingPkg,
-            String destinationAddress, String scAddress,
+            String callingAttributionTag, String destinationAddress, String scAddress,
             List<String> parts, List<PendingIntent> sentIntents,
             List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
         throw new UnsupportedOperationException();
@@ -96,7 +83,7 @@
 
     @Override
     public void sendMultipartTextForSubscriberWithOptions(int subId, String callingPkg,
-            String destinationAddress, String scAddress,
+            String callingAttributionTag, String destinationAddress, String scAddress,
             List<String> parts, List<PendingIntent> sentIntents,
             List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp,
             int priority, boolean expectMore, int validityPeriod) {
@@ -173,14 +160,15 @@
     }
 
     @Override
-    public void sendStoredText(int subId, String callingPkg, Uri messageUri, String scAddress,
-            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+    public void sendStoredText(int subId, String callingPkg, String callingAttributionTag,
+            Uri messageUri, String scAddress, PendingIntent sentIntent,
+            PendingIntent deliveryIntent) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void sendStoredMultipartText(int subId, String callingPkg, Uri messageUri,
-            String scAddress, List<PendingIntent> sentIntents,
+    public void sendStoredMultipartText(int subId, String callingPkg, String callingAttributionTag,
+            Uri messageUri, String scAddress, List<PendingIntent> sentIntents,
             List<PendingIntent> deliveryIntents) {
         throw new UnsupportedOperationException();
     }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index eb669a6..5293efa 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -22,6 +22,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
 import android.os.ResultReceiver;
 import android.os.WorkSource;
 import android.net.NetworkStats;
@@ -281,7 +282,7 @@
      * operator's MCC (Mobile Country Code).
      * @see android.telephony.TelephonyManager#getNetworkCountryIso
      */
-    String getNetworkCountryIsoForPhone(int phoneId, String callingPkg, String callingFeatureId);
+    String getNetworkCountryIsoForPhone(int phoneId);
 
     /**
      * Returns the neighboring cell information of the device.
@@ -472,8 +473,8 @@
      * Send a visual voicemail SMS. Internal use only.
      * Requires caller to be the default dialer and have SEND_SMS permission
      */
-    void sendVisualVoicemailSmsForSubscriber(in String callingPackage, in int subId,
-            in String number, in int port, in String text, in PendingIntent sentIntent);
+    void sendVisualVoicemailSmsForSubscriber(in String callingPackage, String callingAttributeTag,
+            in int subId, in String number, in int port, in String text, in PendingIntent sentIntent);
 
     // Send the special dialer code. The IPC caller must be the current default dialer.
     void sendDialerSpecialCode(String callingPackageName, String inputCode);
@@ -1816,14 +1817,6 @@
     boolean switchSlots(in int[] physicalSlots);
 
     /**
-     * Sets radio indication update mode. This can be used to control the behavior of indication
-     * update from modem to Android frameworks. For example, by default several indication updates
-     * are turned off when screen is off, but in some special cases (e.g. carkit is connected but
-     * screen is off) we want to turn on those indications even when the screen is off.
-     */
-    void setRadioIndicationUpdateMode(int subId, int filters, int mode);
-
-    /**
      * Returns whether mobile data roaming is enabled on the subscription with id {@code subId}.
      *
      * @param subId the subscription id
@@ -2129,9 +2122,14 @@
     void notifyOtaEmergencyNumberDbInstalled();
 
     /**
-     * Override the file partition name for testing OTA emergency number database.
+     * Override a customized file partition name for OTA emergency number database.
      */
-    void updateTestOtaEmergencyNumberDbFilePath(String otaFilePath);
+    void updateOtaEmergencyNumberDbFilePath(in ParcelFileDescriptor otaParcelFileDescriptor);
+
+    /**
+     * Reset file partition to default for OTA emergency number database.
+     */
+    void resetOtaEmergencyNumberDbFilePath();
 
     /**
      * Enable or disable a logical modem stack associated with the slotIndex.
@@ -2206,7 +2204,8 @@
      * Enqueue a pending sms Consumer, which will answer with the user specified selection for an
      * outgoing SmsManager operation.
      */
-    oneway void enqueueSmsPickResult(String callingPackage, IIntegerConsumer subIdResult);
+    oneway void enqueueSmsPickResult(String callingPackage, String callingAttributeTag,
+        IIntegerConsumer subIdResult);
 
     /**
      * Returns the MMS user agent.
@@ -2249,4 +2248,9 @@
     int setIccLockEnabled(int subId, boolean enabled, String password);
 
     int changeIccLockPassword(int subId, String oldPassword, String newPassword);
+
+    /**
+     * Whether device can connect to 5G network when two SIMs are active.
+     */
+    boolean canConnectTo5GInDsdsMode();
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 6eea88f..db9fdf5 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -111,6 +111,7 @@
     public static final int PIN_RESULT_SUCCESS = 0;
     public static final int PIN_PASSWORD_INCORRECT = 1;
     public static final int PIN_GENERAL_FAILURE = 2;
+    public static final int PIN_OPERATION_ABORTED = 3;
 
     /**
      * Return codes for <code>enableApnType()</code>
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 53ad70a..d524299 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -492,6 +492,8 @@
     int RIL_REQUEST_ENABLE_UICC_APPLICATIONS = 208;
     int RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT = 209;
     int RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS = 210;
+    int RIL_REQUEST_GET_BARRING_INFO = 211;
+    int RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION = 212;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -557,4 +559,5 @@
     int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102;
     int RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED = 1103;
     int RIL_UNSOL_REGISTRATION_FAILED = 1104;
+    int RIL_UNSOL_BARRING_INFO_CHANGED = 1105;
 }
diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java
index ab3fdf4..2f3897b 100644
--- a/telephony/java/com/android/internal/telephony/SmsHeader.java
+++ b/telephony/java/com/android/internal/telephony/SmsHeader.java
@@ -23,6 +23,8 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * SMS user data header, as specified in TS 23.040 9.2.3.24.
@@ -71,6 +73,25 @@
     public static final int PORT_WAP_PUSH = 2948;
     public static final int PORT_WAP_WSP  = 9200;
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SmsHeader smsHeader = (SmsHeader) o;
+        return languageTable == smsHeader.languageTable
+                && languageShiftTable == smsHeader.languageShiftTable
+                && Objects.equals(portAddrs, smsHeader.portAddrs)
+                && Objects.equals(concatRef, smsHeader.concatRef)
+                && Objects.equals(specialSmsMsgList, smsHeader.specialSmsMsgList)
+                && Objects.equals(miscEltList, smsHeader.miscEltList);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(portAddrs, concatRef, specialSmsMsgList, miscEltList, languageTable,
+                languageShiftTable);
+    }
+
     public static class PortAddrs {
         @UnsupportedAppUsage
         public PortAddrs() {
@@ -81,6 +102,21 @@
         @UnsupportedAppUsage
         public int origPort;
         public boolean areEightBits;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            PortAddrs portAddrs = (PortAddrs) o;
+            return destPort == portAddrs.destPort
+                    && origPort == portAddrs.origPort
+                    && areEightBits == portAddrs.areEightBits;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(destPort, origPort, areEightBits);
+        }
     }
 
     public static class ConcatRef {
@@ -95,11 +131,41 @@
         @UnsupportedAppUsage
         public int msgCount;
         public boolean isEightBits;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            ConcatRef concatRef = (ConcatRef) o;
+            return refNumber == concatRef.refNumber
+                    && seqNumber == concatRef.seqNumber
+                    && msgCount == concatRef.msgCount
+                    && isEightBits == concatRef.isEightBits;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(refNumber, seqNumber, msgCount, isEightBits);
+        }
     }
 
     public static class SpecialSmsMsg {
         public int msgIndType;
         public int msgCount;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            SpecialSmsMsg that = (SpecialSmsMsg) o;
+            return msgIndType == that.msgIndType
+                    && msgCount == that.msgCount;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(msgIndType, msgCount);
+        }
     }
 
     /**
@@ -109,6 +175,22 @@
     public static class MiscElt {
         public int id;
         public byte[] data;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            MiscElt miscElt = (MiscElt) o;
+            return id == miscElt.id
+                    && Arrays.equals(data, miscElt.data);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = Objects.hash(id);
+            result = 31 * result + Arrays.hashCode(data);
+            return result;
+        }
     }
 
     @UnsupportedAppUsage
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index ff70f8b..29286e8 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -240,5 +240,5 @@
      * two.
      * Type: int
      */
-    static final String PROPERTY_MAX_ACTIVE_MODEMS = "ro.telephony.max.active.modems";
+    static final String PROPERTY_MAX_ACTIVE_MODEMS = "telephony.active_modems.max_count";
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 74eb51d..542e08d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -17,7 +17,6 @@
 package com.android.internal.telephony.cdma;
 
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.res.Resources;
 import android.os.Build;
 import android.sysprop.TelephonyProperties;
 import android.telephony.PhoneNumberUtils;
@@ -28,7 +27,6 @@
 import android.util.Log;
 
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.Sms7BitEncodingTranslator;
 import com.android.internal.telephony.SmsAddress;
 import com.android.internal.telephony.SmsConstants;
 import com.android.internal.telephony.SmsHeader;
@@ -416,15 +414,7 @@
     @UnsupportedAppUsage
     public static TextEncodingDetails calculateLength(CharSequence messageBody,
             boolean use7bitOnly, boolean isEntireMsg) {
-        CharSequence newMsgBody = null;
-        Resources r = Resources.getSystem();
-        if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
-            newMsgBody = Sms7BitEncodingTranslator.translate(messageBody, true /* isCdmaFormat */);
-        }
-        if (TextUtils.isEmpty(newMsgBody)) {
-            newMsgBody = messageBody;
-        }
-        return BearerData.calcTextEncodingDetails(newMsgBody, use7bitOnly, isEntireMsg);
+        return BearerData.calcTextEncodingDetails(messageBody, use7bitOnly, isEntireMsg);
     }
 
     /**
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 48cb1cd..3f85de3 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -21,9 +21,11 @@
 import android.telephony.SmsCbCmasInfo;
 import android.telephony.cdma.CdmaSmsCbProgramData;
 import android.telephony.cdma.CdmaSmsCbProgramResults;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
 import com.android.internal.telephony.SmsConstants;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
@@ -540,8 +542,17 @@
      */
     public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg,
             boolean force7BitEncoding, boolean isEntireMsg) {
+        CharSequence newMsg = null;
+        Resources r = Resources.getSystem();
+        if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
+            newMsg = Sms7BitEncodingTranslator.translate(msg, true /* isCdmaFormat */);
+        }
+        if (TextUtils.isEmpty(newMsg)) {
+            newMsg = msg;
+        }
+
         TextEncodingDetails ted;
-        int septets = countAsciiSeptets(msg, force7BitEncoding);
+        int septets = countAsciiSeptets(newMsg, force7BitEncoding);
         if (septets != -1 && septets <= SmsConstants.MAX_USER_DATA_SEPTETS) {
             ted = new TextEncodingDetails();
             ted.msgCount = 1;
diff --git a/test-base/api/lint-baseline.txt b/test-base/api/lint-baseline.txt
new file mode 100644
index 0000000..86c8a91
--- /dev/null
+++ b/test-base/api/lint-baseline.txt
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+MissingNullability: junit.framework.ComparisonFailure#getMessage():
+    Missing nullability on method `getMessage` return
diff --git a/test-mock/api/lint-baseline.txt b/test-mock/api/lint-baseline.txt
new file mode 100644
index 0000000..c6ba3f5
--- /dev/null
+++ b/test-mock/api/lint-baseline.txt
@@ -0,0 +1,143 @@
+// Baseline format: 1.0
+ArrayReturn: android.test.mock.MockContentProvider#bulkInsert(android.net.Uri, android.content.ContentValues[]) parameter #1:
+    Method parameter should be Collection<ContentValues> (or subclass) instead of raw array; was `android.content.ContentValues[]`
+ArrayReturn: android.test.mock.MockResources#getTextArray(int):
+    Method should return Collection<CharSequence> (or subclass) instead of raw array; was `java.lang.CharSequence[]`
+
+
+MissingNullability: android.test.mock.MockApplication#onConfigurationChanged(android.content.res.Configuration) parameter #0:
+    Missing nullability on parameter `newConfig` in method `onConfigurationChanged`
+MissingNullability: android.test.mock.MockContentProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #0:
+    Missing nullability on parameter `context` in method `attachInfo`
+MissingNullability: android.test.mock.MockContentProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #1:
+    Missing nullability on parameter `info` in method `attachInfo`
+MissingNullability: android.test.mock.MockContentProvider#bulkInsert(android.net.Uri, android.content.ContentValues[]) parameter #0:
+    Missing nullability on parameter `uri` in method `bulkInsert`
+MissingNullability: android.test.mock.MockContentProvider#bulkInsert(android.net.Uri, android.content.ContentValues[]) parameter #1:
+    Missing nullability on parameter `values` in method `bulkInsert`
+MissingNullability: android.test.mock.MockContentProvider#getStreamTypes(android.net.Uri, String):
+    Missing nullability on method `getStreamTypes` return
+MissingNullability: android.test.mock.MockContentProvider#getStreamTypes(android.net.Uri, String) parameter #0:
+    Missing nullability on parameter `url` in method `getStreamTypes`
+MissingNullability: android.test.mock.MockContentProvider#getStreamTypes(android.net.Uri, String) parameter #1:
+    Missing nullability on parameter `mimeTypeFilter` in method `getStreamTypes`
+MissingNullability: android.test.mock.MockContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean) parameter #0:
+    Missing nullability on parameter `uri` in method `notifyChange`
+MissingNullability: android.test.mock.MockContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean) parameter #1:
+    Missing nullability on parameter `observer` in method `notifyChange`
+MissingNullability: android.test.mock.MockContext#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #0:
+    Missing nullability on parameter `service` in method `bindIsolatedService`
+MissingNullability: android.test.mock.MockContext#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #2:
+    Missing nullability on parameter `instanceName` in method `bindIsolatedService`
+MissingNullability: android.test.mock.MockContext#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #3:
+    Missing nullability on parameter `executor` in method `bindIsolatedService`
+MissingNullability: android.test.mock.MockContext#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #4:
+    Missing nullability on parameter `conn` in method `bindIsolatedService`
+MissingNullability: android.test.mock.MockContext#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #0:
+    Missing nullability on parameter `service` in method `bindService`
+MissingNullability: android.test.mock.MockContext#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #2:
+    Missing nullability on parameter `executor` in method `bindService`
+MissingNullability: android.test.mock.MockContext#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #3:
+    Missing nullability on parameter `conn` in method `bindService`
+MissingNullability: android.test.mock.MockContext#getMainExecutor():
+    Missing nullability on method `getMainExecutor` return
+MissingNullability: android.test.mock.MockContext#sendOrderedBroadcast(android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle) parameter #0:
+    Missing nullability on parameter `intent` in method `sendOrderedBroadcast`
+MissingNullability: android.test.mock.MockContext#sendOrderedBroadcast(android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle) parameter #1:
+    Missing nullability on parameter `receiverPermission` in method `sendOrderedBroadcast`
+MissingNullability: android.test.mock.MockContext#sendOrderedBroadcast(android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle) parameter #2:
+    Missing nullability on parameter `receiverAppOp` in method `sendOrderedBroadcast`
+MissingNullability: android.test.mock.MockContext#sendOrderedBroadcast(android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle) parameter #3:
+    Missing nullability on parameter `resultReceiver` in method `sendOrderedBroadcast`
+MissingNullability: android.test.mock.MockContext#sendOrderedBroadcast(android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle) parameter #4:
+    Missing nullability on parameter `scheduler` in method `sendOrderedBroadcast`
+MissingNullability: android.test.mock.MockContext#sendOrderedBroadcast(android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle) parameter #6:
+    Missing nullability on parameter `initialData` in method `sendOrderedBroadcast`
+MissingNullability: android.test.mock.MockContext#sendOrderedBroadcast(android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle) parameter #7:
+    Missing nullability on parameter `initialExtras` in method `sendOrderedBroadcast`
+MissingNullability: android.test.mock.MockContext#updateServiceGroup(android.content.ServiceConnection, int, int) parameter #0:
+    Missing nullability on parameter `conn` in method `updateServiceGroup`
+MissingNullability: android.test.mock.MockCursor#getNotificationUris():
+    Missing nullability on method `getNotificationUris` return
+MissingNullability: android.test.mock.MockCursor#setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>) parameter #0:
+    Missing nullability on parameter `cr` in method `setNotificationUris`
+MissingNullability: android.test.mock.MockCursor#setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>) parameter #1:
+    Missing nullability on parameter `uris` in method `setNotificationUris`
+MissingNullability: android.test.mock.MockPackageManager#getPackageArchiveInfo(String, int):
+    Missing nullability on method `getPackageArchiveInfo` return
+MissingNullability: android.test.mock.MockPackageManager#getPackageArchiveInfo(String, int) parameter #0:
+    Missing nullability on parameter `archiveFilePath` in method `getPackageArchiveInfo`
+MissingNullability: android.test.mock.MockPackageManager#hasSigningCertificate(String, byte[], int) parameter #0:
+    Missing nullability on parameter `packageName` in method `hasSigningCertificate`
+MissingNullability: android.test.mock.MockPackageManager#hasSigningCertificate(String, byte[], int) parameter #1:
+    Missing nullability on parameter `certificate` in method `hasSigningCertificate`
+MissingNullability: android.test.mock.MockPackageManager#hasSigningCertificate(int, byte[], int) parameter #1:
+    Missing nullability on parameter `certificate` in method `hasSigningCertificate`
+MissingNullability: android.test.mock.MockResources#getAnimation(int):
+    Missing nullability on method `getAnimation` return
+MissingNullability: android.test.mock.MockResources#getConfiguration():
+    Missing nullability on method `getConfiguration` return
+MissingNullability: android.test.mock.MockResources#getDisplayMetrics():
+    Missing nullability on method `getDisplayMetrics` return
+MissingNullability: android.test.mock.MockResources#getIdentifier(String, String, String) parameter #0:
+    Missing nullability on parameter `name` in method `getIdentifier`
+MissingNullability: android.test.mock.MockResources#getIdentifier(String, String, String) parameter #1:
+    Missing nullability on parameter `defType` in method `getIdentifier`
+MissingNullability: android.test.mock.MockResources#getIdentifier(String, String, String) parameter #2:
+    Missing nullability on parameter `defPackage` in method `getIdentifier`
+MissingNullability: android.test.mock.MockResources#getIntArray(int):
+    Missing nullability on method `getIntArray` return
+MissingNullability: android.test.mock.MockResources#getLayout(int):
+    Missing nullability on method `getLayout` return
+MissingNullability: android.test.mock.MockResources#getQuantityString(int, int):
+    Missing nullability on method `getQuantityString` return
+MissingNullability: android.test.mock.MockResources#getQuantityString(int, int, java.lang.Object...):
+    Missing nullability on method `getQuantityString` return
+MissingNullability: android.test.mock.MockResources#getQuantityString(int, int, java.lang.Object...) parameter #2:
+    Missing nullability on parameter `formatArgs` in method `getQuantityString`
+MissingNullability: android.test.mock.MockResources#getQuantityText(int, int):
+    Missing nullability on method `getQuantityText` return
+MissingNullability: android.test.mock.MockResources#getResourceEntryName(int):
+    Missing nullability on method `getResourceEntryName` return
+MissingNullability: android.test.mock.MockResources#getResourceName(int):
+    Missing nullability on method `getResourceName` return
+MissingNullability: android.test.mock.MockResources#getResourcePackageName(int):
+    Missing nullability on method `getResourcePackageName` return
+MissingNullability: android.test.mock.MockResources#getResourceTypeName(int):
+    Missing nullability on method `getResourceTypeName` return
+MissingNullability: android.test.mock.MockResources#getString(int):
+    Missing nullability on method `getString` return
+MissingNullability: android.test.mock.MockResources#getString(int, java.lang.Object...):
+    Missing nullability on method `getString` return
+MissingNullability: android.test.mock.MockResources#getString(int, java.lang.Object...) parameter #1:
+    Missing nullability on parameter `formatArgs` in method `getString`
+MissingNullability: android.test.mock.MockResources#getStringArray(int):
+    Missing nullability on method `getStringArray` return
+MissingNullability: android.test.mock.MockResources#getText(int):
+    Missing nullability on method `getText` return
+MissingNullability: android.test.mock.MockResources#getText(int, CharSequence):
+    Missing nullability on method `getText` return
+MissingNullability: android.test.mock.MockResources#getText(int, CharSequence) parameter #1:
+    Missing nullability on parameter `def` in method `getText`
+MissingNullability: android.test.mock.MockResources#getTextArray(int):
+    Missing nullability on method `getTextArray` return
+MissingNullability: android.test.mock.MockResources#getValue(String, android.util.TypedValue, boolean) parameter #0:
+    Missing nullability on parameter `name` in method `getValue`
+MissingNullability: android.test.mock.MockResources#getValue(String, android.util.TypedValue, boolean) parameter #1:
+    Missing nullability on parameter `outValue` in method `getValue`
+MissingNullability: android.test.mock.MockResources#getValue(int, android.util.TypedValue, boolean) parameter #1:
+    Missing nullability on parameter `outValue` in method `getValue`
+MissingNullability: android.test.mock.MockResources#getXml(int):
+    Missing nullability on method `getXml` return
+MissingNullability: android.test.mock.MockResources#obtainAttributes(android.util.AttributeSet, int[]):
+    Missing nullability on method `obtainAttributes` return
+MissingNullability: android.test.mock.MockResources#obtainAttributes(android.util.AttributeSet, int[]) parameter #0:
+    Missing nullability on parameter `set` in method `obtainAttributes`
+MissingNullability: android.test.mock.MockResources#obtainAttributes(android.util.AttributeSet, int[]) parameter #1:
+    Missing nullability on parameter `attrs` in method `obtainAttributes`
+MissingNullability: android.test.mock.MockResources#obtainTypedArray(int):
+    Missing nullability on method `obtainTypedArray` return
+MissingNullability: android.test.mock.MockResources#openRawResource(int):
+    Missing nullability on method `openRawResource` return
+MissingNullability: android.test.mock.MockResources#openRawResourceFd(int):
+    Missing nullability on method `openRawResourceFd` return
diff --git a/test-mock/api/system-lint-baseline.txt b/test-mock/api/system-lint-baseline.txt
new file mode 100644
index 0000000..466bc53
--- /dev/null
+++ b/test-mock/api/system-lint-baseline.txt
@@ -0,0 +1,15 @@
+// Baseline format: 1.0
+IntentBuilderName: android.test.mock.MockContext#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler):
+    Methods creating an Intent should be named `create<Foo>Intent()`, was `registerReceiverForAllUsers`
+
+
+MissingNullability: android.test.mock.MockContext#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler):
+    Missing nullability on method `registerReceiverForAllUsers` return
+MissingNullability: android.test.mock.MockContext#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler) parameter #0:
+    Missing nullability on parameter `receiver` in method `registerReceiverForAllUsers`
+MissingNullability: android.test.mock.MockContext#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler) parameter #1:
+    Missing nullability on parameter `filter` in method `registerReceiverForAllUsers`
+MissingNullability: android.test.mock.MockContext#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler) parameter #2:
+    Missing nullability on parameter `broadcastPermission` in method `registerReceiverForAllUsers`
+MissingNullability: android.test.mock.MockContext#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler) parameter #3:
+    Missing nullability on parameter `scheduler` in method `registerReceiverForAllUsers`
diff --git a/test-runner/api/lint-baseline.txt b/test-runner/api/lint-baseline.txt
new file mode 100644
index 0000000..54947fe
--- /dev/null
+++ b/test-runner/api/lint-baseline.txt
@@ -0,0 +1,173 @@
+// Baseline format: 1.0
+GenericException: android.test.ActivityInstrumentationTestCase#setUp():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ActivityInstrumentationTestCase#tearDown():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ActivityInstrumentationTestCase2#runTest():
+    Methods must not throw generic exceptions (`java.lang.Throwable`)
+GenericException: android.test.ActivityInstrumentationTestCase2#setUp():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ActivityInstrumentationTestCase2#tearDown():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ActivityUnitTestCase#setUp():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ActivityUnitTestCase#tearDown():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ApplicationTestCase#setUp():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ApplicationTestCase#tearDown():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ProviderTestCase#setUp():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ProviderTestCase#tearDown():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ProviderTestCase2#setUp():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ProviderTestCase2#tearDown():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ServiceTestCase#setUp():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.ServiceTestCase#tearDown():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.SingleLaunchActivityTestCase#setUp():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.SingleLaunchActivityTestCase#tearDown():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+GenericException: android.test.SyncBaseInstrumentation#setUp():
+    Methods must not throw generic exceptions (`java.lang.Exception`)
+
+
+IntentBuilderName: android.test.IsolatedContext#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter):
+    Methods creating an Intent should be named `create<Foo>Intent()`, was `registerReceiver`
+
+
+MissingNullability: android.test.ComparisonFailure#getMessage():
+    Missing nullability on method `getMessage` return
+MissingNullability: android.test.InstrumentationTestRunner#onCreate(android.os.Bundle) parameter #0:
+    Missing nullability on parameter `arguments` in method `onCreate`
+MissingNullability: android.test.IsolatedContext#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #0:
+    Missing nullability on parameter `service` in method `bindIsolatedService`
+MissingNullability: android.test.IsolatedContext#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #2:
+    Missing nullability on parameter `instanceName` in method `bindIsolatedService`
+MissingNullability: android.test.IsolatedContext#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #3:
+    Missing nullability on parameter `executor` in method `bindIsolatedService`
+MissingNullability: android.test.IsolatedContext#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #4:
+    Missing nullability on parameter `conn` in method `bindIsolatedService`
+MissingNullability: android.test.IsolatedContext#bindService(android.content.Intent, android.content.ServiceConnection, int) parameter #0:
+    Missing nullability on parameter `service` in method `bindService`
+MissingNullability: android.test.IsolatedContext#bindService(android.content.Intent, android.content.ServiceConnection, int) parameter #1:
+    Missing nullability on parameter `conn` in method `bindService`
+MissingNullability: android.test.IsolatedContext#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #0:
+    Missing nullability on parameter `service` in method `bindService`
+MissingNullability: android.test.IsolatedContext#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #2:
+    Missing nullability on parameter `executor` in method `bindService`
+MissingNullability: android.test.IsolatedContext#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection) parameter #3:
+    Missing nullability on parameter `conn` in method `bindService`
+MissingNullability: android.test.IsolatedContext#checkUriPermission(android.net.Uri, String, String, int, int, int) parameter #0:
+    Missing nullability on parameter `uri` in method `checkUriPermission`
+MissingNullability: android.test.IsolatedContext#checkUriPermission(android.net.Uri, String, String, int, int, int) parameter #1:
+    Missing nullability on parameter `readPermission` in method `checkUriPermission`
+MissingNullability: android.test.IsolatedContext#checkUriPermission(android.net.Uri, String, String, int, int, int) parameter #2:
+    Missing nullability on parameter `writePermission` in method `checkUriPermission`
+MissingNullability: android.test.IsolatedContext#checkUriPermission(android.net.Uri, int, int, int) parameter #0:
+    Missing nullability on parameter `uri` in method `checkUriPermission`
+MissingNullability: android.test.IsolatedContext#getContentResolver():
+    Missing nullability on method `getContentResolver` return
+MissingNullability: android.test.IsolatedContext#getFilesDir():
+    Missing nullability on method `getFilesDir` return
+MissingNullability: android.test.IsolatedContext#getSystemService(String):
+    Missing nullability on method `getSystemService` return
+MissingNullability: android.test.IsolatedContext#getSystemService(String) parameter #0:
+    Missing nullability on parameter `name` in method `getSystemService`
+MissingNullability: android.test.IsolatedContext#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter):
+    Missing nullability on method `registerReceiver` return
+MissingNullability: android.test.IsolatedContext#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter) parameter #0:
+    Missing nullability on parameter `receiver` in method `registerReceiver`
+MissingNullability: android.test.IsolatedContext#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter) parameter #1:
+    Missing nullability on parameter `filter` in method `registerReceiver`
+MissingNullability: android.test.IsolatedContext#sendBroadcast(android.content.Intent) parameter #0:
+    Missing nullability on parameter `intent` in method `sendBroadcast`
+MissingNullability: android.test.IsolatedContext#sendOrderedBroadcast(android.content.Intent, String) parameter #0:
+    Missing nullability on parameter `intent` in method `sendOrderedBroadcast`
+MissingNullability: android.test.IsolatedContext#sendOrderedBroadcast(android.content.Intent, String) parameter #1:
+    Missing nullability on parameter `receiverPermission` in method `sendOrderedBroadcast`
+MissingNullability: android.test.IsolatedContext#unregisterReceiver(android.content.BroadcastReceiver) parameter #0:
+    Missing nullability on parameter `receiver` in method `unregisterReceiver`
+MissingNullability: android.test.RenamingDelegatingContext#databaseList():
+    Missing nullability on method `databaseList` return
+MissingNullability: android.test.RenamingDelegatingContext#deleteDatabase(String) parameter #0:
+    Missing nullability on parameter `name` in method `deleteDatabase`
+MissingNullability: android.test.RenamingDelegatingContext#deleteFile(String) parameter #0:
+    Missing nullability on parameter `name` in method `deleteFile`
+MissingNullability: android.test.RenamingDelegatingContext#fileList():
+    Missing nullability on method `fileList` return
+MissingNullability: android.test.RenamingDelegatingContext#getCacheDir():
+    Missing nullability on method `getCacheDir` return
+MissingNullability: android.test.RenamingDelegatingContext#getDatabasePath(String):
+    Missing nullability on method `getDatabasePath` return
+MissingNullability: android.test.RenamingDelegatingContext#getDatabasePath(String) parameter #0:
+    Missing nullability on parameter `name` in method `getDatabasePath`
+MissingNullability: android.test.RenamingDelegatingContext#getFileStreamPath(String):
+    Missing nullability on method `getFileStreamPath` return
+MissingNullability: android.test.RenamingDelegatingContext#getFileStreamPath(String) parameter #0:
+    Missing nullability on parameter `name` in method `getFileStreamPath`
+MissingNullability: android.test.RenamingDelegatingContext#openFileInput(String):
+    Missing nullability on method `openFileInput` return
+MissingNullability: android.test.RenamingDelegatingContext#openFileInput(String) parameter #0:
+    Missing nullability on parameter `name` in method `openFileInput`
+MissingNullability: android.test.RenamingDelegatingContext#openFileOutput(String, int):
+    Missing nullability on method `openFileOutput` return
+MissingNullability: android.test.RenamingDelegatingContext#openFileOutput(String, int) parameter #0:
+    Missing nullability on parameter `name` in method `openFileOutput`
+MissingNullability: android.test.RenamingDelegatingContext#openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory):
+    Missing nullability on method `openOrCreateDatabase` return
+MissingNullability: android.test.RenamingDelegatingContext#openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory) parameter #0:
+    Missing nullability on parameter `name` in method `openOrCreateDatabase`
+MissingNullability: android.test.RenamingDelegatingContext#openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory) parameter #2:
+    Missing nullability on parameter `factory` in method `openOrCreateDatabase`
+MissingNullability: android.test.RenamingDelegatingContext#openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler):
+    Missing nullability on method `openOrCreateDatabase` return
+MissingNullability: android.test.RenamingDelegatingContext#openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler) parameter #0:
+    Missing nullability on parameter `name` in method `openOrCreateDatabase`
+MissingNullability: android.test.RenamingDelegatingContext#openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler) parameter #2:
+    Missing nullability on parameter `factory` in method `openOrCreateDatabase`
+MissingNullability: android.test.RenamingDelegatingContext#openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler) parameter #3:
+    Missing nullability on parameter `errorHandler` in method `openOrCreateDatabase`
+
+
+ProtectedMember: android.test.ActivityInstrumentationTestCase#setUp():
+    Protected methods not allowed; must be public: method android.test.ActivityInstrumentationTestCase.setUp()}
+ProtectedMember: android.test.ActivityInstrumentationTestCase#tearDown():
+    Protected methods not allowed; must be public: method android.test.ActivityInstrumentationTestCase.tearDown()}
+ProtectedMember: android.test.ActivityInstrumentationTestCase2#runTest():
+    Protected methods not allowed; must be public: method android.test.ActivityInstrumentationTestCase2.runTest()}
+ProtectedMember: android.test.ActivityInstrumentationTestCase2#setUp():
+    Protected methods not allowed; must be public: method android.test.ActivityInstrumentationTestCase2.setUp()}
+ProtectedMember: android.test.ActivityInstrumentationTestCase2#tearDown():
+    Protected methods not allowed; must be public: method android.test.ActivityInstrumentationTestCase2.tearDown()}
+ProtectedMember: android.test.ActivityUnitTestCase#setUp():
+    Protected methods not allowed; must be public: method android.test.ActivityUnitTestCase.setUp()}
+ProtectedMember: android.test.ActivityUnitTestCase#tearDown():
+    Protected methods not allowed; must be public: method android.test.ActivityUnitTestCase.tearDown()}
+ProtectedMember: android.test.ApplicationTestCase#setUp():
+    Protected methods not allowed; must be public: method android.test.ApplicationTestCase.setUp()}
+ProtectedMember: android.test.ApplicationTestCase#tearDown():
+    Protected methods not allowed; must be public: method android.test.ApplicationTestCase.tearDown()}
+ProtectedMember: android.test.ProviderTestCase#setUp():
+    Protected methods not allowed; must be public: method android.test.ProviderTestCase.setUp()}
+ProtectedMember: android.test.ProviderTestCase#tearDown():
+    Protected methods not allowed; must be public: method android.test.ProviderTestCase.tearDown()}
+ProtectedMember: android.test.ProviderTestCase2#setUp():
+    Protected methods not allowed; must be public: method android.test.ProviderTestCase2.setUp()}
+ProtectedMember: android.test.ProviderTestCase2#tearDown():
+    Protected methods not allowed; must be public: method android.test.ProviderTestCase2.tearDown()}
+ProtectedMember: android.test.ServiceTestCase#setUp():
+    Protected methods not allowed; must be public: method android.test.ServiceTestCase.setUp()}
+ProtectedMember: android.test.ServiceTestCase#tearDown():
+    Protected methods not allowed; must be public: method android.test.ServiceTestCase.tearDown()}
+ProtectedMember: android.test.SingleLaunchActivityTestCase#setUp():
+    Protected methods not allowed; must be public: method android.test.SingleLaunchActivityTestCase.setUp()}
+ProtectedMember: android.test.SingleLaunchActivityTestCase#tearDown():
+    Protected methods not allowed; must be public: method android.test.SingleLaunchActivityTestCase.tearDown()}
+ProtectedMember: android.test.SyncBaseInstrumentation#setUp():
+    Protected methods not allowed; must be public: method android.test.SyncBaseInstrumentation.setUp()}
diff --git a/tests/AppLaunch/Android.bp b/tests/AppLaunch/Android.bp
index f90f26f..75db551 100644
--- a/tests/AppLaunch/Android.bp
+++ b/tests/AppLaunch/Android.bp
@@ -8,6 +8,8 @@
         "android.test.base",
         "android.test.runner",
     ],
-    static_libs: ["androidx.test.rules"],
+    static_libs: [
+        "androidx.test.rules",
+        "ub-uiautomator"],
     test_suites: ["device-tests"],
 }
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 95b8f67..0d05044 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,6 +15,8 @@
  */
 package com.android.tests.applaunch;
 
+import static org.junit.Assert.assertNotNull;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.ActivityManager;
@@ -29,7 +31,9 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
+import android.support.test.uiautomator.UiDevice;
 import android.test.InstrumentationTestCase;
 import android.test.InstrumentationTestRunner;
 import android.util.Log;
@@ -41,11 +45,17 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
+import java.nio.file.Paths;
+import java.time.format.DateTimeFormatter;
+import java.time.ZonedDateTime;
+import java.time.ZoneOffset;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -62,6 +72,7 @@
  * in the following format:
  * -e apps <app name>^<result key>|<app name>^<result key>
  */
+@Deprecated
 public class AppLaunch extends InstrumentationTestCase {
 
     private static final int JOIN_TIMEOUT = 10000;
@@ -71,6 +82,8 @@
     // with the app launch
     private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
     private static final String KEY_APPS = "apps";
+    private static final String KEY_IORAP_TRIAL_LAUNCH = "iorap_trial_launch";
+    private static final String KEY_IORAP_COMPILER_FILTERS = "iorap_compiler_filters";
     private static final String KEY_TRIAL_LAUNCH = "trial_launch";
     private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
     private static final String KEY_LAUNCH_ORDER = "launch_order";
@@ -87,6 +100,9 @@
     private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
     private static final String KEY_COMPILER_FILTERS = "compiler_filters";
     private static final String KEY_FORCE_STOP_APP = "force_stop_app";
+    private static final String ENABLE_SCREEN_RECORDING = "enable_screen_recording";
+    private static final int MAX_RECORDING_PARTS = 5;
+    private static final long VIDEO_TAIL_BUFFER = 500;
 
     private static final String SIMPLEPERF_APP_CMD =
             "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
@@ -98,6 +114,11 @@
     private static final int BEFORE_KILL_APP_SLEEP_TIMEOUT = 1000; // 1s before killing
     private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 3000; // 3s between launching apps
     private static final int PROFILE_SAVE_SLEEP_TIMEOUT = 1000; // Allow 1s for the profile to save
+    private static final int IORAP_TRACE_DURATION_TIMEOUT = 7000; // Allow 7s for trace to complete.
+    private static final int IORAP_TRIAL_LAUNCH_ITERATIONS = 3;  // min 3 launches to merge traces.
+    private static final int IORAP_COMPILE_CMD_TIMEOUT = 60;  // in seconds: 1 minutes
+    private static final int IORAP_COMPILE_MIN_TRACES = 1;  // configure iorapd to need 1 trace.
+    private static final int IORAP_COMPILE_RETRIES = 3;  // retry compiler 3 times if it fails.
     private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
     private static final String LAUNCH_FILE = "applaunch.txt";
     private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
@@ -106,6 +127,9 @@
     private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
     private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
     private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
+    private static final String IORAP_TRIAL_LAUNCH = "IORAP_TRIAL_LAUNCH";
+    private static final String IORAP_TRIAL_LAUNCH_FIRST = "IORAP_TRIAL_LAUNCH_FIRST";
+    private static final String IORAP_TRIAL_LAUNCH_LAST = "IORAP_TRIAL_LAUNCH_LAST";
     private static final String DELIMITER = ",";
     private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
     private static final String APP_LAUNCH_CMD = "am start -W -n";
@@ -119,32 +143,49 @@
     private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
     private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
     private static final String COMPILE_CMD = "cmd package compile -f -m %s %s";
+    private static final String IORAP_COMPILE_CMD = "dumpsys iorapd --compile-package %s";
+    private static final String IORAP_MAINTENANCE_CMD =
+            "dumpsys iorapd --purge-package %s";
+    private static final String IORAP_DUMPSYS_CMD = "dumpsys iorapd";
     private static final String SPEED_PROFILE_FILTER = "speed-profile";
     private static final String VERIFY_FILTER = "verify";
     private static final String LAUNCH_SCRIPT_NAME = "appLaunch";
 
     private Map<String, Intent> mNameToIntent;
     private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
+    private RecordingThread mCurrentThread;
     private Map<String, String> mNameToResultKey;
     private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
     private IActivityManager mAm;
+    private File launchSubDir = null;
     private String mSimplePerfCmd = null;
     private String mLaunchOrder = null;
     private boolean mDropCache = false;
     private int mLaunchIterations = 10;
     private boolean mForceStopApp = true;
+    private boolean mEnableRecording = false;
     private int mTraceLaunchCount = 0;
     private String mTraceDirectoryStr = null;
     private Bundle mResult = new Bundle();
     private Set<String> mRequiredAccounts;
     private boolean mTrialLaunch = false;
+    private boolean mIorapTrialLaunch = false;
     private BufferedWriter mBufferedWriter = null;
     private boolean mSimplePerfAppOnly = false;
     private String[] mCompilerFilters = null;
+    private List<String> mIorapCompilerFilters = null;
     private String mLastAppName = "";
     private boolean mCycleCleanUp = false;
     private boolean mTraceAll = false;
     private boolean mIterationCycle = false;
+    private UiDevice mDevice;
+
+    enum IorapStatus {
+        UNDEFINED,
+        ENABLED,
+        DISABLED
+    }
+    private IorapStatus mIorapStatus = IorapStatus.UNDEFINED;
     private long mCycleTime = 0;
     private StringBuilder mCycleTimes = new StringBuilder();
 
@@ -194,7 +235,7 @@
         }
 
         try {
-            File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+            launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
 
             if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
                 throw new IOException("Unable to create the lauch file sub directory "
@@ -243,7 +284,10 @@
             setLaunchOrder();
 
             for (LaunchOrder launch : mLaunchOrderList) {
-                dropCache();
+                toggleIorapStatus(launch.getIorapEnabled());
+                dropCache(/*override*/false);
+
+                Log.v(TAG, "Launch reason: " + launch.getLaunchReason());
 
                 // App launch times for trial launch will not be used for final
                 // launch time calculations.
@@ -289,6 +333,43 @@
                               compileApp(launch.getCompilerFilter(), appPkgName));
                     }
                 }
+                else if (launch.getLaunchReason().startsWith(IORAP_TRIAL_LAUNCH)) {
+                    mIterationCycle = false;
+
+                    // In the "applaunch.txt" file, iorap-trial launches is referenced using
+                    // "IORAP_TRIAL_LAUNCH" or "IORAP_TRIAL_LAUNCH_LAST"
+                    Intent startIntent = mNameToIntent.get(launch.getApp());
+                    if (startIntent == null) {
+                        Log.w(TAG, "App does not exist: " + launch.getApp());
+                        mResult.putString(mNameToResultKey.get(launch.getApp()),
+                            "App does not exist");
+                        continue;
+                    }
+                    String appPkgName = startIntent.getComponent().getPackageName();
+
+                    if (launch.getLaunchReason().equals(IORAP_TRIAL_LAUNCH_FIRST)) {
+                        // delete any iorap-traces associated with this package.
+                        purgeIorapPackage(appPkgName);
+                    }
+                    dropCache(/*override*/true);  // iorap-trial runs must have drop cache.
+
+                    AppLaunchResult launchResult =
+                        startApp(launch.getApp(), launch.getLaunchReason());
+                    if (launchResult.mLaunchTime < 0) {
+                        addLaunchResult(launch, new AppLaunchResult());
+                        // simply pass the app if launch isn't successful
+                        // error should have already been logged by startApp
+                        continue;
+                    }
+                    // wait for slightly more than 5s (iorapd.perfetto.trace_duration_ms) for the trace buffers to complete.
+                    sleep(IORAP_TRACE_DURATION_TIMEOUT);
+
+                    if (launch.getLaunchReason().equals(IORAP_TRIAL_LAUNCH_LAST)) {
+                        // run the iorap compiler and wait for iorap to compile fully.
+                        // this throws an exception if it fails.
+                        compileAppForIorapWithRetries(appPkgName, IORAP_COMPILE_RETRIES);
+                    }
+                }
 
                 // App launch times used for final calculation
                 else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
@@ -438,6 +519,168 @@
     }
 
     /**
+     * Compile the app package using compilerFilter,
+     * retrying if the compilation command fails in between.
+     */
+    private void compileAppForIorapWithRetries(String appPkgName, int retries) throws IOException {
+        for (int i = 0; i < retries; ++i) {
+            if (compileAppForIorap(appPkgName)) {
+                return;
+            }
+            sleep(1000);
+        }
+
+        throw new IllegalStateException("compileAppForIorapWithRetries: timed out after "
+                + retries + " retries");
+    }
+
+    /**
+     * Compile the app package using compilerFilter and return true or false
+     * based on status of the compilation command.
+     */
+    private boolean compileAppForIorap(String appPkgName) throws IOException {
+        String logcatTimestamp = getTimeNowForLogcat();
+
+        getInstrumentation().getUiAutomation().
+                executeShellCommand(String.format(IORAP_COMPILE_CMD, appPkgName));
+
+        int i = 0;
+        for (i = 0; i < IORAP_COMPILE_CMD_TIMEOUT; ++i) {
+            IorapCompilationStatus status = waitForIorapCompiled(appPkgName);
+            if (status == IorapCompilationStatus.COMPLETE) {
+                Log.v(TAG, "compileAppForIorap: success");
+                logDumpsysIorapd(appPkgName);
+                break;
+            } else if (status == IorapCompilationStatus.INSUFFICIENT_TRACES) {
+                Log.e(TAG, "compileAppForIorap: failed due to insufficient traces");
+                logDumpsysIorapd(appPkgName);
+                throw new IllegalStateException(
+                        "compileAppForIorap: failed due to insufficient traces");
+            } // else INCOMPLETE. keep asking iorapd if it's done yet.
+            sleep(1000);
+        }
+
+        if (i == IORAP_COMPILE_CMD_TIMEOUT) {
+            Log.e(TAG, "compileAppForIorap: failed due to timeout");
+            logDumpsysIorapd(appPkgName);
+            return false;
+        }
+
+        return true;
+    }
+
+    /** Save the contents of $(adb shell dumpsys iorapd) to the launch_logs directory. */
+    private void logDumpsysIorapd(String packageName) throws IOException {
+        InstrumentationTestRunner instrumentation =
+                (InstrumentationTestRunner)getInstrumentation();
+        Bundle args = instrumentation.getArguments();
+
+        String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
+
+        // Root directory for applaunch file to log the app launch output
+        // Will be useful in case of simpleperf command is used
+        File launchRootDir = null;
+        if (null != launchDirectory && !launchDirectory.isEmpty()) {
+            launchRootDir = new File(launchDirectory);
+            if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
+                throw new IOException("Unable to create the destination directory "
+                    + launchRootDir + ". Try disabling selinux.");
+            }
+        } else {
+            Log.w(TAG, "logDumpsysIorapd: Missing launch-directory arg");
+            return;
+        }
+
+        File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+
+        if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
+            throw new IOException("Unable to create the lauch file sub directory "
+                + launchSubDir + ". Try disabling selinux.");
+        }
+        String path = "iorapd_dumpsys_" + packageName + "_" + System.nanoTime() + ".txt";
+        File file = new File(launchSubDir, path);
+        try (FileOutputStream outputStream = new FileOutputStream(file);
+                BufferedWriter writer = new BufferedWriter(
+                        new OutputStreamWriter(outputStream));
+                ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+                        executeShellCommand(IORAP_DUMPSYS_CMD);
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                        new FileInputStream(result.getFileDescriptor())))) {
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                writer.write(line + "\n");
+            }
+        }
+
+        Log.v(TAG, "logDumpsysIorapd: Saved to file: " + path);
+    }
+
+    enum IorapCompilationStatus {
+        INCOMPLETE,
+        COMPLETE,
+        INSUFFICIENT_TRACES,
+    }
+    private IorapCompilationStatus waitForIorapCompiled(String appPkgName) throws IOException {
+        try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+                executeShellCommand(IORAP_DUMPSYS_CMD);
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                        new FileInputStream(result.getFileDescriptor())))) {
+            String line;
+            String prevLine = "";
+            while ((line = bufferedReader.readLine()) != null) {
+                // Match the indented VersionedComponentName string.
+                // "  com.google.android.deskclock/com.android.deskclock.DeskClock@62000712"
+                // Note: spaces are meaningful here.
+                if (prevLine.contains("  " + appPkgName) && prevLine.contains("@")) {
+                    // pre-requisite:
+                    // Compiled Status: Raw traces pending compilation (3)
+                    if (line.contains("Compiled Status: Usable compiled trace")) {
+                        return IorapCompilationStatus.COMPLETE;
+                    } else if (line.contains("Compiled Status: ") &&
+                            line.contains("more traces for compilation")) {
+                        //      Compiled Status: Need 1 more traces for compilation
+                        // No amount of waiting will help here because there were
+                        // insufficient traces made.
+                        return IorapCompilationStatus.INSUFFICIENT_TRACES;
+                    }
+                }
+
+                prevLine = line;
+            }
+            return IorapCompilationStatus.INCOMPLETE;
+        }
+    }
+
+    private String makeReasonForIorapTrialLaunch(int launchCount) {
+        String reason = IORAP_TRIAL_LAUNCH;
+        if (launchCount == 0) {
+            reason = IORAP_TRIAL_LAUNCH_FIRST;
+        }
+        if (launchCount == IORAP_TRIAL_LAUNCH_ITERATIONS - 1) {
+            reason = IORAP_TRIAL_LAUNCH_LAST;
+        }
+        return reason;
+    }
+
+    private boolean shouldIncludeIorap(String compilerFilter) {
+        if (!mIorapTrialLaunch) {
+            return false;
+        }
+
+        // No iorap compiler filters specified: treat all compiler filters as ok.
+        if (mIorapCompilerFilters == null) {
+            return true;
+        }
+
+        // iorap compiler filters specified: the compilerFilter must be in the whitelist.
+        if (mIorapCompilerFilters.indexOf(compilerFilter) != -1) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
      * If launch order is "cyclic" then apps will be launched one after the
      * other for each iteration count.
      * If launch order is "sequential" then each app will be launched for given number
@@ -448,20 +691,33 @@
             for (String compilerFilter : mCompilerFilters) {
                 if (mTrialLaunch) {
                     for (String app : mNameToResultKey.keySet()) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH, /*iorapEnabled*/false));
+                    }
+                }
+                if (shouldIncludeIorap(compilerFilter)) {
+                    for (int launchCount = 0; launchCount < IORAP_TRIAL_LAUNCH_ITERATIONS; ++launchCount) {
+                        for (String app : mNameToResultKey.keySet()) {
+                            String reason = makeReasonForIorapTrialLaunch(launchCount);
+                            mLaunchOrderList.add(
+                                    new LaunchOrder(app, compilerFilter,
+                                            reason,
+                                            /*iorapEnabled*/true));
+                        }
                     }
                 }
                 for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
                     for (String app : mNameToResultKey.keySet()) {
                         mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                  String.format(LAUNCH_ITERATION, launchCount)));
+                                  String.format(LAUNCH_ITERATION, launchCount),
+                                        shouldIncludeIorap(compilerFilter)));
                     }
                 }
                 if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
                     for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
                         for (String app : mNameToResultKey.keySet()) {
                             mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                      String.format(TRACE_ITERATION, traceCount)));
+                                      String.format(TRACE_ITERATION, traceCount),
+                                            shouldIncludeIorap(compilerFilter)));
                         }
                     }
                 }
@@ -470,16 +726,27 @@
             for (String compilerFilter : mCompilerFilters) {
                 for (String app : mNameToResultKey.keySet()) {
                     if (mTrialLaunch) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
+                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH, /*iorapEnabled*/false));
+                    }
+                    if (shouldIncludeIorap(compilerFilter)) {
+                        for (int launchCount = 0; launchCount < IORAP_TRIAL_LAUNCH_ITERATIONS; ++launchCount) {
+                            String reason = makeReasonForIorapTrialLaunch(launchCount);
+                            mLaunchOrderList.add(
+                                    new LaunchOrder(app, compilerFilter,
+                                            reason,
+                                            /*iorapEnabled*/true));
+                        }
                     }
                     for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
                         mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                String.format(LAUNCH_ITERATION, launchCount)));
+                                String.format(LAUNCH_ITERATION, launchCount),
+                                        shouldIncludeIorap(compilerFilter)));
                     }
                     if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
                         for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
                             mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                    String.format(TRACE_ITERATION, traceCount)));
+                                    String.format(TRACE_ITERATION, traceCount),
+                                            shouldIncludeIorap(compilerFilter)));
                         }
                     }
                 }
@@ -489,14 +756,176 @@
         }
     }
 
-    private void dropCache() {
-        if (mDropCache) {
+    private void dropCache(boolean override) {
+        if (mDropCache || override) {
             assertNotNull("Issue in dropping the cache",
                     getInstrumentation().getUiAutomation()
                             .executeShellCommand(DROP_CACHE_SCRIPT));
         }
     }
 
+    // [[ $(adb shell whoami) == "root" ]]
+    private boolean checkIfRoot() throws IOException {
+        String total = "";
+        try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+                executeShellCommand("whoami");
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                        new FileInputStream(result.getFileDescriptor())))) {
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                total = total + line;
+            }
+        }
+        return total.contains("root");
+    }
+
+    private void stopIorapd() {
+        getInstrumentation().getUiAutomation()
+                .executeShellCommand("stop iorapd");
+        sleep(100);  // give it extra time to fully stop.
+    }
+
+    private void startIorapd() {
+        String logcatTimeNow = getTimeNowForLogcat();
+        Log.v(TAG, "startIorapd, logcat time: " + logcatTimeNow);
+
+        getInstrumentation().getUiAutomation()
+                .executeShellCommand("start iorapd");
+
+        int maxAttempts = 100;
+        int attempt = 0;
+        do {
+            // Ensure that IorapForwardingService fully reconnects to iorapd before proceeding.
+            String needle = "Connected to iorapd native service";
+            String logcatLines = getLogcatSinceTime(logcatTimeNow);
+
+            if (logcatLines.contains(needle)) {
+                break;
+            }
+
+            sleep(1000);
+            attempt++;
+        } while (attempt < maxAttempts);
+
+        if (attempt == maxAttempts) {
+            Log.e(TAG, "Timed out after waiting for iorapd to start");
+        }
+        // Wait a little bit longer for iorapd to settle.
+        sleep(1000);
+    }
+
+    // Delete all db rows and files associated with a package in iorapd.
+    // Effectively deletes any raw or compiled trace files, unoptimizing the package in iorap.
+    private void purgeIorapPackage(String packageName) {
+        try {
+            if (!checkIfRoot()) {
+                throw new AssertionError("must be root to toggle iorapd; try adb root?");
+            }
+        } catch (IOException e) {
+            throw new AssertionError(e);
+        }
+
+        Log.v(TAG, "Purge iorap package: " + packageName);
+        getInstrumentation().getUiAutomation()
+                .executeShellCommand(String.format(IORAP_MAINTENANCE_CMD, packageName));
+        Log.v(TAG, "Executed: " + String.format(IORAP_MAINTENANCE_CMD, packageName));
+    }
+
+    String executeShellCommandWithTempFile(String cmd) {
+        Log.v(TAG, "executeShellCommandWithTempFile, cmd: " + cmd);
+        try {
+            //File outputDir =
+            //       InstrumentationRegistry.getInstrumentation().getContext().getCacheDir();
+            File outputFile = File.createTempFile("exec_shell_command", ".sh");
+
+            try {
+                outputFile.setWritable(true);
+                outputFile.setExecutable(true, /*ownersOnly*/false);
+
+                String scriptPath = outputFile.toString();
+
+                // If this works correctly, the next log-line will print 'Success'.
+                try (BufferedWriter writer = new BufferedWriter(new FileWriter(scriptPath))) {
+                    writer.write(cmd);
+                }
+
+                String resultString = "";
+                try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+                        executeShellCommand(scriptPath);
+                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                                new FileInputStream(result.getFileDescriptor())))) {
+                    String line;
+                    while ((line = bufferedReader.readLine()) != null) {
+                        resultString += line + "\n";
+                    }
+                }
+
+                return resultString;
+            } finally {
+                outputFile.delete();
+            }
+        } catch (IOException e) {
+            throw new AssertionError("Failed to execute shell command: " + cmd, e);
+        }
+    }
+
+    // Get the 'now' timestamp usable with $(adb logcat -v utc -T "time string")
+    String getTimeNowForLogcat() {
+        ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC);
+
+        // YYYY-MM-DD hh:mm:ss.mmm
+        return utc.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
+    }
+
+    String getLogcatSinceTime(String logcatTime) {
+        // The time has spaces in it but must be passed as a single arg.
+        // Therefore use a temp script file.
+        return executeShellCommandWithTempFile(
+                String.format("logcat -d -v threadtime -v utc -T '%s'", logcatTime));
+    }
+
+    /**
+     * Toggle iorapd-based readahead and trace-collection.
+     * If iorapd is already enabled and enable is true, does nothing.
+     * If iorapd is already disabled and enable is false, does nothing.
+     */
+    private void toggleIorapStatus(boolean enable) {
+        boolean currentlyEnabled = false;
+        Log.v(TAG, "toggleIorapStatus " + Boolean.toString(enable));
+
+        // Do nothing if we are already enabled or disabled.
+        if (mIorapStatus == IorapStatus.ENABLED && enable) {
+            return;
+        } else if (mIorapStatus == IorapStatus.DISABLED && !enable) {
+            return;
+        }
+
+        try {
+            if (!checkIfRoot()) {
+                throw new AssertionError("must be root to toggle iorapd; try adb root?");
+            }
+        } catch (IOException e) {
+            throw new AssertionError(e);
+        }
+
+        getInstrumentation().getUiAutomation()
+                .executeShellCommand(String.format("setprop iorapd.perfetto.enable %b", enable));
+        getInstrumentation().getUiAutomation()
+                .executeShellCommand(String.format("setprop iorapd.readahead.enable %b", enable));
+        getInstrumentation().getUiAutomation()
+                .executeShellCommand(String.format(
+                        "setprop iorapd.maintenance.min_traces %d", IORAP_COMPILE_MIN_TRACES));
+        // this last command blocks until iorapd refreshes its system properties
+        getInstrumentation().getUiAutomation()
+                .executeShellCommand(String.format("dumpsys iorapd --refresh-properties"));
+
+        if (enable) {
+            mIorapStatus = IorapStatus.ENABLED;
+        } else {
+            mIorapStatus = IorapStatus.DISABLED;
+        }
+    }
+
     private void parseArgs(Bundle args) {
         mNameToResultKey = new LinkedHashMap<String, String>();
         mNameToLaunchTime = new HashMap<>();
@@ -505,9 +934,16 @@
             mLaunchIterations = Integer.parseInt(launchIterations);
         }
         String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
+
         if (forceStopApp != null) {
             mForceStopApp = Boolean.parseBoolean(forceStopApp);
         }
+
+        String enableRecording = args.getString(ENABLE_SCREEN_RECORDING);
+
+        if (enableRecording != null) {
+            mEnableRecording = Boolean.parseBoolean(enableRecording);
+        }
         String appList = args.getString(KEY_APPS);
         if (appList == null)
             return;
@@ -543,6 +979,13 @@
             mCompilerFilters = new String[1];
         }
 
+        String iorapCompilerFilterList = args.getString(KEY_IORAP_COMPILER_FILTERS);
+        if (iorapCompilerFilterList != null) {
+            // Passing in iorap compiler filters implies an iorap trial launch.
+            mIorapTrialLaunch = true;
+            mIorapCompilerFilters = Arrays.asList(iorapCompilerFilterList.split("\\|"));
+        }
+
         // Pre-populate the results map to avoid null checks.
         for (String app : mNameToLaunchTime.keySet()) {
             HashMap<String, List<AppLaunchResult>> map = new HashMap<>();
@@ -560,6 +1003,8 @@
         mCycleCleanUp = Boolean.parseBoolean(args.getString(KEY_CYCLE_CLEAN));
         mTraceAll = Boolean.parseBoolean(args.getString(KEY_TRACE_ALL));
         mTrialLaunch = mTrialLaunch || Boolean.parseBoolean(args.getString(KEY_TRIAL_LAUNCH));
+        mIorapTrialLaunch = mIorapTrialLaunch ||
+                Boolean.parseBoolean(args.getString(KEY_IORAP_TRIAL_LAUNCH));
 
         if (mSimplePerfCmd != null && mSimplePerfAppOnly) {
             Log.w(TAG, String.format("Passing both %s and %s is not supported, ignoring %s",
@@ -611,6 +1056,9 @@
     private AppLaunchResult startApp(String appName, String launchReason)
             throws NameNotFoundException, RemoteException {
         Log.i(TAG, "Starting " + appName);
+        if(mEnableRecording) {
+            startRecording(appName, launchReason);
+        }
 
         Intent startIntent = mNameToIntent.get(appName);
         if (startIntent == null) {
@@ -626,6 +1074,10 @@
         } catch (InterruptedException e) {
             // ignore
         }
+
+        if(mEnableRecording) {
+            stopRecording();
+        }
         return runnable.getResult();
     }
 
@@ -738,11 +1190,13 @@
         private String mApp;
         private String mCompilerFilter;
         private String mLaunchReason;
+        private boolean mIorapEnabled;
 
-        LaunchOrder(String app, String compilerFilter, String launchReason){
+        LaunchOrder(String app, String compilerFilter, String launchReason, boolean iorapEnabled) {
             mApp = app;
             mCompilerFilter = compilerFilter;
             mLaunchReason = launchReason;
+            mIorapEnabled = iorapEnabled;
         }
 
         public String getApp() {
@@ -764,6 +1218,14 @@
         public void setLaunchReason(String launchReason) {
             mLaunchReason = launchReason;
         }
+
+        public void setIorapEnabled(boolean iorapEnabled) {
+            mIorapEnabled = iorapEnabled;
+        }
+
+        public boolean getIorapEnabled() {
+            return mIorapEnabled;
+        }
     }
 
     private class AppLaunchResult {
@@ -923,4 +1385,126 @@
         }
 
     }
+
+    /**
+     * Start the screen recording while launching the app.
+     *
+     * @param appName
+     * @param launchReason
+     */
+    private void startRecording(String appName, String launchReason) {
+        Log.v(TAG, "Started Recording");
+        mCurrentThread = new RecordingThread("test-screen-record",
+                String.format("%s_%s", appName, launchReason));
+        mCurrentThread.start();
+    }
+
+    /**
+     * Stop already started screen recording.
+     */
+    private void stopRecording() {
+        // Skip if not directory.
+        if (launchSubDir == null) {
+            return;
+        }
+
+        // Add some extra time to the video end.
+        SystemClock.sleep(VIDEO_TAIL_BUFFER);
+        // Ctrl + C all screen record processes.
+        mCurrentThread.cancel();
+        // Wait for the thread to completely die.
+        try {
+            mCurrentThread.join();
+        } catch (InterruptedException ex) {
+            Log.e(TAG, "Interrupted when joining the recording thread.", ex);
+        }
+        Log.v(TAG, "Stopped Recording");
+    }
+
+    /** Returns the recording's name for part {@code part} of launch description. */
+    private File getOutputFile(String description, int part) {
+        // Omit the iteration number for the first iteration.
+        final String fileName =
+                String.format(
+                        "%s-video%s.mp4", description, part == 1 ? "" : part);
+        return Paths.get(launchSubDir.getAbsolutePath(), description).toFile();
+    }
+
+
+    /**
+     * Encapsulates the start and stop screen recording logic.
+     * Copied from ScreenRecordCollector.
+     */
+    private class RecordingThread extends Thread {
+        private final String mDescription;
+        private final List<File> mRecordings;
+
+        private boolean mContinue;
+
+        public RecordingThread(String name, String description) {
+            super(name);
+
+            mContinue = true;
+            mRecordings = new ArrayList<>();
+
+            assertNotNull("No test description provided for recording.", description);
+            mDescription = description;
+        }
+
+        @Override
+        public void run() {
+            try {
+                // Start at i = 1 to encode parts as X.mp4, X2.mp4, X3.mp4, etc.
+                for (int i = 1; i <= MAX_RECORDING_PARTS && mContinue; i++) {
+                    File output = getOutputFile(mDescription, i);
+                    Log.d(
+                            TAG,
+                            String.format("Recording screen to %s", output.getAbsolutePath()));
+                    mRecordings.add(output);
+                    // Make sure not to block on this background command in the main thread so
+                    // that the test continues to run, but block in this thread so it does not
+                    // trigger a new screen recording session before the prior one completes.
+                    getDevice().executeShellCommand(
+                                    String.format("screenrecord %s", output.getAbsolutePath()));
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Caught exception while screen recording.");
+            }
+        }
+
+        public void cancel() {
+            mContinue = false;
+
+            // Identify the screenrecord PIDs and send SIGINT 2 (Ctrl + C) to each.
+            try {
+                String[] pids = getDevice().executeShellCommand(
+                        "pidof screenrecord").split(" ");
+                for (String pid : pids) {
+                    // Avoid empty process ids, because of weird splitting behavior.
+                    if (pid.isEmpty()) {
+                        continue;
+                    }
+
+                    getDevice().executeShellCommand(
+                            String.format("kill -2 %s", pid));
+                    Log.d(
+                            TAG,
+                            String.format("Sent SIGINT 2 to screenrecord process (%s)", pid));
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Failed to kill screen recording process.");
+            }
+        }
+
+        public List<File> getRecordings() {
+            return mRecordings;
+        }
+    }
+
+    public UiDevice getDevice() {
+        if (mDevice == null) {
+            mDevice = UiDevice.getInstance(getInstrumentation());
+        }
+        return mDevice;
+    }
 }
diff --git a/tests/GamePerformance/Android.bp b/tests/GamePerformance/Android.bp
new file mode 100644
index 0000000..648fd81
--- /dev/null
+++ b/tests/GamePerformance/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+android_test_helper_app {
+    name: "GamePerformance",
+    // Don't include this package in any target
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    srcs: ["src/**/*.java"],
+    static_libs: ["android-support-test"],
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/tests/GamePerformance/Android.mk b/tests/GamePerformance/Android.mk
deleted file mode 100644
index 58654de..0000000
--- a/tests/GamePerformance/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-
-LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
-
-LOCAL_PACKAGE_NAME := GamePerformance
-
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-
-include $(BUILD_PACKAGE)
diff --git a/tests/NullHomeTest/Android.bp b/tests/NullHomeTest/Android.bp
new file mode 100644
index 0000000..99248bf
--- /dev/null
+++ b/tests/NullHomeTest/Android.bp
@@ -0,0 +1,22 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "NullHomeTest",
+    srcs: ["src/**/*.java"],
+    certificate: "platform",
+    platform_apis: true,
+    static_libs: ["android-support-test"],
+    test_suites: ["device-tests"],
+}
diff --git a/tests/NullHomeTest/AndroidManifest.xml b/tests/NullHomeTest/AndroidManifest.xml
new file mode 100644
index 0000000..dc6402e
--- /dev/null
+++ b/tests/NullHomeTest/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.nullhome"
+    android:sharedUserId="android.uid.system" >
+
+    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.test.nullhome"
+        android:label="Check if no null Home exists/is enabled" />
+
+    <application android:label="Null Home Test">
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/tests/NullHomeTest/src/com/android/test/nullhome/NullHomeTest.java b/tests/NullHomeTest/src/com/android/test/nullhome/NullHomeTest.java
new file mode 100644
index 0000000..1d77cdc5
--- /dev/null
+++ b/tests/NullHomeTest/src/com/android/test/nullhome/NullHomeTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.test.nullhome;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ * Check if NullHome/SystemUserHome activity does not exist/is disabled.
+ *
+ * SystemUserHome is only enabled in bootable CSI (csi_x86, csi_arm64)
+ * products and should not be enabled in other products.
+ *
+ * Shell's NullHome is empty and caused issues in sevaral manual GUI tests
+ * that try to select/use it, and should be removed.
+ *
+ * Settings' FallbackHome is fine because it's specially handled by Settings.
+ *
+ */
+
+@RunWith(JUnit4.class)
+public class NullHomeTest {
+    private static final String TAG = "NullHomeTest";
+    private Context mContext;
+    private PackageManager mPm;
+
+    @Before
+    public void before() {
+        Log.d(TAG, "beforeClass()");
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        mPm = mContext.getPackageManager();
+    }
+
+    @Test
+    public void checkNullHome() {
+        final List<ResolveInfo> homeActivities = new ArrayList<>();
+
+        mPm.getHomeActivities(homeActivities);
+        for (ResolveInfo activity : homeActivities) {
+            Log.d(TAG, "Home activity: " + activity.activityInfo.packageName);
+            Assert.assertNotEquals(activity.activityInfo.packageName,
+                    "com.android.internal.app.SystemUserHomeActivity");
+            Assert.assertNotEquals(activity.activityInfo.packageName,
+                    "com.android.shell");
+        }
+    }
+}
diff --git a/tests/ProtoInputStreamTests/Android.bp b/tests/ProtoInputStreamTests/Android.bp
new file mode 100644
index 0000000..ecc40566
--- /dev/null
+++ b/tests/ProtoInputStreamTests/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "ProtoInputStreamTests",
+    proto: {
+        type: "nano",
+    },
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.proto",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+    test_suites: ["device-tests"],
+    libs: ["android.test.runner"],
+    static_libs: [
+        "androidx.test.rules",
+        "frameworks-base-testutils",
+        "mockito-target-minus-junit4",
+    ],
+}
diff --git a/tests/ProtoInputStreamTests/Android.mk b/tests/ProtoInputStreamTests/Android.mk
deleted file mode 100644
index eb747cc..0000000
--- a/tests/ProtoInputStreamTests/Android.mk
+++ /dev/null
@@ -1,34 +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.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := ProtoInputStreamTests
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_MODULE_TAGS := tests optional
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-proto-files-under, src)
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.rules \
-    frameworks-base-testutils \
-    mockito-target-minus-junit4
-
-include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index a590fc4..47d0173 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -128,7 +128,7 @@
         runPhase("resetNetworkStack");
         // Reduce health check deadline
         getDevice().executeShellCommand("device_config put rollback "
-                + "watchdog_request_timeout_millis 300000");
+                + "watchdog_request_timeout_millis 120000");
         // Simulate re-installation of new NetworkStack with rollbacks enabled
         getDevice().executeShellCommand("pm install -r --staged --enable-rollback "
                 + getNetworkStackPath());
@@ -146,18 +146,10 @@
         Thread.sleep(5000);
         // Verify rollback was not executed before health check deadline
         runPhase("assertNoNetworkStackRollbackCommitted");
-        try {
-            // This is expected to fail due to the device being rebooted out
-            // from underneath the test. If this fails for reasons other than
-            // the device reboot, those failures should result in failure of
-            // the assertNetworkStackExecutedRollback phase.
-            CLog.logAndDisplay(LogLevel.INFO, "Sleep and expect to fail while sleeping");
-            // Sleep for > health check deadline
-            Thread.sleep(260000);
-        } catch (AssertionError e) {
-            // AssertionError is expected.
-        }
 
+        // Wait for reboot to happen
+        assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5)));
+        // Wait for reboot to complete and device to become available
         getDevice().waitForDeviceAvailable();
         // Verify rollback was executed after health check deadline
         runPhase("assertNetworkStackRollbackCommitted");
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index 480b12b..009f817 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -47,6 +47,7 @@
     <uses-permission android:name="android.permission.NETWORK_STACK" />
     <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.NETWORK_FACTORY" />
+    <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index e44d460..46d680f 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -20,6 +20,7 @@
     name: "FrameworksNetCommonTests",
     srcs: ["java/**/*.java", "java/**/*.kt"],
     static_libs: [
+        "androidx.core_core",
         "androidx.test.rules",
         "junit",
         "mockito-target-minus-junit4",
diff --git a/tests/net/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
similarity index 67%
rename from tests/net/java/android/net/CaptivePortalDataTest.kt
rename to tests/net/common/java/android/net/CaptivePortalDataTest.kt
index 0071438..bd1847b 100644
--- a/tests/net/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -16,17 +16,22 @@
 
 package android.net
 
+import android.os.Build
 import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
 import com.android.testutils.assertParcelSane
 import com.android.testutils.assertParcelingIsLossless
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
 import org.junit.Test
 import org.junit.runner.RunWith
 import kotlin.test.assertEquals
 import kotlin.test.assertNotEquals
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.Q)
 class CaptivePortalDataTest {
     private val data = CaptivePortalData.Builder()
             .setRefreshTime(123L)
@@ -63,6 +68,46 @@
         assertNotEqualsAfterChange { it.setCaptive(false) }
     }
 
+    @Test
+    fun testUserPortalUrl() {
+        assertEquals(Uri.parse("https://portal.example.com/test"), data.userPortalUrl)
+    }
+
+    @Test
+    fun testVenueInfoUrl() {
+        assertEquals(Uri.parse("https://venue.example.com/test"), data.venueInfoUrl)
+    }
+
+    @Test
+    fun testIsSessionExtendable() {
+        assertTrue(data.isSessionExtendable)
+    }
+
+    @Test
+    fun testByteLimit() {
+        assertEquals(456L, data.byteLimit)
+        // Test byteLimit unset.
+        assertEquals(-1L, CaptivePortalData.Builder(null).build().byteLimit)
+    }
+
+    @Test
+    fun testRefreshTimeMillis() {
+        assertEquals(123L, data.refreshTimeMillis)
+    }
+
+    @Test
+    fun testExpiryTimeMillis() {
+        assertEquals(789L, data.expiryTimeMillis)
+        // Test expiryTimeMillis unset.
+        assertEquals(-1L, CaptivePortalData.Builder(null).build().expiryTimeMillis)
+    }
+
+    @Test
+    fun testIsCaptive() {
+        assertTrue(data.isCaptive)
+        assertFalse(makeBuilder().setCaptive(false).build().isCaptive)
+    }
+
     private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
             CaptivePortalData.Builder(this).apply { mutator(this) }.build()
 
diff --git a/tests/net/common/java/android/net/DependenciesTest.java b/tests/net/common/java/android/net/DependenciesTest.java
new file mode 100644
index 0000000..ac1c28a
--- /dev/null
+++ b/tests/net/common/java/android/net/DependenciesTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.net;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A simple class that tests dependencies to java standard tools from the
+ * Network stack. These tests are not meant to be comprehensive tests of
+ * the relevant APIs : such tests belong in the relevant test suite for
+ * these dependencies. Instead, this just makes sure coverage is present
+ * by calling the methods in the exact way (or a representative way of how)
+ * they are called in the network stack.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DependenciesTest {
+    // Used to in ipmemorystore's RegularMaintenanceJobService to convert
+    // 24 hours into seconds
+    @Test
+    public void testTimeUnit() {
+        final int hours = 24;
+        final long inSeconds = TimeUnit.HOURS.toMillis(hours);
+        assertEquals(inSeconds, hours * 60 * 60 * 1000);
+    }
+
+    private byte[] makeTrivialArray(final int size) {
+        final byte[] src = new byte[size];
+        for (int i = 0; i < size; ++i) {
+            src[i] = (byte) i;
+        }
+        return src;
+    }
+
+    // Used in ApfFilter to find an IP address from a byte array
+    @Test
+    public void testArrays() {
+        final int size = 128;
+        final byte[] src = makeTrivialArray(size);
+
+        // Test copy
+        final int copySize = 16;
+        final int offset = 24;
+        final byte[] expected = new byte[copySize];
+        for (int i = 0; i < copySize; ++i) {
+            expected[i] = (byte) (offset + i);
+        }
+
+        final byte[] copy = Arrays.copyOfRange(src, offset, offset + copySize);
+        assertArrayEquals(expected, copy);
+        assertArrayEquals(new byte[0], Arrays.copyOfRange(src, size, size));
+    }
+
+    // Used mainly in the Dhcp code
+    @Test
+    public void testCopyOf() {
+        final byte[] src = makeTrivialArray(128);
+        final byte[] copy = Arrays.copyOf(src, src.length);
+        assertArrayEquals(src, copy);
+        assertFalse(src == copy);
+
+        assertArrayEquals(new byte[0], Arrays.copyOf(src, 0));
+
+        final int excess = 16;
+        final byte[] biggerCopy = Arrays.copyOf(src, src.length + excess);
+        for (int i = src.length; i < src.length + excess; ++i) {
+            assertEquals(0, biggerCopy[i]);
+        }
+        for (int i = src.length - 1; i >= 0; --i) {
+            assertEquals(src[i], biggerCopy[i]);
+        }
+    }
+
+    // Used mainly in DnsUtils but also various other places
+    @Test
+    public void testAsList() {
+        final int size = 24;
+        final Object[] src = new Object[size];
+        final ArrayList<Object> expected = new ArrayList<>(size);
+        for (int i = 0; i < size; ++i) {
+            final Object o = new Object();
+            src[i] = o;
+            expected.add(o);
+        }
+        assertEquals(expected, Arrays.asList(src));
+    }
+}
diff --git a/tests/net/common/java/android/net/DhcpInfoTest.java b/tests/net/common/java/android/net/DhcpInfoTest.java
new file mode 100644
index 0000000..bd5533f
--- /dev/null
+++ b/tests/net/common/java/android/net/DhcpInfoTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTL;
+
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.Nullable;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+
+@RunWith(AndroidJUnit4.class)
+public class DhcpInfoTest {
+    private static final String STR_ADDR1 = "255.255.255.255";
+    private static final String STR_ADDR2 = "127.0.0.1";
+    private static final String STR_ADDR3 = "192.168.1.1";
+    private static final String STR_ADDR4 = "192.168.1.0";
+    private static final int LEASE_TIME = 9999;
+
+    private int ipToInteger(String ipString) throws Exception {
+        return inet4AddressToIntHTL((Inet4Address) InetAddress.getByName(ipString));
+    }
+
+    private DhcpInfo createDhcpInfoObject() throws Exception {
+        final DhcpInfo dhcpInfo = new DhcpInfo();
+        dhcpInfo.ipAddress = ipToInteger(STR_ADDR1);
+        dhcpInfo.gateway = ipToInteger(STR_ADDR2);
+        dhcpInfo.netmask = ipToInteger(STR_ADDR3);
+        dhcpInfo.dns1 = ipToInteger(STR_ADDR4);
+        dhcpInfo.dns2 = ipToInteger(STR_ADDR4);
+        dhcpInfo.serverAddress = ipToInteger(STR_ADDR2);
+        dhcpInfo.leaseDuration = LEASE_TIME;
+        return dhcpInfo;
+    }
+
+    @Test
+    public void testConstructor() {
+        new DhcpInfo();
+    }
+
+    @Test
+    public void testToString() throws Exception {
+        final String expectedDefault = "ipaddr 0.0.0.0 gateway 0.0.0.0 netmask 0.0.0.0 "
+                + "dns1 0.0.0.0 dns2 0.0.0.0 DHCP server 0.0.0.0 lease 0 seconds";
+
+        DhcpInfo dhcpInfo = new DhcpInfo();
+
+        // Test default string.
+        assertEquals(expectedDefault, dhcpInfo.toString());
+
+        dhcpInfo = createDhcpInfoObject();
+
+        final String expected = "ipaddr " + STR_ADDR1 + " gateway " + STR_ADDR2 + " netmask "
+                + STR_ADDR3 + " dns1 " + STR_ADDR4 + " dns2 " + STR_ADDR4 + " DHCP server "
+                + STR_ADDR2 + " lease " + LEASE_TIME + " seconds";
+        // Test with new values
+        assertEquals(expected, dhcpInfo.toString());
+    }
+
+    private boolean dhcpInfoEquals(@Nullable DhcpInfo left, @Nullable DhcpInfo right) {
+        if (left == null && right == null) return true;
+
+        if (left == null || right == null) return false;
+
+        return left.ipAddress == right.ipAddress
+                && left.gateway == right.gateway
+                && left.netmask == right.netmask
+                && left.dns1 == right.dns1
+                && left.dns2 == right.dns2
+                && left.serverAddress == right.serverAddress
+                && left.leaseDuration == right.leaseDuration;
+    }
+
+    @Test
+    public void testParcelDhcpInfo() throws Exception {
+        // Cannot use assertParcelSane() here because this requires .equals() to work as
+        // defined, but DhcpInfo has a different legacy behavior that we cannot change.
+        final DhcpInfo dhcpInfo = createDhcpInfoObject();
+        assertFieldCountEquals(7, DhcpInfo.class);
+
+        final DhcpInfo dhcpInfoRoundTrip = parcelingRoundTrip(dhcpInfo);
+        assertTrue(dhcpInfoEquals(null, null));
+        assertFalse(dhcpInfoEquals(null, dhcpInfoRoundTrip));
+        assertFalse(dhcpInfoEquals(dhcpInfo, null));
+        assertTrue(dhcpInfoEquals(dhcpInfo, dhcpInfoRoundTrip));
+    }
+}
diff --git a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt b/tests/net/common/java/android/net/KeepalivePacketDataTest.kt
new file mode 100644
index 0000000..f464ec6
--- /dev/null
+++ b/tests/net/common/java/android/net/KeepalivePacketDataTest.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.net
+
+import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS
+import android.net.InvalidPacketException.ERROR_INVALID_PORT
+import android.os.Build
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import java.net.InetAddress
+import java.util.Arrays
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class KeepalivePacketDataTest {
+    @Rule @JvmField
+    val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
+
+    private val INVALID_PORT = 65537
+    private val TEST_DST_PORT = 4244
+    private val TEST_SRC_PORT = 4243
+
+    private val TESTBYTES = byteArrayOf(12, 31, 22, 44)
+    private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
+    private val TEST_DST_ADDRV4 = "198.168.0.1".address()
+    private val TEST_ADDRV6 = "2001:db8::1".address()
+
+    private fun String.address() = InetAddresses.parseNumericAddress(this)
+
+    // Add for test because constructor of KeepalivePacketData is protected.
+    private inner class TestKeepalivePacketData(
+        srcAddress: InetAddress? = TEST_SRC_ADDRV4,
+        srcPort: Int = TEST_SRC_PORT,
+        dstAddress: InetAddress? = TEST_DST_ADDRV4,
+        dstPort: Int = TEST_DST_PORT,
+        data: ByteArray = TESTBYTES
+    ) : KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, data)
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testConstructor() {
+        var data: TestKeepalivePacketData
+
+        try {
+            data = TestKeepalivePacketData(srcAddress = null)
+            fail("Null src address should cause exception")
+        } catch (e: InvalidPacketException) {
+            assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+        }
+
+        try {
+            data = TestKeepalivePacketData(dstAddress = null)
+            fail("Null dst address should cause exception")
+        } catch (e: InvalidPacketException) {
+            assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+        }
+
+        try {
+            data = TestKeepalivePacketData(dstAddress = TEST_ADDRV6)
+            fail("Ip family mismatched should cause exception")
+        } catch (e: InvalidPacketException) {
+            assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+        }
+
+        try {
+            data = TestKeepalivePacketData(srcPort = INVALID_PORT)
+            fail("Invalid srcPort should cause exception")
+        } catch (e: InvalidPacketException) {
+            assertEquals(e.error, ERROR_INVALID_PORT)
+        }
+
+        try {
+            data = TestKeepalivePacketData(dstPort = INVALID_PORT)
+            fail("Invalid dstPort should cause exception")
+        } catch (e: InvalidPacketException) {
+            assertEquals(e.error, ERROR_INVALID_PORT)
+        }
+    }
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testSrcAddress() = assertEquals(TEST_SRC_ADDRV4, TestKeepalivePacketData().srcAddress)
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testDstAddress() = assertEquals(TEST_DST_ADDRV4, TestKeepalivePacketData().dstAddress)
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testSrcPort() = assertEquals(TEST_SRC_PORT, TestKeepalivePacketData().srcPort)
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testDstPort() = assertEquals(TEST_DST_PORT, TestKeepalivePacketData().dstPort)
+
+    @Test
+    @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testPacket() = assertTrue(Arrays.equals(TESTBYTES, TestKeepalivePacketData().packet))
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 6ec2cd6..0fc9be3 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import static android.net.RouteInfo.RTN_UNREACHABLE;
+
 import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
 import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless;
 import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip;
@@ -27,18 +29,26 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.net.LinkProperties.CompareResult;
 import android.net.LinkProperties.ProvisioningChange;
+import android.net.util.LinkPropertiesUtils.CompareResult;
+import android.os.Build;
 import android.system.OsConstants;
 import android.util.ArraySet;
 
+import androidx.core.os.BuildCompat;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -50,6 +60,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class LinkPropertiesTest {
+    @Rule
+    public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
     private static final InetAddress ADDRV4 = address("75.208.6.1");
     private static final InetAddress ADDRV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
     private static final InetAddress DNS1 = address("75.208.7.1");
@@ -76,13 +89,23 @@
     private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
     private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
     private static final Uri CAPPORT_API_URL = Uri.parse("https://test.example.com/capportapi");
-    private static final CaptivePortalData CAPPORT_DATA = new CaptivePortalData.Builder()
-            .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build();
+
+    // CaptivePortalData cannot be in a constant as it does not exist on Q.
+    // The test runner also crashes when scanning for tests if it is a return type.
+    private static Object getCaptivePortalData() {
+        return new CaptivePortalData.Builder()
+                .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build();
+    }
 
     private static InetAddress address(String addrString) {
         return InetAddresses.parseNumericAddress(addrString);
     }
 
+    private static boolean isAtLeastR() {
+        // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R)
+        return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR();
+    }
+
     private void checkEmpty(final LinkProperties lp) {
         assertEquals(0, lp.getAllInterfaceNames().size());
         assertEquals(0, lp.getAllAddresses().size());
@@ -98,14 +121,17 @@
         assertNull(lp.getHttpProxy());
         assertNull(lp.getTcpBufferSizes());
         assertNull(lp.getNat64Prefix());
-        assertNull(lp.getDhcpServerAddress());
         assertFalse(lp.isProvisioned());
         assertFalse(lp.isIpv4Provisioned());
         assertFalse(lp.isIpv6Provisioned());
         assertFalse(lp.isPrivateDnsActive());
-        assertFalse(lp.isWakeOnLanSupported());
-        assertNull(lp.getCaptivePortalApiUrl());
-        assertNull(lp.getCaptivePortalData());
+
+        if (isAtLeastR()) {
+            assertNull(lp.getDhcpServerAddress());
+            assertFalse(lp.isWakeOnLanSupported());
+            assertNull(lp.getCaptivePortalApiUrl());
+            assertNull(lp.getCaptivePortalData());
+        }
     }
 
     private LinkProperties makeTestObject() {
@@ -127,10 +153,12 @@
         lp.setMtu(MTU);
         lp.setTcpBufferSizes(TCP_BUFFER_SIZES);
         lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
-        lp.setDhcpServerAddress(DHCPSERVER);
-        lp.setWakeOnLanSupported(true);
-        lp.setCaptivePortalApiUrl(CAPPORT_API_URL);
-        lp.setCaptivePortalData(CAPPORT_DATA);
+        if (isAtLeastR()) {
+            lp.setDhcpServerAddress(DHCPSERVER);
+            lp.setWakeOnLanSupported(true);
+            lp.setCaptivePortalApiUrl(CAPPORT_API_URL);
+            lp.setCaptivePortalData((CaptivePortalData) getCaptivePortalData());
+        }
         return lp;
     }
 
@@ -169,14 +197,19 @@
         assertTrue(source.isIdenticalTcpBufferSizes(target));
         assertTrue(target.isIdenticalTcpBufferSizes(source));
 
-        assertTrue(source.isIdenticalWakeOnLan(target));
-        assertTrue(target.isIdenticalWakeOnLan(source));
+        if (isAtLeastR()) {
+            assertTrue(source.isIdenticalDhcpServerAddress(target));
+            assertTrue(source.isIdenticalDhcpServerAddress(source));
 
-        assertTrue(source.isIdenticalCaptivePortalApiUrl(target));
-        assertTrue(target.isIdenticalCaptivePortalApiUrl(source));
+            assertTrue(source.isIdenticalWakeOnLan(target));
+            assertTrue(target.isIdenticalWakeOnLan(source));
 
-        assertTrue(source.isIdenticalCaptivePortalData(target));
-        assertTrue(target.isIdenticalCaptivePortalData(source));
+            assertTrue(source.isIdenticalCaptivePortalApiUrl(target));
+            assertTrue(target.isIdenticalCaptivePortalApiUrl(source));
+
+            assertTrue(source.isIdenticalCaptivePortalData(target));
+            assertTrue(target.isIdenticalCaptivePortalData(source));
+        }
 
         // Check result of equals().
         assertTrue(source.equals(target));
@@ -415,14 +448,20 @@
         // Check comparisons work.
         LinkProperties lp2 = new LinkProperties(lp);
         assertAllRoutesHaveInterface("wlan0", lp2);
-        assertEquals(0, lp.compareAllRoutes(lp2).added.size());
-        assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
+        // LinkProperties#compareAllRoutes exists both in R and before R, but the return type
+        // changed in R, so a test compiled with the R version of LinkProperties cannot run on Q.
+        if (isAtLeastR()) {
+            assertEquals(0, lp.compareAllRoutes(lp2).added.size());
+            assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
+        }
 
         lp2.setInterfaceName("p2p0");
         assertAllRoutesHaveInterface("p2p0", lp2);
         assertAllRoutesNotHaveInterface("wlan0", lp2);
-        assertEquals(3, lp.compareAllRoutes(lp2).added.size());
-        assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
+        if (isAtLeastR()) {
+            assertEquals(3, lp.compareAllRoutes(lp2).added.size());
+            assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
+        }
 
         // Remove route with incorrect interface, no route removed.
         lp.removeRoute(new RouteInfo(prefix2, null, null));
@@ -450,6 +489,8 @@
         assertEquals(1, rmnet0.getLinkAddresses().size());
         assertEquals(1, rmnet0.getAllAddresses().size());
         assertEquals(1, rmnet0.getAllLinkAddresses().size());
+        assertEquals(1, rmnet0.getAllInterfaceNames().size());
+        assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0));
 
         rmnet0.addStackedLink(clat4);
         assertEquals(1, rmnet0.getStackedLinks().size());
@@ -457,6 +498,9 @@
         assertEquals(1, rmnet0.getLinkAddresses().size());
         assertEquals(2, rmnet0.getAllAddresses().size());
         assertEquals(2, rmnet0.getAllLinkAddresses().size());
+        assertEquals(2, rmnet0.getAllInterfaceNames().size());
+        assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0));
+        assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1));
 
         rmnet0.addStackedLink(clat4);
         assertEquals(1, rmnet0.getStackedLinks().size());
@@ -464,6 +508,9 @@
         assertEquals(1, rmnet0.getLinkAddresses().size());
         assertEquals(2, rmnet0.getAllAddresses().size());
         assertEquals(2, rmnet0.getAllLinkAddresses().size());
+        assertEquals(2, rmnet0.getAllInterfaceNames().size());
+        assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0));
+        assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1));
 
         assertEquals(0, clat4.getStackedLinks().size());
 
@@ -483,6 +530,8 @@
         assertEquals(1, rmnet0.getLinkAddresses().size());
         assertEquals(1, rmnet0.getAllAddresses().size());
         assertEquals(1, rmnet0.getAllLinkAddresses().size());
+        assertEquals(1, rmnet0.getAllInterfaceNames().size());
+        assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0));
 
         assertFalse(rmnet0.removeStackedLink("clat4"));
     }
@@ -906,7 +955,7 @@
 
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testCompareResult() {
         // Either adding or removing items
         compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(1),
@@ -943,8 +992,7 @@
         assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
     }
 
-    @Test
-    public void testLinkPropertiesParcelable() throws Exception {
+    private static LinkProperties makeLinkPropertiesForParceling() {
         LinkProperties source = new LinkProperties();
         source.setInterfaceName(NAME);
 
@@ -978,17 +1026,29 @@
 
         source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
 
-        source.setWakeOnLanSupported(true);
-        source.setCaptivePortalApiUrl(CAPPORT_API_URL);
-        source.setCaptivePortalData(CAPPORT_DATA);
-
-        source.setDhcpServerAddress((Inet4Address) GATEWAY1);
-
         final LinkProperties stacked = new LinkProperties();
         stacked.setInterfaceName("test-stacked");
         source.addStackedLink(stacked);
 
-        assertParcelSane(source.makeSensitiveFieldsParcelingCopy(), 18 /* fieldCount */);
+        return source;
+    }
+
+    @Test @IgnoreAfter(Build.VERSION_CODES.Q)
+    public void testLinkPropertiesParcelable_Q() throws Exception {
+        final LinkProperties source = makeLinkPropertiesForParceling();
+        assertParcelSane(source, 14 /* fieldCount */);
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testLinkPropertiesParcelable() throws Exception {
+        final LinkProperties source = makeLinkPropertiesForParceling();
+
+        source.setWakeOnLanSupported(true);
+        source.setCaptivePortalApiUrl(CAPPORT_API_URL);
+        source.setCaptivePortalData((CaptivePortalData) getCaptivePortalData());
+        source.setDhcpServerAddress((Inet4Address) GATEWAY1);
+        assertParcelSane(new LinkProperties(source, true /* parcelSensitiveFields */),
+                18 /* fieldCount */);
 
         // Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared.
         final LinkProperties sanitized = new LinkProperties(source);
@@ -997,7 +1057,8 @@
         assertEquals(sanitized, parcelingRoundTrip(source));
     }
 
-    @Test
+    // Parceling of the scope was broken until Q-QPR2
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testLinkLocalDnsServerParceling() throws Exception {
         final String strAddress = "fe80::1%lo";
         final LinkProperties lp = new LinkProperties();
@@ -1120,7 +1181,7 @@
         assertFalse(lp.isPrivateDnsActive());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testDhcpServerAddress() {
         final LinkProperties lp = makeTestObject();
         assertEquals(DHCPSERVER, lp.getDhcpServerAddress());
@@ -1129,7 +1190,7 @@
         assertNull(lp.getDhcpServerAddress());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testWakeOnLanSupported() {
         final LinkProperties lp = makeTestObject();
         assertTrue(lp.isWakeOnLanSupported());
@@ -1138,7 +1199,7 @@
         assertFalse(lp.isWakeOnLanSupported());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testCaptivePortalApiUrl() {
         final LinkProperties lp = makeTestObject();
         assertEquals(CAPPORT_API_URL, lp.getCaptivePortalApiUrl());
@@ -1147,12 +1208,78 @@
         assertNull(lp.getCaptivePortalApiUrl());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testCaptivePortalData() {
         final LinkProperties lp = makeTestObject();
-        assertEquals(CAPPORT_DATA, lp.getCaptivePortalData());
+        assertEquals(getCaptivePortalData(), lp.getCaptivePortalData());
 
         lp.clear();
         assertNull(lp.getCaptivePortalData());
     }
+
+    private LinkProperties makeIpv4LinkProperties() {
+        final LinkProperties linkProperties = new LinkProperties();
+        linkProperties.setInterfaceName(NAME);
+        linkProperties.addLinkAddress(LINKADDRV4);
+        linkProperties.addDnsServer(DNS1);
+        linkProperties.addRoute(new RouteInfo(GATEWAY1));
+        linkProperties.addRoute(new RouteInfo(GATEWAY2));
+        return linkProperties;
+    }
+
+    private LinkProperties makeIpv6LinkProperties() {
+        final LinkProperties linkProperties = new LinkProperties();
+        linkProperties.setInterfaceName(NAME);
+        linkProperties.addLinkAddress(LINKADDRV6);
+        linkProperties.addDnsServer(DNS6);
+        linkProperties.addRoute(new RouteInfo(GATEWAY61));
+        linkProperties.addRoute(new RouteInfo(GATEWAY62));
+        return linkProperties;
+    }
+
+    @Test
+    public void testHasIpv4DefaultRoute() {
+        final LinkProperties Ipv4 = makeIpv4LinkProperties();
+        assertTrue(Ipv4.hasIpv4DefaultRoute());
+        final LinkProperties Ipv6 = makeIpv6LinkProperties();
+        assertFalse(Ipv6.hasIpv4DefaultRoute());
+    }
+
+    @Test
+    public void testHasIpv4DnsServer() {
+        final LinkProperties Ipv4 = makeIpv4LinkProperties();
+        assertTrue(Ipv4.hasIpv4DnsServer());
+        final LinkProperties Ipv6 = makeIpv6LinkProperties();
+        assertFalse(Ipv6.hasIpv4DnsServer());
+    }
+
+    @Test
+    public void testHasIpv6DnsServer() {
+        final LinkProperties Ipv4 = makeIpv4LinkProperties();
+        assertFalse(Ipv4.hasIpv6DnsServer());
+        final LinkProperties Ipv6 = makeIpv6LinkProperties();
+        assertTrue(Ipv6.hasIpv6DnsServer());
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testHasIpv4UnreachableDefaultRoute() {
+        final LinkProperties lp = makeTestObject();
+        assertFalse(lp.hasIpv4UnreachableDefaultRoute());
+        assertFalse(lp.hasIpv6UnreachableDefaultRoute());
+
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+        assertTrue(lp.hasIpv4UnreachableDefaultRoute());
+        assertFalse(lp.hasIpv6UnreachableDefaultRoute());
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testHasIpv6UnreachableDefaultRoute() {
+        final LinkProperties lp = makeTestObject();
+        assertFalse(lp.hasIpv6UnreachableDefaultRoute());
+        assertFalse(lp.hasIpv4UnreachableDefaultRoute());
+
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+        assertTrue(lp.hasIpv6UnreachableDefaultRoute());
+        assertFalse(lp.hasIpv4UnreachableDefaultRoute());
+    }
 }
diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
new file mode 100644
index 0000000..a50f046
--- /dev/null
+++ b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.net
+
+import android.net.wifi.aware.DiscoverySession
+import android.net.wifi.aware.PeerHandle
+import android.net.wifi.aware.WifiAwareNetworkSpecifier
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+
+import com.android.testutils.assertParcelSane
+
+import java.lang.IllegalStateException
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MatchAllNetworkSpecifierTest {
+    @Test
+    fun testParcel() {
+        assertParcelSane(MatchAllNetworkSpecifier(), 0)
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testCanBeSatisfiedBy() {
+        val specifier = MatchAllNetworkSpecifier()
+        val discoverySession = Mockito.mock(DiscoverySession::class.java)
+        val peerHandle = Mockito.mock(PeerHandle::class.java)
+        val wifiAwareNetworkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession,
+                peerHandle).build()
+        specifier.canBeSatisfiedBy(wifiAwareNetworkSpecifier)
+    }
+}
diff --git a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
new file mode 100644
index 0000000..46f39dd
--- /dev/null
+++ b/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.net
+
+import android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS
+import android.net.InvalidPacketException.ERROR_INVALID_PORT
+import android.net.NattSocketKeepalive.NATT_PORT
+import android.os.Build
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.assertEqualBothWays
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertParcelSane
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.parcelingRoundTrip
+import java.net.InetAddress
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NattKeepalivePacketDataTest {
+    @Rule @JvmField
+    val ignoreRule: DevSdkIgnoreRule = DevSdkIgnoreRule()
+
+    /* Refer to the definition in {@code NattKeepalivePacketData} */
+    private val IPV4_HEADER_LENGTH = 20
+    private val UDP_HEADER_LENGTH = 8
+
+    private val TEST_PORT = 4243
+    private val TEST_PORT2 = 4244
+    private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
+    private val TEST_DST_ADDRV4 = "198.168.0.1".address()
+    private val TEST_ADDRV6 = "2001:db8::1".address()
+
+    private fun String.address() = InetAddresses.parseNumericAddress(this)
+    private fun nattKeepalivePacket(
+        srcAddress: InetAddress? = TEST_SRC_ADDRV4,
+        srcPort: Int = TEST_PORT,
+        dstAddress: InetAddress? = TEST_DST_ADDRV4,
+        dstPort: Int = NATT_PORT
+    ) = NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort, dstAddress, dstPort)
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testConstructor() {
+        try {
+            nattKeepalivePacket(dstPort = TEST_PORT)
+            fail("Dst port is not NATT port should cause exception")
+        } catch (e: InvalidPacketException) {
+            assertEquals(e.error, ERROR_INVALID_PORT)
+        }
+
+        try {
+            nattKeepalivePacket(srcAddress = TEST_ADDRV6)
+            fail("A v6 srcAddress should cause exception")
+        } catch (e: InvalidPacketException) {
+            assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+        }
+
+        try {
+            nattKeepalivePacket(dstAddress = TEST_ADDRV6)
+            fail("A v6 dstAddress should cause exception")
+        } catch (e: InvalidPacketException) {
+            assertEquals(e.error, ERROR_INVALID_IP_ADDRESS)
+        }
+
+        try {
+            parcelingRoundTrip(
+                    NattKeepalivePacketData(TEST_SRC_ADDRV4, TEST_PORT, TEST_DST_ADDRV4, TEST_PORT,
+                    byteArrayOf(12, 31, 22, 44)))
+            fail("Invalid data should cause exception")
+        } catch (e: IllegalArgumentException) { }
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testParcel() {
+        assertParcelSane(nattKeepalivePacket(), 0)
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testEquals() {
+        assertEqualBothWays(nattKeepalivePacket(), nattKeepalivePacket())
+        assertNotEquals(nattKeepalivePacket(dstAddress = TEST_SRC_ADDRV4), nattKeepalivePacket())
+        assertNotEquals(nattKeepalivePacket(srcAddress = TEST_DST_ADDRV4), nattKeepalivePacket())
+        // Test src port only because dst port have to be NATT_PORT
+        assertNotEquals(nattKeepalivePacket(srcPort = TEST_PORT2), nattKeepalivePacket())
+        // Make sure the parceling test is updated if fields are added in the base class.
+        assertFieldCountEquals(5, KeepalivePacketData::class.java)
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testHashCode() {
+        assertEquals(nattKeepalivePacket().hashCode(), nattKeepalivePacket().hashCode())
+    }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
index 173dbd1..de65ba2 100644
--- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
@@ -22,6 +22,9 @@
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.assertParcelSane
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -43,4 +46,27 @@
         }.build()
         assertParcelSane(config, 9)
     }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testBuilder() {
+        val config = NetworkAgentConfig.Builder().apply {
+            setExplicitlySelected(true)
+            setLegacyType(ConnectivityManager.TYPE_ETHERNET)
+            setSubscriberId("MySubId")
+            setPartialConnectivityAcceptable(false)
+            setUnvalidatedConnectivityAcceptable(true)
+            setLegacyTypeName("TEST_NETWORK")
+            disableNat64Detection()
+            disableProvisioningNotification()
+        }.build()
+
+        assertTrue(config.isExplicitlySelected())
+        assertEquals(ConnectivityManager.TYPE_ETHERNET, config.getLegacyType())
+        assertEquals("MySubId", config.getSubscriberId())
+        assertFalse(config.isPartialConnectivityAcceptable())
+        assertTrue(config.isUnvalidatedConnectivityAcceptable())
+        assertEquals("TEST_NETWORK", config.getLegacyTypeName())
+        assertFalse(config.isNat64DetectionEnabled())
+        assertFalse(config.isProvisioningNotificationEnabled())
+    }
 }
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index efea91a..3f8261d 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -17,6 +17,8 @@
 package android.net;
 
 import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+import static android.net.NetworkCapabilities.MAX_TRANSPORT;
+import static android.net.NetworkCapabilities.MIN_TRANSPORT;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
@@ -32,10 +34,12 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
 import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
+import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
 import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
 
 import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
@@ -45,17 +49,30 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.net.wifi.aware.DiscoverySession;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
+import android.os.Build;
+import android.os.Process;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 
+import androidx.core.os.BuildCompat;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
+import java.util.Arrays;
 import java.util.Set;
 
 @RunWith(AndroidJUnit4.class)
@@ -64,6 +81,19 @@
     private static final String TEST_SSID = "TEST_SSID";
     private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
 
+    @Rule
+    public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
+
+    private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
+    private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
+
+    private boolean isAtLeastR() {
+        // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
+        // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
+        // releasing Android R.
+        return BuildCompat.isAtLeastR() || Build.VERSION.SDK_INT > Build.VERSION_CODES.Q;
+    }
+
     @Test
     public void testMaybeMarkCapabilitiesRestricted() {
         // verify EIMS is restricted
@@ -269,25 +299,37 @@
             .setUids(uids)
             .addCapability(NET_CAPABILITY_EIMS)
             .addCapability(NET_CAPABILITY_NOT_METERED);
-        netCap.setOwnerUid(123);
+        if (isAtLeastR()) {
+            netCap.setOwnerUid(123);
+            netCap.setAdministratorUids(new int[] {5, 11});
+        }
         assertParcelingIsLossless(netCap);
         netCap.setSSID(TEST_SSID);
-        assertParcelSane(netCap, 15);
+        testParcelSane(netCap);
     }
 
     @Test
     public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() {
         final NetworkCapabilities netCap = new NetworkCapabilities()
                 .addCapability(NET_CAPABILITY_INTERNET)
-                .setRequestorUid(9304)
-                .setRequestorPackageName("com.android.test")
                 .addCapability(NET_CAPABILITY_EIMS)
                 .addCapability(NET_CAPABILITY_NOT_METERED);
+        if (isAtLeastR()) {
+            netCap.setRequestorPackageName("com.android.test");
+            netCap.setRequestorUid(9304);
+        }
         assertParcelingIsLossless(netCap);
         netCap.setSSID(TEST_SSID);
-        assertParcelSane(netCap, 15);
+        testParcelSane(netCap);
     }
 
+    private void testParcelSane(NetworkCapabilities cap) {
+        if (isAtLeastR()) {
+            assertParcelSane(cap, 15);
+        } else {
+            assertParcelSane(cap, 11);
+        }
+    }
 
     @Test
     public void testOemPaid() {
@@ -419,6 +461,23 @@
         return range;
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testSetAdministratorUids() {
+        NetworkCapabilities nc =
+                new NetworkCapabilities().setAdministratorUids(new int[] {2, 1, 3});
+
+        assertArrayEquals(new int[] {1, 2, 3}, nc.getAdministratorUids());
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testSetAdministratorUidsWithDuplicates() {
+        try {
+            new NetworkCapabilities().setAdministratorUids(new int[] {1, 1});
+            fail("Expected IllegalArgumentException for duplicate uids");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
     @Test
     public void testCombineCapabilities() {
         NetworkCapabilities nc1 = new NetworkCapabilities();
@@ -443,7 +502,9 @@
 
         nc1.setSSID(TEST_SSID);
         nc2.combineCapabilities(nc1);
-        assertTrue(TEST_SSID.equals(nc2.getSSID()));
+        if (isAtLeastR()) {
+            assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        }
 
         // Because they now have the same SSID, the following call should not throw
         nc2.combineCapabilities(nc1);
@@ -471,6 +532,31 @@
         assertTrue(nc2.appliesToUid(22));
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testCombineCapabilities_AdministratorUids() {
+        final NetworkCapabilities nc1 = new NetworkCapabilities();
+        final NetworkCapabilities nc2 = new NetworkCapabilities();
+
+        final int[] adminUids = {3, 6, 12};
+        nc1.setAdministratorUids(adminUids);
+        nc2.combineCapabilities(nc1);
+        assertTrue(nc2.equalsAdministratorUids(nc1));
+        assertArrayEquals(nc2.getAdministratorUids(), adminUids);
+
+        final int[] adminUidsOtherOrder = {3, 12, 6};
+        nc1.setAdministratorUids(adminUidsOtherOrder);
+        assertTrue(nc2.equalsAdministratorUids(nc1));
+
+        final int[] adminUids2 = {11, 1, 12, 3, 6};
+        nc1.setAdministratorUids(adminUids2);
+        assertFalse(nc2.equalsAdministratorUids(nc1));
+        assertFalse(Arrays.equals(nc2.getAdministratorUids(), adminUids2));
+        try {
+            nc2.combineCapabilities(nc1);
+            fail("Shouldn't be able to combine different lists of admin UIDs");
+        } catch (IllegalStateException expected) { }
+    }
+
     @Test
     public void testSetCapabilities() {
         final int[] REQUIRED_CAPABILITIES = new int[] {
@@ -581,12 +667,16 @@
         // from nc2.
         assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(TEST_SSID.equals(nc2.getSSID()));
+        if (isAtLeastR()) {
+            assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        }
 
         nc1.setSSID(DIFFERENT_TEST_SSID);
         nc2.set(nc1);
         assertEquals(nc1, nc2);
-        assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSSID()));
+        if (isAtLeastR()) {
+            assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+        }
 
         nc1.setUids(uidRange(10, 13));
         nc2.set(nc1);  // Overwrites, as opposed to combineCapabilities
@@ -608,4 +698,238 @@
         assertEquals(TRANSPORT_VPN, transportTypes[2]);
         assertEquals(TRANSPORT_TEST, transportTypes[3]);
     }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testTelephonyNetworkSpecifier() {
+        final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+        final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_WIFI)
+                .setNetworkSpecifier(specifier)
+                .build();
+        assertEquals(specifier, nc1.getNetworkSpecifier());
+        try {
+            final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                    .setNetworkSpecifier(specifier)
+                    .build();
+            fail("Must have a single transport type. Without transport type or multiple transport"
+                    + " types is invalid.");
+        } catch (IllegalStateException expected) { }
+    }
+
+    @Test
+    public void testWifiAwareNetworkSpecifier() {
+        final NetworkCapabilities nc = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_WIFI_AWARE);
+        // If NetworkSpecifier is not set, the default value is null.
+        assertNull(nc.getNetworkSpecifier());
+        final WifiAwareNetworkSpecifier specifier = new WifiAwareNetworkSpecifier.Builder(
+                mDiscoverySession, mPeerHandle).build();
+        nc.setNetworkSpecifier(specifier);
+        assertEquals(specifier, nc.getNetworkSpecifier());
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testAdministratorUidsAndOwnerUid() {
+        // Test default owner uid.
+        // If the owner uid is not set, the default value should be Process.INVALID_UID.
+        final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build();
+        assertEquals(Process.INVALID_UID, nc1.getOwnerUid());
+        // Test setAdministratorUids and getAdministratorUids.
+        final int[] administratorUids = {1001, 10001};
+        final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                .setAdministratorUids(administratorUids)
+                .build();
+        assertTrue(Arrays.equals(administratorUids, nc2.getAdministratorUids()));
+        // Test setOwnerUid and getOwnerUid.
+        // The owner UID must be included in administrator UIDs, or throw IllegalStateException.
+        try {
+            final NetworkCapabilities nc3 = new NetworkCapabilities.Builder()
+                    .setOwnerUid(1001)
+                    .build();
+            fail("The owner UID must be included in administrator UIDs.");
+        } catch (IllegalStateException expected) { }
+        final NetworkCapabilities nc4 = new NetworkCapabilities.Builder()
+                .setAdministratorUids(administratorUids)
+                .setOwnerUid(1001)
+                .build();
+        assertEquals(1001, nc4.getOwnerUid());
+        try {
+            final NetworkCapabilities nc5 = new NetworkCapabilities.Builder()
+                    .setAdministratorUids(null)
+                    .build();
+            fail("Should not set null into setAdministratorUids");
+        } catch (NullPointerException expected) { }
+    }
+
+    @Test
+    public void testLinkBandwidthKbps() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        // The default value of LinkDown/UpstreamBandwidthKbps should be LINK_BANDWIDTH_UNSPECIFIED.
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkDownstreamBandwidthKbps());
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkUpstreamBandwidthKbps());
+        nc.setLinkDownstreamBandwidthKbps(512);
+        nc.setLinkUpstreamBandwidthKbps(128);
+        assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+        assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+        assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+        assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+    }
+
+    @Test
+    public void testSignalStrength() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        // The default value of signal strength should be SIGNAL_STRENGTH_UNSPECIFIED.
+        assertEquals(SIGNAL_STRENGTH_UNSPECIFIED, nc.getSignalStrength());
+        nc.setSignalStrength(-80);
+        assertEquals(-80, nc.getSignalStrength());
+        assertNotEquals(-50, nc.getSignalStrength());
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testDeduceRestrictedCapability() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        // Default capabilities don't have restricted capability.
+        assertFalse(nc.deduceRestrictedCapability());
+        // If there is a force restricted capability, then the network capabilities is restricted.
+        nc.addCapability(NET_CAPABILITY_OEM_PAID);
+        nc.addCapability(NET_CAPABILITY_INTERNET);
+        assertTrue(nc.deduceRestrictedCapability());
+        // Except for the force restricted capability, if there is any unrestricted capability in
+        // capabilities, then the network capabilities is not restricted.
+        nc.removeCapability(NET_CAPABILITY_OEM_PAID);
+        nc.addCapability(NET_CAPABILITY_CBS);
+        assertFalse(nc.deduceRestrictedCapability());
+        // Except for the force restricted capability, the network capabilities will only be treated
+        // as restricted when there is no any unrestricted capability.
+        nc.removeCapability(NET_CAPABILITY_INTERNET);
+        assertTrue(nc.deduceRestrictedCapability());
+    }
+
+    private void assertNoTransport(NetworkCapabilities nc) {
+        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+            assertFalse(nc.hasTransport(i));
+        }
+    }
+
+    // Checks that all transport types from MIN_TRANSPORT to maxTransportType are set and all
+    // transport types from maxTransportType + 1 to MAX_TRANSPORT are not set when positiveSequence
+    // is true. If positiveSequence is false, then the check sequence is opposite.
+    private void checkCurrentTransportTypes(NetworkCapabilities nc, int maxTransportType,
+            boolean positiveSequence) {
+        for (int i = MIN_TRANSPORT; i <= maxTransportType; i++) {
+            if (positiveSequence) {
+                assertTrue(nc.hasTransport(i));
+            } else {
+                assertFalse(nc.hasTransport(i));
+            }
+        }
+        for (int i = MAX_TRANSPORT; i > maxTransportType; i--) {
+            if (positiveSequence) {
+                assertFalse(nc.hasTransport(i));
+            } else {
+                assertTrue(nc.hasTransport(i));
+            }
+        }
+    }
+
+    @Test
+    public void testMultipleTransportTypes() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        assertNoTransport(nc);
+        // Test adding multiple transport types.
+        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+            nc.addTransportType(i);
+            checkCurrentTransportTypes(nc, i, true /* positiveSequence */);
+        }
+        // Test removing multiple transport types.
+        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+            nc.removeTransportType(i);
+            checkCurrentTransportTypes(nc, i, false /* positiveSequence */);
+        }
+        assertNoTransport(nc);
+        nc.addTransportType(TRANSPORT_WIFI);
+        assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(nc.hasTransport(TRANSPORT_VPN));
+        nc.addTransportType(TRANSPORT_VPN);
+        assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+        assertTrue(nc.hasTransport(TRANSPORT_VPN));
+        nc.removeTransportType(TRANSPORT_WIFI);
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        assertTrue(nc.hasTransport(TRANSPORT_VPN));
+        nc.removeTransportType(TRANSPORT_VPN);
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(nc.hasTransport(TRANSPORT_VPN));
+        assertNoTransport(nc);
+    }
+
+    @Test
+    public void testAddAndRemoveTransportType() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        try {
+            nc.addTransportType(-1);
+            fail("Should not set invalid transport type into addTransportType");
+        } catch (IllegalArgumentException expected) { }
+        try {
+            nc.removeTransportType(-1);
+            fail("Should not set invalid transport type into removeTransportType");
+        } catch (IllegalArgumentException e) { }
+    }
+
+    private class TestTransportInfo implements TransportInfo {
+        TestTransportInfo() {
+        }
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testBuilder() {
+        final int ownerUid = 1001;
+        final int signalStrength = -80;
+        final int requestUid = 10100;
+        final int[] administratorUids = {ownerUid, 10001};
+        final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+        final TestTransportInfo transportInfo = new TestTransportInfo();
+        final String ssid = "TEST_SSID";
+        final String packageName = "com.google.test.networkcapabilities";
+        final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_WIFI)
+                .addTransportType(TRANSPORT_CELLULAR)
+                .removeTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_EIMS)
+                .addCapability(NET_CAPABILITY_CBS)
+                .removeCapability(NET_CAPABILITY_CBS)
+                .setAdministratorUids(administratorUids)
+                .setOwnerUid(ownerUid)
+                .setLinkDownstreamBandwidthKbps(512)
+                .setLinkUpstreamBandwidthKbps(128)
+                .setNetworkSpecifier(specifier)
+                .setTransportInfo(transportInfo)
+                .setSignalStrength(signalStrength)
+                .setSsid(ssid)
+                .setRequestorUid(requestUid)
+                .setRequestorPackageName(packageName)
+                .build();
+        assertEquals(1, nc.getTransportTypes().length);
+        assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]);
+        assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS));
+        assertFalse(nc.hasCapability(NET_CAPABILITY_CBS));
+        assertTrue(Arrays.equals(administratorUids, nc.getAdministratorUids()));
+        assertEquals(ownerUid, nc.getOwnerUid());
+        assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+        assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+        assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+        assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+        assertEquals(specifier, nc.getNetworkSpecifier());
+        assertEquals(transportInfo, nc.getTransportInfo());
+        assertEquals(signalStrength, nc.getSignalStrength());
+        assertNotEquals(-50, nc.getSignalStrength());
+        assertEquals(ssid, nc.getSsid());
+        assertEquals(requestUid, nc.getRequestorUid());
+        assertEquals(packageName, nc.getRequestorPackageName());
+        // Cannot assign null into NetworkCapabilities.Builder
+        try {
+            final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null);
+            fail("Should not set null into NetworkCapabilities.Builder");
+        } catch (NullPointerException expected) { }
+        assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+    }
 }
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
new file mode 100644
index 0000000..b7c47c2
--- /dev/null
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -0,0 +1,187 @@
+/*
+ * 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.net
+
+import android.app.Instrumentation
+import android.content.Context
+import android.net.NetworkCapabilities.TRANSPORT_TEST
+import android.os.Build
+import android.os.HandlerThread
+import android.os.Looper
+import android.net.NetworkProviderTest.TestNetworkCallback.CallbackEntry.OnUnavailable
+import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequested
+import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequestWithdrawn
+import androidx.test.InstrumentationRegistry
+import com.android.testutils.ArrayTrackRecord
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import java.util.UUID
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val DEFAULT_TIMEOUT_MS = 5000L
+private val instrumentation: Instrumentation
+    get() = InstrumentationRegistry.getInstrumentation()
+private val context: Context get() = InstrumentationRegistry.getContext()
+private val PROVIDER_NAME = "NetworkProviderTest"
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.Q)
+class NetworkProviderTest {
+    private val mCm = context.getSystemService(ConnectivityManager::class.java)
+    private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
+
+    @Before
+    fun setUp() {
+        instrumentation.getUiAutomation().adoptShellPermissionIdentity()
+        mHandlerThread.start()
+    }
+
+    @After
+    fun tearDown() {
+        mHandlerThread.quitSafely()
+        instrumentation.getUiAutomation().dropShellPermissionIdentity()
+    }
+
+    private class TestNetworkProvider(context: Context, looper: Looper) :
+            NetworkProvider(context, looper, PROVIDER_NAME) {
+        private val seenEvents = ArrayTrackRecord<CallbackEntry>().newReadHead()
+
+        sealed class CallbackEntry {
+            data class OnNetworkRequested(
+                val request: NetworkRequest,
+                val score: Int,
+                val id: Int
+            ) : CallbackEntry()
+            data class OnNetworkRequestWithdrawn(val request: NetworkRequest) : CallbackEntry()
+        }
+
+        override fun onNetworkRequested(request: NetworkRequest, score: Int, id: Int) {
+            seenEvents.add(OnNetworkRequested(request, score, id))
+        }
+
+        override fun onNetworkRequestWithdrawn(request: NetworkRequest) {
+            seenEvents.add(OnNetworkRequestWithdrawn(request))
+        }
+
+        inline fun <reified T : CallbackEntry> expectCallback(
+            crossinline predicate: (T) -> Boolean
+        ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) }
+    }
+
+    private fun createNetworkProvider(): TestNetworkProvider {
+        return TestNetworkProvider(context, mHandlerThread.looper)
+    }
+
+    @Test
+    fun testOnNetworkRequested() {
+        val provider = createNetworkProvider()
+        assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
+        mCm.registerNetworkProvider(provider)
+        assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
+
+        val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+        val nr: NetworkRequest = NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_TEST)
+                .setNetworkSpecifier(specifier)
+                .build()
+        val cb = ConnectivityManager.NetworkCallback()
+        mCm.requestNetwork(nr, cb)
+        provider.expectCallback<OnNetworkRequested>() { callback ->
+            callback.request.getNetworkSpecifier() == specifier &&
+            callback.request.hasTransport(TRANSPORT_TEST)
+        }
+
+        val initialScore = 40
+        val updatedScore = 60
+        val nc = NetworkCapabilities().apply {
+                addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+                removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+                removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+                addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
+                addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+                setNetworkSpecifier(specifier)
+        }
+        val lp = LinkProperties()
+        val config = NetworkAgentConfig.Builder().build()
+        val agent = object : NetworkAgent(context, mHandlerThread.looper, "TestAgent", nc, lp,
+                initialScore, config, provider) {}
+
+        provider.expectCallback<OnNetworkRequested>() { callback ->
+            callback.request.getNetworkSpecifier() == specifier &&
+            callback.score == initialScore &&
+            callback.id == agent.providerId
+        }
+
+        agent.sendNetworkScore(updatedScore)
+        provider.expectCallback<OnNetworkRequested>() { callback ->
+            callback.request.getNetworkSpecifier() == specifier &&
+            callback.score == updatedScore &&
+            callback.id == agent.providerId
+        }
+
+        mCm.unregisterNetworkCallback(cb)
+        provider.expectCallback<OnNetworkRequestWithdrawn>() { callback ->
+            callback.request.getNetworkSpecifier() == specifier &&
+            callback.request.hasTransport(TRANSPORT_TEST)
+        }
+        mCm.unregisterNetworkProvider(provider)
+        // Provider id should be ID_NONE after unregister network provider
+        assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE)
+        // unregisterNetworkProvider should not crash even if it's called on an
+        // already unregistered provider.
+        mCm.unregisterNetworkProvider(provider)
+    }
+
+    private class TestNetworkCallback : ConnectivityManager.NetworkCallback() {
+        private val seenEvents = ArrayTrackRecord<CallbackEntry>().newReadHead()
+        sealed class CallbackEntry {
+            object OnUnavailable : CallbackEntry()
+        }
+
+        override fun onUnavailable() {
+            seenEvents.add(OnUnavailable)
+        }
+
+        inline fun <reified T : CallbackEntry> expectCallback(
+            crossinline predicate: (T) -> Boolean
+        ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) }
+    }
+
+    @Test
+    fun testDeclareNetworkRequestUnfulfillable() {
+        val provider = createNetworkProvider()
+        mCm.registerNetworkProvider(provider)
+
+        val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
+        val nr: NetworkRequest = NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_TEST)
+                .setNetworkSpecifier(specifier)
+                .build()
+
+        val cb = TestNetworkCallback()
+        mCm.requestNetwork(nr, cb)
+        provider.declareNetworkRequestUnfulfillable(nr)
+        cb.expectCallback<OnUnavailable>() { nr.getNetworkSpecifier() == specifier }
+        mCm.unregisterNetworkProvider(provider)
+    }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkSpecifierTest.kt b/tests/net/common/java/android/net/NetworkSpecifierTest.kt
new file mode 100644
index 0000000..f3409f5
--- /dev/null
+++ b/tests/net/common/java/android/net/NetworkSpecifierTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.net
+
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertTrue
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNotEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.Q)
+class NetworkSpecifierTest {
+    private class TestNetworkSpecifier(
+        val intData: Int = 123,
+        val stringData: String = "init"
+    ) : NetworkSpecifier() {
+        override fun canBeSatisfiedBy(other: NetworkSpecifier?): Boolean =
+                other != null &&
+                other is TestNetworkSpecifier &&
+                other.intData >= intData &&
+                stringData.equals(other.stringData)
+
+        override fun redact(): NetworkSpecifier = TestNetworkSpecifier(intData, "redact")
+    }
+
+    @Test
+    fun testRedact() {
+        val ns: TestNetworkSpecifier = TestNetworkSpecifier()
+        val redactNs = ns.redact()
+        assertTrue(redactNs is TestNetworkSpecifier)
+        assertEquals(ns.intData, redactNs.intData)
+        assertNotEquals(ns.stringData, redactNs.stringData)
+        assertTrue("redact".equals(redactNs.stringData))
+    }
+
+    @Test
+    fun testcanBeSatisfiedBy() {
+        val target: TestNetworkSpecifier = TestNetworkSpecifier()
+        assertFalse(target.canBeSatisfiedBy(null))
+        assertTrue(target.canBeSatisfiedBy(TestNetworkSpecifier()))
+        val otherNs = TelephonyNetworkSpecifier.Builder().setSubscriptionId(123).build()
+        assertFalse(target.canBeSatisfiedBy(otherNs))
+        assertTrue(target.canBeSatisfiedBy(TestNetworkSpecifier(intData = 999)))
+        assertFalse(target.canBeSatisfiedBy(TestNetworkSpecifier(intData = 1)))
+        assertFalse(target.canBeSatisfiedBy(TestNetworkSpecifier(stringData = "diff")))
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/NetworkStackTest.java b/tests/net/common/java/android/net/NetworkStackTest.java
similarity index 83%
rename from tests/net/java/android/net/NetworkStackTest.java
rename to tests/net/common/java/android/net/NetworkStackTest.java
index f7c6c99..a99aa01 100644
--- a/tests/net/java/android/net/NetworkStackTest.java
+++ b/tests/net/common/java/android/net/NetworkStackTest.java
@@ -22,16 +22,23 @@
 import static android.net.NetworkStack.checkNetworkStackPermission;
 import static android.net.NetworkStack.checkNetworkStackPermissionOr;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.os.Build;
+import android.os.IBinder;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -41,7 +48,11 @@
 public class NetworkStackTest {
     private static final String [] OTHER_PERMISSION = {"otherpermission1", "otherpermission2"};
 
+    @Rule
+    public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
+
     @Mock Context mCtx;
+    @Mock private IBinder mConnectorBinder;
 
     @Before public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -72,4 +83,10 @@
 
         fail("Expect fail but permission granted.");
     }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testGetService() {
+        NetworkStack.setServiceForTest(mConnectorBinder);
+        assertEquals(NetworkStack.getService(), mConnectorBinder);
+    }
 }
diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java
index 1658262..8204b49 100644
--- a/tests/net/common/java/android/net/RouteInfoTest.java
+++ b/tests/net/common/java/android/net/RouteInfoTest.java
@@ -31,6 +31,7 @@
 
 import android.os.Build;
 
+import androidx.core.os.BuildCompat;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -62,6 +63,11 @@
         return new IpPrefix(prefix);
     }
 
+    private static boolean isAtLeastR() {
+        // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R)
+        return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR();
+    }
+
     @Test
     public void testConstructor() {
         RouteInfo r;
@@ -195,78 +201,130 @@
         assertTrue(r.isDefaultRoute());
         assertTrue(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("::/0"), Address("::"), "wlan0");
         assertFalse(r.isHostRoute());
         assertTrue(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertTrue(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0");
         assertFalse(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("2001:db8::/48"), null, "wlan0");
         assertFalse(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("192.0.2.0/32"), Address("0.0.0.0"), "wlan0");
         assertTrue(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("2001:db8::/128"), Address("::"), "wlan0");
         assertTrue(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("192.0.2.0/32"), null, "wlan0");
         assertTrue(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("2001:db8::/128"), null, "wlan0");
         assertTrue(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("::/128"), Address("fe80::"), "wlan0");
         assertTrue(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0");
         assertTrue(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0");
         assertTrue(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE);
         assertFalse(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertTrue(r.isIPv4UnreachableDefault());
+            assertFalse(r.isIPv6UnreachableDefault());
+        }
 
         r = new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE);
         assertFalse(r.isHostRoute());
         assertFalse(r.isDefaultRoute());
         assertFalse(r.isIPv4Default());
         assertFalse(r.isIPv6Default());
+        if (isAtLeastR()) {
+            assertFalse(r.isIPv4UnreachableDefault());
+            assertTrue(r.isIPv6UnreachableDefault());
+        }
     }
 
     @Test
diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
index f4f804a..8480544 100644
--- a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
@@ -21,17 +21,31 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ApfCapabilitiesTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getContext();
+    }
+
     @Test
     public void testConstructAndParcel() {
         final ApfCapabilities caps = new ApfCapabilities(123, 456, 789);
@@ -59,4 +73,27 @@
         caps = new ApfCapabilities(4 /* apfVersionSupported */, 5, 6);
         assertTrue(caps.hasDataAccess());
     }
+
+    @Test
+    public void testGetApfDrop8023Frames() {
+        // Get com.android.internal.R.bool.config_apfDrop802_3Frames. The test cannot directly
+        // use R.bool.config_apfDrop802_3Frames because that is not a stable resource ID.
+        final int resId = mContext.getResources().getIdentifier("config_apfDrop802_3Frames",
+                "bool", "android");
+        final boolean shouldDrop8023Frames = mContext.getResources().getBoolean(resId);
+        final boolean actual = ApfCapabilities.getApfDrop8023Frames();
+        assertEquals(shouldDrop8023Frames, actual);
+    }
+
+    @Test
+    public void testGetApfEtherTypeBlackList() {
+        // Get com.android.internal.R.array.config_apfEthTypeBlackList. The test cannot directly
+        // use R.array.config_apfEthTypeBlackList because that is not a stable resource ID.
+        final int resId = mContext.getResources().getIdentifier("config_apfEthTypeBlackList",
+                "array", "android");
+        final int[] blacklistedEtherTypeArray = mContext.getResources().getIntArray(resId);
+        final int[] actual = ApfCapabilities.getApfEtherTypeBlackList();
+        assertNotNull(actual);
+        assertTrue(Arrays.equals(blacklistedEtherTypeArray, actual));
+    }
 }
diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
new file mode 100644
index 0000000..7b22e45
--- /dev/null
+++ b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
@@ -0,0 +1,209 @@
+/*
+ * 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.net.netstats
+
+import android.net.NetworkStats
+import android.net.NetworkStats.DEFAULT_NETWORK_NO
+import android.net.NetworkStats.DEFAULT_NETWORK_YES
+import android.net.NetworkStats.Entry
+import android.net.NetworkStats.IFACE_VT
+import android.net.NetworkStats.METERED_NO
+import android.net.NetworkStats.METERED_YES
+import android.net.NetworkStats.ROAMING_NO
+import android.net.NetworkStats.ROAMING_YES
+import android.net.NetworkStats.SET_DEFAULT
+import android.net.NetworkStats.SET_FOREGROUND
+import android.net.NetworkStats.TAG_NONE
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertNetworkStatsEquals
+import com.android.testutils.assertParcelingIsLossless
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+
+@RunWith(JUnit4::class)
+@SmallTest
+class NetworkStatsApiTest {
+    @Rule
+    @JvmField
+    val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q)
+
+    private val testStatsEmpty = NetworkStats(0L, 0)
+
+    // Note that these variables need to be initialized outside of constructor, initialize
+    // here with methods that don't exist in Q devices will result in crash.
+
+    // stats1 and stats2 will have some entries with common keys, which are expected to
+    // be merged if performing add on these 2 stats.
+    private lateinit var testStats1: NetworkStats
+    private lateinit var testStats2: NetworkStats
+
+    // This is a result of adding stats1 and stats2, while the merging of common key items is
+    // subject to test later, this should not be initialized with for a loop to add stats1
+    // and stats2 above.
+    private lateinit var testStats3: NetworkStats
+
+    companion object {
+        private const val TEST_IFACE = "test0"
+        private const val TEST_UID1 = 1001
+        private const val TEST_UID2 = 1002
+    }
+
+    @Before
+    fun setUp() {
+        testStats1 = NetworkStats(0L, 0)
+                // Entries which only appear in set1.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4))
+                // Entries which are common for set1 and set2.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0))
+                .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8))
+                .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0))
+        assertEquals(8, testStats1.size())
+
+        testStats2 = NetworkStats(0L, 0)
+                // Entries which are common for set1 and set2.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+                .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7))
+                .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0))
+                // Entry which only appears in set2.
+                .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+        assertEquals(5, testStats2.size())
+
+        testStats3 = NetworkStats(0L, 9)
+                // Entries which are unique either in stats1 or stats2.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+                .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+                // Entries which are common for stats1 and stats2 are being merged.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49))
+                .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15))
+                .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0))
+        assertEquals(9, testStats3.size())
+    }
+
+    @Test
+    fun testAddEntry() {
+        val expectedEntriesInStats2 = arrayOf(
+                Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1),
+                Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45),
+                Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7),
+                Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0),
+                Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+
+        // While testStats* are already initialized with addEntry, verify content added
+        // matches expectation.
+        for (i in expectedEntriesInStats2.indices) {
+            val entry = testStats2.getValues(i, null)
+            assertEquals(expectedEntriesInStats2[i], entry)
+        }
+
+        // Verify entry updated with addEntry.
+        val stats = testStats2.addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12, -5, 7, 0, 9))
+        assertEquals(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16, -2, 9, 1, 9),
+                stats.getValues(3, null))
+    }
+
+    @Test
+    fun testAdd() {
+        var stats = NetworkStats(0L, 0)
+        assertNetworkStatsEquals(testStatsEmpty, stats)
+        stats = stats.add(testStats2)
+        assertNetworkStatsEquals(testStats2, stats)
+        stats = stats.add(testStats1)
+        // EMPTY + STATS2 + STATS1 = STATS3
+        assertNetworkStatsEquals(testStats3, stats)
+    }
+
+    @Test
+    fun testParcelUnparcel() {
+        assertParcelingIsLossless(testStatsEmpty)
+        assertParcelingIsLossless(testStats1)
+        assertParcelingIsLossless(testStats2)
+        assertFieldCountEquals(15, NetworkStats::class.java)
+    }
+
+    @Test
+    fun testDescribeContents() {
+        assertEquals(0, testStatsEmpty.describeContents())
+        assertEquals(0, testStats1.describeContents())
+        assertEquals(0, testStats2.describeContents())
+        assertEquals(0, testStats3.describeContents())
+    }
+
+    @Test
+    fun testSubtract() {
+        // STATS3 - STATS2 = STATS1
+        assertNetworkStatsEquals(testStats1, testStats3.subtract(testStats2))
+        // STATS3 - STATS1 = STATS2
+        assertNetworkStatsEquals(testStats2, testStats3.subtract(testStats1))
+    }
+
+    @Test
+    fun testMethodsDontModifyReceiver() {
+        listOf(testStatsEmpty, testStats1, testStats2, testStats3).forEach {
+            val origStats = it.clone()
+            it.addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+            it.add(testStats3)
+            it.subtract(testStats1)
+            assertNetworkStatsEquals(origStats, it)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
index 9c7cfb0..aaf97f3 100644
--- a/tests/net/common/java/android/net/util/SocketUtilsTest.kt
+++ b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package android.net.util;
+package android.net.util
 
+import android.os.Build
 import android.system.NetlinkSocketAddress
 import android.system.Os
 import android.system.OsConstants.AF_INET
@@ -26,18 +27,26 @@
 import android.system.PacketSocketAddress
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 private const val TEST_INDEX = 123
 private const val TEST_PORT = 555
+private const val FF_BYTE = 0xff.toByte()
+
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class SocketUtilsTest {
+    @Rule @JvmField
+    val ignoreRule = DevSdkIgnoreRule()
+
     @Test
     fun testMakeNetlinkSocketAddress() {
         val nlAddress = SocketUtils.makeNetlinkSocketAddress(TEST_PORT, RTMGRP_NEIGH)
@@ -50,16 +59,21 @@
     }
 
     @Test
-    fun testMakePacketSocketAddress() {
+    fun testMakePacketSocketAddress_Q() {
         val pkAddress = SocketUtils.makePacketSocketAddress(ETH_P_ALL, TEST_INDEX)
         assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress)
 
-        val ff = 0xff.toByte()
-        val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX,
-                byteArrayOf(ff, ff, ff, ff, ff, ff))
+        val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX, ByteArray(6) { FF_BYTE })
         assertTrue("Not PacketSocketAddress object", pkAddress2 is PacketSocketAddress)
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testMakePacketSocketAddress() {
+        val pkAddress = SocketUtils.makePacketSocketAddress(
+                ETH_P_ALL, TEST_INDEX, ByteArray(6) { FF_BYTE })
+        assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress)
+    }
+
     @Test
     fun testCloseSocket() {
         // Expect no exception happening with null object.
diff --git a/tests/net/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml
index 09c0e48..f5a4234 100644
--- a/tests/net/integration/AndroidManifest.xml
+++ b/tests/net/integration/AndroidManifest.xml
@@ -16,50 +16,55 @@
  * limitations under the License.
  */
 -->
+
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.server.net.integrationtests">
+     package="com.android.server.net.integrationtests">
 
     <!-- For ConnectivityService registerReceiverAsUser (receiving broadcasts) -->
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
     <!-- PermissionMonitor sets network permissions for each user -->
-    <uses-permission android:name="android.permission.MANAGE_USERS" />
+    <uses-permission android:name="android.permission.MANAGE_USERS"/>
     <!-- ConnectivityService sends notifications to BatteryStats -->
-    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
     <!-- Reading network status -->
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.NETWORK_FACTORY" />
-    <uses-permission android:name="android.permission.NETWORK_STACK" />
-    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
-    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.NETWORK_FACTORY"/>
+    <!-- Obtain LinkProperties callbacks with sensitive fields -->
+    <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
+    <uses-permission android:name="android.permission.NETWORK_STACK"/>
+    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY"/>
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
     <!-- Reading DeviceConfig flags -->
-    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
     <application android:debuggable="true">
-        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="android.test.runner"/>
 
         <!-- This manifest is merged with the base manifest of the real NetworkStack app.
-             Remove the NetworkStackService from the base (real) manifest, and replace with a test
-             service that responds to the same intent -->
+                         Remove the NetworkStackService from the base (real) manifest, and replace with a test
+                         service that responds to the same intent -->
         <service android:name=".TestNetworkStackService"
-                 android:process="com.android.server.net.integrationtests.testnetworkstack">
+             android:process="com.android.server.net.integrationtests.testnetworkstack"
+             android:exported="true">
             <intent-filter>
                 <action android:name="android.net.INetworkStackConnector.Test"/>
             </intent-filter>
         </service>
         <service android:name=".NetworkStackInstrumentationService"
-                 android:process="com.android.server.net.integrationtests.testnetworkstack">
+             android:process="com.android.server.net.integrationtests.testnetworkstack"
+             android:exported="true">
             <intent-filter>
                 <action android:name=".INetworkStackInstrumentation"/>
             </intent-filter>
         </service>
         <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
-                 android:process="com.android.server.net.integrationtests.testnetworkstack"
-                 android:permission="android.permission.BIND_JOB_SERVICE"/>
+             android:process="com.android.server.net.integrationtests.testnetworkstack"
+             android:permission="android.permission.BIND_JOB_SERVICE"/>
 
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.server.net.integrationtests"
-                     android:label="Frameworks Net Integration Tests" />
+         android:targetPackage="com.android.server.net.integrationtests"
+         android:label="Frameworks Net Integration Tests"/>
 
 </manifest>
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index c4801aa..bc069e1 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -28,10 +28,13 @@
 import android.net.INetworkPolicyManager
 import android.net.INetworkStatsService
 import android.net.LinkProperties
+import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
 import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
+import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
 import android.net.NetworkRequest
 import android.net.TestNetworkStackClient
+import android.net.Uri
 import android.net.metrics.IpConnectivityLog
 import android.os.ConditionVariable
 import android.os.IBinder
@@ -64,6 +67,8 @@
 import org.mockito.MockitoAnnotations
 import org.mockito.Spy
 import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNotNull
 import kotlin.test.assertTrue
 import kotlin.test.fail
 
@@ -110,6 +115,10 @@
         private val bindingCondition = ConditionVariable(false)
 
         private val realContext get() = InstrumentationRegistry.getInstrumentation().context
+        private val httpProbeUrl get() =
+            realContext.getResources().getString(R.string.config_captive_portal_http_url)
+        private val httpsProbeUrl get() =
+            realContext.getResources().getString(R.string.config_captive_portal_https_url)
 
         private class InstrumentationServiceConnection : ServiceConnection {
             override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
@@ -188,12 +197,8 @@
         val testCallback = TestableNetworkCallback()
 
         cm.registerNetworkCallback(request, testCallback)
-        nsInstrumentation.addHttpResponse(HttpResponse(
-                "http://test.android.com",
-                responseCode = 204, contentLength = 42, redirectUrl = null))
-        nsInstrumentation.addHttpResponse(HttpResponse(
-                "https://secure.test.android.com",
-                responseCode = 204, contentLength = 42, redirectUrl = null))
+        nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204))
+        nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
 
         val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
         networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
@@ -204,4 +209,52 @@
         testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS)
         assertEquals(2, nsInstrumentation.getRequestUrls().size)
     }
+
+    @Test
+    fun testCapportApi() {
+        val request = NetworkRequest.Builder()
+                .clearCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build()
+        val testCb = TestableNetworkCallback()
+        val apiUrl = "https://capport.android.com"
+
+        cm.registerNetworkCallback(request, testCb)
+        nsInstrumentation.addHttpResponse(HttpResponse(
+                apiUrl,
+                """
+                    |{
+                    |  "captive": true,
+                    |  "user-portal-url": "https://login.capport.android.com",
+                    |  "venue-info-url": "https://venueinfo.capport.android.com"
+                    |}
+                """.trimMargin()))
+
+        // Tests will fail if a non-mocked query is received: mock the HTTPS probe, but not the
+        // HTTP probe as it should not be sent.
+        // Even if the HTTPS probe succeeds, a portal should be detected as the API takes precedence
+        // in that case.
+        nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
+
+        val lp = LinkProperties()
+        lp.captivePortalApiUrl = Uri.parse(apiUrl)
+        val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, context)
+        networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
+
+        na.addCapability(NET_CAPABILITY_INTERNET)
+        na.connect()
+
+        testCb.expectAvailableCallbacks(na.network, validated = false, tmt = TEST_TIMEOUT_MS)
+
+        val capportData = testCb.expectLinkPropertiesThat(na, TEST_TIMEOUT_MS) {
+            it.captivePortalData != null
+        }.lp.captivePortalData
+        assertNotNull(capportData)
+        assertTrue(capportData.isCaptive)
+        assertEquals(Uri.parse("https://login.capport.android.com"), capportData.userPortalUrl)
+        assertEquals(Uri.parse("https://venueinfo.capport.android.com"), capportData.venueInfoUrl)
+
+        val nc = testCb.expectCapabilitiesWith(NET_CAPABILITY_CAPTIVE_PORTAL, na, TEST_TIMEOUT_MS)
+        assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED))
+    }
 }
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
index 45073d8..e206313 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
@@ -22,16 +22,21 @@
 data class HttpResponse(
     val requestUrl: String,
     val responseCode: Int,
-    val contentLength: Long,
-    val redirectUrl: String?
+    val content: String = "",
+    val redirectUrl: String? = null
 ) : Parcelable {
-    constructor(p: Parcel): this(p.readString(), p.readInt(), p.readLong(), p.readString())
+    constructor(p: Parcel): this(p.readString(), p.readInt(), p.readString(), p.readString())
+    constructor(requestUrl: String, contentBody: String): this(
+            requestUrl,
+            responseCode = 200,
+            content = contentBody,
+            redirectUrl = null)
 
     override fun writeToParcel(dest: Parcel, flags: Int) {
         with(dest) {
             writeString(requestUrl)
             writeInt(responseCode)
-            writeLong(contentLength)
+            writeString(content)
             writeString(redirectUrl)
         }
     }
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
index 4827d29..e807952 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
@@ -65,6 +65,9 @@
          *
          * <p>For any subsequent HTTP/HTTPS query, the first response with a matching URL will be
          * used to mock the query response.
+         *
+         * <p>All requests that are expected to be sent must have a mock response: if an unexpected
+         * request is seen, the test will fail.
          */
         override fun addHttpResponse(response: HttpResponse) {
             httpResponses.getValue(response.requestUrl).add(response)
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 23caf49..a44ad1e0 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
@@ -24,7 +24,6 @@
 import android.net.metrics.IpConnectivityLog
 import android.net.util.SharedLog
 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
@@ -34,9 +33,11 @@
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
+import java.io.ByteArrayInputStream
 import java.net.HttpURLConnection
 import java.net.URL
 import java.net.URLConnection
+import java.nio.charset.StandardCharsets
 
 private const val TEST_NETID = 42
 
@@ -53,7 +54,7 @@
         doReturn(mock(IBinder::class.java)).`when`(it).getSystemService(Context.NETD_SERVICE)
     }
 
-    private class TestPermissionChecker : NetworkStackConnector.PermissionChecker() {
+    private class TestPermissionChecker : NetworkStackService.PermissionChecker() {
         override fun enforceNetworkStackCallingPermission() = Unit
     }
 
@@ -63,8 +64,8 @@
         override fun sendNetworkConditionsBroadcast(context: Context, broadcast: Intent) = Unit
     }
 
-    private inner class TestNetworkStackConnector(context: Context) :
-            NetworkStackConnector(context, TestPermissionChecker()) {
+    private inner class TestNetworkStackConnector(context: Context) : NetworkStackConnector(
+            context, TestPermissionChecker(), NetworkStackService.Dependencies()) {
 
         private val network = Network(TEST_NETID)
         private val privateDnsBypassNetwork = TestNetwork(TEST_NETID)
@@ -72,11 +73,13 @@
         private inner class TestNetwork(netId: Int) : Network(netId) {
             override fun openConnection(url: URL): URLConnection {
                 val response = InstrumentationConnector.processRequest(url)
+                val responseBytes = response.content.toByteArray(StandardCharsets.UTF_8)
 
                 val connection = mock(HttpURLConnection::class.java)
                 doReturn(response.responseCode).`when`(connection).responseCode
-                doReturn(response.contentLength).`when`(connection).contentLengthLong
+                doReturn(responseBytes.size.toLong()).`when`(connection).contentLengthLong
                 doReturn(response.redirectUrl).`when`(connection).getHeaderField("location")
+                doReturn(ByteArrayInputStream(responseBytes)).`when`(connection).inputStream
                 return connection
             }
         }
@@ -91,7 +94,6 @@
                     mock(IpConnectivityLog::class.java), mock(SharedLog::class.java),
                     mock(NetworkStackService.NetworkStackServiceManager::class.java),
                     NetworkMonitorDeps(privateDnsBypassNetwork),
-                    mock(DataStallStatsUtils::class.java),
                     mock(TcpSocketTracker::class.java))
             cb.onNetworkMonitorCreated(NetworkMonitorConnector(nm, TestPermissionChecker()))
         }
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index a35fb40..0ffafd45 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -92,6 +92,9 @@
                 break;
             case TRANSPORT_VPN:
                 mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
+                // VPNs deduce the SUSPENDED capability from their underlying networks and there
+                // is no public API to let VPN services set it.
+                mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
                 mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
                 break;
             default:
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 8eb5cfa..1d6c107 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -304,12 +304,12 @@
     }
 
     @Test
-    public void testConnectivityDiagnosticsCallbackOnConnectivityReport() {
-        mBinder.onConnectivityReport(createSampleConnectivityReport());
+    public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() {
+        mBinder.onConnectivityReportAvailable(createSampleConnectivityReport());
 
         // The callback will be invoked synchronously by inline executor. Immediately check the
         // latch without waiting.
-        verify(mCb).onConnectivityReport(eq(createSampleConnectivityReport()));
+        verify(mCb).onConnectivityReportAvailable(eq(createSampleConnectivityReport()));
     }
 
     @Test
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index d6bf334..d74a621 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -36,6 +36,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -213,7 +214,7 @@
 
         // register callback
         when(mService.requestNetwork(
-                any(), captor.capture(), anyInt(), any(), anyInt(), any()))
+                any(), captor.capture(), anyInt(), any(), anyInt(), any(), nullable(String.class)))
                 .thenReturn(request);
         manager.requestNetwork(request, callback, handler);
 
@@ -242,7 +243,7 @@
 
         // register callback
         when(mService.requestNetwork(
-                any(), captor.capture(), anyInt(), any(), anyInt(), any()))
+                any(), captor.capture(), anyInt(), any(), anyInt(), any(), nullable(String.class)))
                 .thenReturn(req1);
         manager.requestNetwork(req1, callback, handler);
 
@@ -261,7 +262,7 @@
 
         // callback can be registered again
         when(mService.requestNetwork(
-                any(), captor.capture(), anyInt(), any(), anyInt(), any()))
+                any(), captor.capture(), anyInt(), any(), anyInt(), any(), nullable(String.class)))
                 .thenReturn(req2);
         manager.requestNetwork(req2, callback, handler);
 
@@ -285,8 +286,8 @@
         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
 
         when(mCtx.getApplicationInfo()).thenReturn(info);
-        when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any()))
-                .thenReturn(request);
+        when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any(),
+                nullable(String.class))).thenReturn(request);
 
         Handler handler = new Handler(Looper.getMainLooper());
         manager.requestNetwork(request, callback, handler);
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
index 2273bc6..ada5494 100644
--- a/tests/net/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -40,7 +40,10 @@
 import java.security.KeyPairGenerator;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import javax.security.auth.x500.X500Principal;
@@ -106,6 +109,7 @@
         assertTrue(profile.isBypassable());
         assertTrue(profile.isMetered());
         assertEquals(TEST_MTU, profile.getMaxMtu());
+        assertEquals(Ikev2VpnProfile.DEFAULT_ALGORITHMS, profile.getAllowedAlgorithms());
     }
 
     @Test
@@ -160,6 +164,78 @@
     }
 
     @Test
+    public void testBuildWithAllowedAlgorithmsAead() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        builder.setAuthPsk(PSK_BYTES);
+
+        List<String> allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_CRYPT_AES_GCM);
+        builder.setAllowedAlgorithms(allowedAlgorithms);
+
+        final Ikev2VpnProfile profile = builder.build();
+        assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
+    }
+
+    @Test
+    public void testBuildWithAllowedAlgorithmsNormal() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        builder.setAuthPsk(PSK_BYTES);
+
+        List<String> allowedAlgorithms =
+                Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.CRYPT_AES_CBC);
+        builder.setAllowedAlgorithms(allowedAlgorithms);
+
+        final Ikev2VpnProfile profile = builder.build();
+        assertEquals(allowedAlgorithms, profile.getAllowedAlgorithms());
+    }
+
+    @Test
+    public void testSetAllowedAlgorithmsEmptyList() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+        try {
+            builder.setAllowedAlgorithms(new ArrayList<>());
+            fail("Expected exception due to no valid algorithm set");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testSetAllowedAlgorithmsInvalidList() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        List<String> allowedAlgorithms = new ArrayList<>();
+
+        try {
+            builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA256));
+            fail("Expected exception due to missing encryption");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.CRYPT_AES_CBC));
+            fail("Expected exception due to missing authentication");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testSetAllowedAlgorithmsInsecureAlgorithm() throws Exception {
+        final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+        List<String> allowedAlgorithms = new ArrayList<>();
+
+        try {
+            builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_MD5));
+            fail("Expected exception due to insecure algorithm");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            builder.setAllowedAlgorithms(Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA1));
+            fail("Expected exception due to insecure algorithm");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
     public void testBuildNoAuthMethodSet() throws Exception {
         final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
 
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java
index b81ca36..442ac56 100644
--- a/tests/net/java/android/net/IpMemoryStoreTest.java
+++ b/tests/net/java/android/net/IpMemoryStoreTest.java
@@ -35,6 +35,7 @@
 import android.net.ipmemorystore.NetworkAttributes;
 import android.net.ipmemorystore.NetworkAttributesParcelable;
 import android.net.ipmemorystore.Status;
+import android.net.networkstack.ModuleNetworkStackClient;
 import android.os.RemoteException;
 
 import androidx.test.filters.SmallTest;
@@ -67,7 +68,7 @@
     @Mock
     Context mMockContext;
     @Mock
-    NetworkStackClient mNetworkStackClient;
+    ModuleNetworkStackClient mModuleNetworkStackClient;
     @Mock
     IIpMemoryStore mMockService;
     @Mock
@@ -90,14 +91,14 @@
                 ((IIpMemoryStoreCallbacks) invocation.getArgument(0))
                         .onIpMemoryStoreFetched(mMockService);
                 return null;
-            }).when(mNetworkStackClient).fetchIpMemoryStore(any());
+            }).when(mModuleNetworkStackClient).fetchIpMemoryStore(any());
         } else {
-            doNothing().when(mNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture());
+            doNothing().when(mModuleNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture());
         }
         mStore = new IpMemoryStore(mMockContext) {
             @Override
-            protected NetworkStackClient getNetworkStackClient() {
-                return mNetworkStackClient;
+            protected ModuleNetworkStackClient getModuleNetworkStackClient(Context ctx) {
+                return mModuleNetworkStackClient;
             }
         };
     }
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index daf187d..91c9a2a 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -22,6 +22,8 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.net.util.MacAddressUtils;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -122,11 +124,11 @@
 
         for (MacAddress mac : multicastAddresses) {
             String msg = mac.toString() + " expected to be a multicast address";
-            assertTrue(msg, mac.isMulticastAddress());
+            assertTrue(msg, MacAddressUtils.isMulticastAddress(mac));
         }
         for (MacAddress mac : unicastAddresses) {
             String msg = mac.toString() + " expected not to be a multicast address";
-            assertFalse(msg, mac.isMulticastAddress());
+            assertFalse(msg, MacAddressUtils.isMulticastAddress(mac));
         }
     }
 
@@ -156,7 +158,7 @@
     public void testMacAddressConversions() {
         final int iterations = 10000;
         for (int i = 0; i < iterations; i++) {
-            MacAddress mac = MacAddress.createRandomUnicastAddress();
+            MacAddress mac = MacAddressUtils.createRandomUnicastAddress();
 
             String stringRepr = mac.toString();
             byte[] bytesRepr = mac.toByteArray();
@@ -188,7 +190,7 @@
         final String expectedLocalOui = "26:5f:78";
         final MacAddress base = MacAddress.fromString(anotherOui + ":0:0:0");
         for (int i = 0; i < iterations; i++) {
-            MacAddress mac = MacAddress.createRandomUnicastAddress(base, r);
+            MacAddress mac = MacAddressUtils.createRandomUnicastAddress(base, r);
             String stringRepr = mac.toString();
 
             assertTrue(stringRepr + " expected to be a locally assigned address",
@@ -199,7 +201,7 @@
         }
 
         for (int i = 0; i < iterations; i++) {
-            MacAddress mac = MacAddress.createRandomUnicastAddress();
+            MacAddress mac = MacAddressUtils.createRandomUnicastAddress();
             String stringRepr = mac.toString();
 
             assertTrue(stringRepr + " expected to be a locally assigned address",
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 33d77d2..98f705f 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -64,15 +64,15 @@
     @Test
     public void testFindIndex() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 5)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12)
-                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12);
 
         assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES,
@@ -94,21 +94,21 @@
     @Test
     public void testFindIndexHinted() {
         final NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10)
-                .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
-                .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12);
 
         // verify that we correctly find across regardless of hinting
@@ -143,27 +143,27 @@
         assertEquals(0, stats.size());
         assertEquals(4, stats.internalSize());
 
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                 DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                 DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
 
         assertEquals(4, stats.size());
         assertEquals(4, stats.internalSize());
 
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                 DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                 DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
 
         assertEquals(9, stats.size());
@@ -193,8 +193,8 @@
     public void testCombineExisting() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 10);
 
-        stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
-        stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
+        stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
+        stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
         stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L,
                 -128L, -1L, -1);
 
@@ -215,12 +215,12 @@
     @Test
     public void testSubtractIdenticalData() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats result = after.subtract(before);
 
@@ -234,12 +234,12 @@
     @Test
     public void testSubtractIdenticalRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
 
         final NetworkStats result = after.subtract(before);
 
@@ -253,13 +253,13 @@
     @Test
     public void testSubtractNewRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
-                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
+                .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
 
         final NetworkStats result = after.subtract(before);
 
@@ -275,11 +275,11 @@
     @Test
     public void testSubtractMissingRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
-                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
+                .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
+                .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
 
         final NetworkStats after = new NetworkStats(TEST_START, 1)
-                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
+                .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
 
         final NetworkStats result = after.subtract(before);
 
@@ -293,40 +293,40 @@
     @Test
     public void testTotalBytes() throws Exception {
         final NetworkStats iface = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
         assertEquals(384L, iface.getTotalBytes());
 
         final NetworkStats uidSet = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidSet.getTotalBytes());
 
         final NetworkStats uidTag = new NetworkStats(TEST_START, 6)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
         assertEquals(64L, uidTag.getTotalBytes());
 
         final NetworkStats uidMetered = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidMetered.getTotalBytes());
 
         final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidRoaming.getTotalBytes());
     }
@@ -343,11 +343,11 @@
     @Test
     public void testGroupedByIfaceAll() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
-                .addEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
+                .insertEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L);
         final NetworkStats grouped = uidStats.groupedByIface();
 
@@ -361,19 +361,19 @@
     @Test
     public void testGroupedByIface() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L);
 
         final NetworkStats grouped = uidStats.groupedByIface();
@@ -390,19 +390,19 @@
     @Test
     public void testAddAllValues() {
         final NetworkStats first = new NetworkStats(TEST_START, 5)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
 
         final NetworkStats second = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
 
         first.combineAllValues(second);
@@ -421,19 +421,19 @@
     @Test
     public void testGetTotal() {
         final NetworkStats stats = new NetworkStats(TEST_START, 7)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
 
         assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
@@ -459,7 +459,7 @@
         assertEquals(0, after.size());
 
         // Test 1 item stats.
-        before.addEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
+        before.insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
         after = before.clone();
         after.removeUids(new int[0]);
         assertEquals(1, after.size());
@@ -469,12 +469,12 @@
         assertEquals(0, after.size());
 
         // Append remaining test items.
-        before.addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
+        before.insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
         assertEquals(7, before.size());
 
         // Test remove with empty uid list.
@@ -503,14 +503,61 @@
     }
 
     @Test
+    public void testRemoveEmptyEntries() throws Exception {
+        // Test empty stats.
+        final NetworkStats statsEmpty = new NetworkStats(TEST_START, 3);
+        assertEquals(0, statsEmpty.removeEmptyEntries().size());
+
+        // Test stats with non-zero entry.
+        final NetworkStats statsNonZero = new NetworkStats(TEST_START, 1)
+                .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+        assertEquals(1, statsNonZero.size());
+        final NetworkStats expectedNonZero = statsNonZero.removeEmptyEntries();
+        assertEquals(1, expectedNonZero.size());
+        assertValues(expectedNonZero, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+
+        // Test stats with empty entry.
+        final NetworkStats statsZero = new NetworkStats(TEST_START, 1)
+                .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+        assertEquals(1, statsZero.size());
+        final NetworkStats expectedZero = statsZero.removeEmptyEntries();
+        assertEquals(1, statsZero.size()); // Assert immutable.
+        assertEquals(0, expectedZero.size());
+
+        // Test stats with multiple entries.
+        final NetworkStats statsMultiple = new NetworkStats(TEST_START, 0)
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 0L, 8L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 4L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 2L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 1L);
+        assertEquals(9, statsMultiple.size());
+        final NetworkStats expectedMultiple = statsMultiple.removeEmptyEntries();
+        assertEquals(9, statsMultiple.size()); // Assert immutable.
+        assertEquals(7, expectedMultiple.size());
+        assertValues(expectedMultiple.getTotalIncludingTags(null), 14L, 104L, 4L, 4L, 21L);
+
+        // Test stats with multiple empty entries.
+        assertEquals(statsMultiple.size(), statsMultiple.subtract(statsMultiple).size());
+        assertEquals(0, statsMultiple.subtract(statsMultiple).removeEmptyEntries().size());
+    }
+
+    @Test
     public void testClone() throws Exception {
         final NetworkStats original = new NetworkStats(TEST_START, 5)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
 
         // make clone and mutate original
         final NetworkStats clone = original.clone();
-        original.addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
+        original.insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
 
         assertEquals(3, original.size());
         assertEquals(2, clone.size());
@@ -523,8 +570,8 @@
     public void testAddWhenEmpty() throws Exception {
         final NetworkStats red = new NetworkStats(TEST_START, -1);
         final NetworkStats blue = new NetworkStats(TEST_START, 5)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
 
         // We're mostly checking that we don't crash
         red.combineAllValues(blue);
@@ -537,37 +584,37 @@
         final String underlyingIface = "wlan0";
         final int testTag1 = 8888;
         NetworkStats delta = new NetworkStats(TEST_START, 17)
-                .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
-                .addEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
-                .addEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
-                .addEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
                 // VPN package also uses some traffic through unprotected network.
-                .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
-                .addEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
                 // Tag entries
-                .addEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
-                .addEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
                 // Irrelevant entries
-                .addEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
                 // Underlying Iface entries
-                .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
-                .addEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                        DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
-                .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+                .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
                         299L /* smaller than sum(tun0) */, 0L)
-                .addEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                        DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+                .insertEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
 
         delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
         assertEquals(20, delta.size());
@@ -635,19 +682,19 @@
         final String underlyingIface = "wlan0";
         NetworkStats delta = new NetworkStats(TEST_START, 9)
                 // 2 different apps sent/receive data via tun0.
-                .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
-                .addEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
                 // VPN package resends data through the tunnel (with exaggerated overhead)
-                .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
                 // 1 app already has some traffic on the underlying interface, the other doesn't yet
-                .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
                 // Traffic through the underlying interface via the vpn app.
                 // This test should redistribute this data correctly.
-                .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
 
         delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
@@ -697,9 +744,9 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3);
 
         stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL);
         assertEquals(3, stats.size());
@@ -724,9 +771,9 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3);
 
         stats.filter(testUid, INTERFACES_ALL, TAG_ALL);
         assertEquals(2, stats.size());
@@ -755,10 +802,10 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 4)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3)
-                .addEntry(entry4);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3)
+                .insertEntry(entry4);
 
         stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL);
         assertEquals(3, stats.size());
@@ -778,8 +825,8 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(entry1)
-                .addEntry(entry2);
+                .insertEntry(entry1)
+                .insertEntry(entry2);
 
         stats.filter(UID_ALL, new String[] { }, TAG_ALL);
         assertEquals(0, stats.size());
@@ -802,9 +849,9 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3);
 
         stats.filter(UID_ALL, INTERFACES_ALL, testTag);
         assertEquals(2, stats.size());
@@ -831,10 +878,10 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 4)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3)
-                .addEntry(entry4);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3)
+                .insertEntry(entry4);
 
         stats.filterDebugEntries();
 
@@ -891,14 +938,14 @@
                 0 /* operations */);
 
         final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
-                .addEntry(appEntry)
-                .addEntry(xtRootUidEntry)
-                .addEntry(otherEntry);
+                .insertEntry(appEntry)
+                .insertEntry(xtRootUidEntry)
+                .insertEntry(otherEntry);
 
         final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
-                .addEntry(appEntry)
-                .addEntry(ebpfRootUidEntry)
-                .addEntry(otherEntry);
+                .insertEntry(appEntry)
+                .insertEntry(ebpfRootUidEntry)
+                .insertEntry(otherEntry);
 
         statsXt.apply464xlatAdjustments(stackedIface, false);
         statsEbpf.apply464xlatAdjustments(stackedIface, true);
@@ -945,8 +992,8 @@
                 0 /* operations */);
 
         NetworkStats stats = new NetworkStats(TEST_START, 2)
-                .addEntry(firstEntry)
-                .addEntry(secondEntry);
+                .insertEntry(firstEntry)
+                .insertEntry(secondEntry);
 
         // Empty map: no adjustment
         stats.apply464xlatAdjustments(new ArrayMap<>(), false);
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
new file mode 100644
index 0000000..5dd0fda
--- /dev/null
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.net
+
+import android.content.Context
+import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.NetworkIdentity.SUBTYPE_COMBINED
+import android.net.NetworkIdentity.buildNetworkIdentity
+import android.net.NetworkStats.DEFAULT_NETWORK_ALL
+import android.net.NetworkStats.METERED_ALL
+import android.net.NetworkStats.ROAMING_ALL
+import android.net.NetworkTemplate.MATCH_MOBILE
+import android.net.NetworkTemplate.MATCH_WIFI
+import android.net.NetworkTemplate.NETWORK_TYPE_ALL
+import android.net.NetworkTemplate.buildTemplateMobileWithRatType
+import android.telephony.TelephonyManager
+import com.android.testutils.assertParcelSane
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+import kotlin.test.assertFalse
+import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+
+private const val TEST_IMSI1 = "imsi1"
+private const val TEST_IMSI2 = "imsi2"
+private const val TEST_SSID1 = "ssid1"
+
+@RunWith(JUnit4::class)
+class NetworkTemplateTest {
+    private val mockContext = mock(Context::class.java)
+
+    private fun buildMobileNetworkState(subscriberId: String): NetworkState =
+            buildNetworkState(TYPE_MOBILE, subscriberId = subscriberId)
+    private fun buildWifiNetworkState(ssid: String): NetworkState =
+            buildNetworkState(TYPE_WIFI, ssid = ssid)
+
+    private fun buildNetworkState(
+        type: Int,
+        subscriberId: String? = null,
+        ssid: String? = null
+    ): NetworkState {
+        val info = mock(NetworkInfo::class.java)
+        doReturn(type).`when`(info).type
+        doReturn(NetworkInfo.State.CONNECTED).`when`(info).state
+        val lp = LinkProperties()
+        val caps = NetworkCapabilities().apply {
+            setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
+            setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
+        }
+        return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
+    }
+
+    private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) =
+            assertTrue(matches(ident), "$this does not match $ident")
+
+    private fun NetworkTemplate.assertDoesNotMatch(ident: NetworkIdentity) =
+            assertFalse(matches(ident), "$this should match $ident")
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun testRatTypeGroupMatches() {
+        val stateMobile = buildMobileNetworkState(TEST_IMSI1)
+        // Build UMTS template that matches mobile identities with RAT in the same
+        // group with any IMSI. See {@link NetworkTemplate#getCollapsedRatType}.
+        val templateUmts = buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS)
+        // Build normal template that matches mobile identities with any RAT and IMSI.
+        val templateAll = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL)
+        // Build template with UNKNOWN RAT that matches mobile identities with RAT that
+        // cannot be determined.
+        val templateUnknown =
+                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN)
+
+        val identUmts = buildNetworkIdentity(
+                mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_UMTS)
+        val identHsdpa = buildNetworkIdentity(
+                mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_HSDPA)
+        val identLte = buildNetworkIdentity(
+                mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_LTE)
+        val identCombined = buildNetworkIdentity(
+                mockContext, stateMobile, false, SUBTYPE_COMBINED)
+        val identImsi2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2),
+                false, TelephonyManager.NETWORK_TYPE_UMTS)
+        val identWifi = buildNetworkIdentity(
+                mockContext, buildWifiNetworkState(TEST_SSID1), true, 0)
+
+        // Assert that identity with the same RAT matches.
+        templateUmts.assertMatches(identUmts)
+        templateAll.assertMatches(identUmts)
+        templateUnknown.assertDoesNotMatch(identUmts)
+        // Assert that identity with the RAT within the same group matches.
+        templateUmts.assertMatches(identHsdpa)
+        templateAll.assertMatches(identHsdpa)
+        templateUnknown.assertDoesNotMatch(identHsdpa)
+        // Assert that identity with the RAT out of the same group only matches template with
+        // NETWORK_TYPE_ALL.
+        templateUmts.assertDoesNotMatch(identLte)
+        templateAll.assertMatches(identLte)
+        templateUnknown.assertDoesNotMatch(identLte)
+        // Assert that identity with combined RAT only matches with template with NETWORK_TYPE_ALL
+        // and NETWORK_TYPE_UNKNOWN.
+        templateUmts.assertDoesNotMatch(identCombined)
+        templateAll.assertMatches(identCombined)
+        templateUnknown.assertMatches(identCombined)
+        // Assert that identity with different IMSI matches.
+        templateUmts.assertMatches(identImsi2)
+        templateAll.assertMatches(identImsi2)
+        templateUnknown.assertDoesNotMatch(identImsi2)
+        // Assert that wifi identity does not match.
+        templateUmts.assertDoesNotMatch(identWifi)
+        templateAll.assertDoesNotMatch(identWifi)
+        templateUnknown.assertDoesNotMatch(identWifi)
+    }
+
+    @Test
+    fun testParcelUnparcel() {
+        val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null, null, METERED_ALL,
+                ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE)
+        val templateWifi = NetworkTemplate(MATCH_WIFI, null, null, TEST_SSID1, METERED_ALL,
+                ROAMING_ALL, DEFAULT_NETWORK_ALL, 0)
+        assertParcelSane(templateMobile, 8)
+        assertParcelSane(templateWifi, 8)
+    }
+
+    // Verify NETWORK_TYPE_ALL does not conflict with TelephonyManager#NETWORK_TYPE_* constants.
+    @Test
+    fun testNetworkTypeAll() {
+        for (ratType in TelephonyManager.getAllNetworkTypes()) {
+            assertNotEquals(NETWORK_TYPE_ALL, ratType)
+        }
+    }
+}
diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
index e632aaf..cea8c57 100644
--- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
+++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
@@ -66,10 +66,10 @@
             fail("InvalidPacketException: " + e);
         }
 
-        assertEquals(InetAddress.getByAddress(testInfo.srcAddress), resultData.srcAddress);
-        assertEquals(InetAddress.getByAddress(testInfo.dstAddress), resultData.dstAddress);
-        assertEquals(testInfo.srcPort, resultData.srcPort);
-        assertEquals(testInfo.dstPort, resultData.dstPort);
+        assertEquals(InetAddress.getByAddress(testInfo.srcAddress), resultData.getSrcAddress());
+        assertEquals(InetAddress.getByAddress(testInfo.dstAddress), resultData.getDstAddress());
+        assertEquals(testInfo.srcPort, resultData.getSrcPort());
+        assertEquals(testInfo.dstPort, resultData.getDstPort());
         assertEquals(testInfo.seq, resultData.tcpSeq);
         assertEquals(testInfo.ack, resultData.tcpAck);
         assertEquals(testInfo.rcvWnd, resultData.tcpWnd);
diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java
index 8a4b533..e5daa71 100644
--- a/tests/net/java/com/android/internal/net/VpnProfileTest.java
+++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java
@@ -33,7 +33,9 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /** Unit tests for {@link VpnProfile}. */
 @SmallTest
@@ -41,6 +43,9 @@
 public class VpnProfileTest {
     private static final String DUMMY_PROFILE_KEY = "Test";
 
+    private static final int ENCODED_INDEX_AUTH_PARAMS_INLINE = 23;
+    private static final int ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS = 24;
+
     @Test
     public void testDefaults() throws Exception {
         final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
@@ -65,12 +70,13 @@
         assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty());
         assertFalse(p.isBypassable);
         assertFalse(p.isMetered);
-        assertEquals(1400, p.maxMtu);
+        assertEquals(1360, p.maxMtu);
         assertFalse(p.areAuthParamsInline);
+        assertFalse(p.isRestrictedToTestNetworks);
     }
 
     private VpnProfile getSampleIkev2Profile(String key) {
-        final VpnProfile p = new VpnProfile(key);
+        final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */);
 
         p.name = "foo";
         p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
@@ -116,7 +122,7 @@
 
     @Test
     public void testParcelUnparcel() {
-        assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22);
+        assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
     }
 
     @Test
@@ -159,14 +165,41 @@
         assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
     }
 
+    private String getEncodedDecodedIkev2ProfileMissingValues(int... missingIndices) {
+        // Sort to ensure when we remove, we can do it from greatest first.
+        Arrays.sort(missingIndices);
+
+        final String encoded = new String(getSampleIkev2Profile(DUMMY_PROFILE_KEY).encode());
+        final List<String> parts =
+                new ArrayList<>(Arrays.asList(encoded.split(VpnProfile.VALUE_DELIMITER)));
+
+        // Remove from back first to ensure indexing is consistent.
+        for (int i = missingIndices.length - 1; i >= 0; i--) {
+            parts.remove(missingIndices[i]);
+        }
+
+        return String.join(VpnProfile.VALUE_DELIMITER, parts.toArray(new String[0]));
+    }
+
     @Test
     public void testEncodeDecodeInvalidNumberOfValues() {
-        final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
-        final String encoded = new String(profile.encode());
-        final byte[] tooFewValues =
-                encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes();
+        final String tooFewValues =
+                getEncodedDecodedIkev2ProfileMissingValues(
+                        ENCODED_INDEX_AUTH_PARAMS_INLINE,
+                        ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
 
-        assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues));
+        assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes()));
+    }
+
+    @Test
+    public void testEncodeDecodeMissingIsRestrictedToTestNetworks() {
+        final String tooFewValues =
+                getEncodedDecodedIkev2ProfileMissingValues(
+                        ENCODED_INDEX_RESTRICTED_TO_TEST_NETWORKS /* missingIndices */);
+
+        // Verify decoding without isRestrictedToTestNetworks defaults to false
+        final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues.getBytes());
+        assertFalse(decoded.isRestrictedToTestNetworks);
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c1999db..ea4982e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -25,7 +25,6 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE;
 import static android.net.ConnectivityManager.NETID_UNSET;
@@ -76,6 +75,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.os.Process.INVALID_UID;
 import static android.system.OsConstants.IPPROTO_TCP;
 
 import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
@@ -145,6 +145,7 @@
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
+import android.net.DataStallReportParcelable;
 import android.net.IConnectivityDiagnosticsCallback;
 import android.net.IDnsResolver;
 import android.net.IIpConnectivityMetrics;
@@ -171,10 +172,12 @@
 import android.net.NetworkStack;
 import android.net.NetworkStackClient;
 import android.net.NetworkState;
+import android.net.NetworkTestResultParcelable;
 import android.net.NetworkUtils;
 import android.net.ProxyInfo;
 import android.net.ResolverParamsParcel;
 import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
 import android.net.SocketKeepalive;
 import android.net.UidRange;
 import android.net.Uri;
@@ -196,7 +199,6 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
-import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -205,6 +207,7 @@
 import android.provider.Settings;
 import android.security.KeyStore;
 import android.system.Os;
+import android.telephony.TelephonyManager;
 import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -307,6 +310,8 @@
 
     private static final long TIMESTAMP = 1234L;
 
+    private static final int NET_ID = 110;
+
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
@@ -314,6 +319,8 @@
     private static final String TEST_PACKAGE_NAME = "com.android.test.package";
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
+    private static final String INTERFACE_NAME = "interface";
+
     private MockContext mServiceContext;
     private HandlerThread mCsHandlerThread;
     private ConnectivityService mService;
@@ -345,6 +352,7 @@
     @Mock IBinder mIBinder;
     @Mock LocationManager mLocationManager;
     @Mock AppOpsManager mAppOpsManager;
+    @Mock TelephonyManager mTelephonyManager;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -427,11 +435,11 @@
         public Object getSystemService(String name) {
             if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
             if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
-            if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
             if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
             if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
             if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
+            if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
             return super.getSystemService(name);
         }
 
@@ -574,14 +582,6 @@
     }
 
     private class TestNetworkAgentWrapper extends NetworkAgentWrapper {
-        private static final int VALIDATION_RESULT_BASE = NETWORK_VALIDATION_PROBE_DNS
-                | NETWORK_VALIDATION_PROBE_HTTP
-                | NETWORK_VALIDATION_PROBE_HTTPS;
-        private static final int VALIDATION_RESULT_VALID = VALIDATION_RESULT_BASE
-                | NETWORK_VALIDATION_RESULT_VALID;
-        private static final int VALIDATION_RESULT_PARTIAL = VALIDATION_RESULT_BASE
-                | NETWORK_VALIDATION_PROBE_FALLBACK
-                | NETWORK_VALIDATION_RESULT_PARTIAL;
         private static final int VALIDATION_RESULT_INVALID = 0;
 
         private static final long DATA_STALL_TIMESTAMP = 10L;
@@ -589,12 +589,10 @@
 
         private INetworkMonitor mNetworkMonitor;
         private INetworkMonitorCallbacks mNmCallbacks;
-        private int mNmValidationResult = VALIDATION_RESULT_BASE;
+        private int mNmValidationResult = VALIDATION_RESULT_INVALID;
         private int mProbesCompleted;
         private int mProbesSucceeded;
         private String mNmValidationRedirectUrl = null;
-        private PersistableBundle mValidationExtras = PersistableBundle.EMPTY;
-        private PersistableBundle mDataStallExtras = PersistableBundle.EMPTY;
         private boolean mNmProvNotificationRequested = false;
 
         private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
@@ -662,8 +660,13 @@
             }
 
             mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded);
-            mNmCallbacks.notifyNetworkTestedWithExtras(
-                    mNmValidationResult, mNmValidationRedirectUrl, TIMESTAMP, mValidationExtras);
+            final NetworkTestResultParcelable p = new NetworkTestResultParcelable();
+            p.result = mNmValidationResult;
+            p.probesAttempted = mProbesCompleted;
+            p.probesSucceeded = mProbesSucceeded;
+            p.redirectUrl = mNmValidationRedirectUrl;
+            p.timestampMillis = TIMESTAMP;
+            mNmCallbacks.notifyNetworkTestedWithExtras(p);
 
             if (mNmValidationRedirectUrl != null) {
                 mNmCallbacks.showProvisioningNotification(
@@ -745,9 +748,9 @@
         }
 
         void setNetworkValid(boolean isStrictMode) {
-            mNmValidationResult = VALIDATION_RESULT_VALID;
+            mNmValidationResult = NETWORK_VALIDATION_RESULT_VALID;
             mNmValidationRedirectUrl = null;
-            int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTP;
+            int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS;
             if (isStrictMode) {
                 probesSucceeded |= NETWORK_VALIDATION_PROBE_PRIVDNS;
             }
@@ -759,8 +762,9 @@
         void setNetworkInvalid(boolean isStrictMode) {
             mNmValidationResult = VALIDATION_RESULT_INVALID;
             mNmValidationRedirectUrl = null;
-            int probesCompleted = VALIDATION_RESULT_BASE;
-            int probesSucceeded = VALIDATION_RESULT_INVALID;
+            int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
+                    | NETWORK_VALIDATION_PROBE_HTTP;
+            int probesSucceeded = 0;
             // If the isStrictMode is true, it means the network is invalid when NetworkMonitor
             // tried to validate the private DNS but failed.
             if (isStrictMode) {
@@ -776,7 +780,7 @@
             mNmValidationRedirectUrl = redirectUrl;
             // Suppose the portal is found when NetworkMonitor probes NETWORK_VALIDATION_PROBE_HTTP
             // in the beginning, so the NETWORK_VALIDATION_PROBE_HTTPS hasn't probed yet.
-            int probesCompleted = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+            int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP;
             int probesSucceeded = VALIDATION_RESULT_INVALID;
             if (isStrictMode) {
                 probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
@@ -785,18 +789,20 @@
         }
 
         void setNetworkPartial() {
-            mNmValidationResult = VALIDATION_RESULT_PARTIAL;
+            mNmValidationResult = NETWORK_VALIDATION_RESULT_PARTIAL;
             mNmValidationRedirectUrl = null;
-            int probesCompleted = VALIDATION_RESULT_BASE;
-            int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+            int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
+                    | NETWORK_VALIDATION_PROBE_FALLBACK;
+            int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK;
             setProbesStatus(probesCompleted, probesSucceeded);
         }
 
         void setNetworkPartialValid(boolean isStrictMode) {
             setNetworkPartial();
-            mNmValidationResult |= VALIDATION_RESULT_VALID;
-            int probesCompleted = VALIDATION_RESULT_BASE;
-            int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+            mNmValidationResult |= NETWORK_VALIDATION_RESULT_VALID;
+            int probesCompleted = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
+                    | NETWORK_VALIDATION_PROBE_HTTP;
+            int probesSucceeded = NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP;
             // Suppose the partial network cannot pass the private DNS validation as well, so only
             // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded.
             if (isStrictMode) {
@@ -832,8 +838,10 @@
         }
 
         void notifyDataStallSuspected() throws Exception {
-            mNmCallbacks.notifyDataStallSuspected(
-                    DATA_STALL_TIMESTAMP, DATA_STALL_DETECTION_METHOD, mDataStallExtras);
+            final DataStallReportParcelable p = new DataStallReportParcelable();
+            p.detectionMethod = DATA_STALL_DETECTION_METHOD;
+            p.timestampMillis = DATA_STALL_TIMESTAMP;
+            mNmCallbacks.notifyDataStallSuspected(p);
         }
     }
 
@@ -1016,6 +1024,7 @@
         private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
 
         private VpnInfo mVpnInfo;
+        private Network[] mUnderlyingNetworks;
 
         public MockVpn(int userId) {
             super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
@@ -1105,9 +1114,21 @@
             return super.getVpnInfo();
         }
 
-        private void setVpnInfo(VpnInfo vpnInfo) {
+        private synchronized void setVpnInfo(VpnInfo vpnInfo) {
             mVpnInfo = vpnInfo;
         }
+
+        @Override
+        public synchronized Network[] getUnderlyingNetworks() {
+            if (mUnderlyingNetworks != null) return mUnderlyingNetworks;
+
+            return super.getUnderlyingNetworks();
+        }
+
+        /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */
+        private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) {
+            mUnderlyingNetworks = underlyingNetworks;
+        }
     }
 
     private void mockVpn(int uid) {
@@ -1374,7 +1395,6 @@
             @NonNull final Predicate<Intent> filter) {
         final ConditionVariable cv = new ConditionVariable();
         final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION);
-        intentFilter.addAction(CONNECTIVITY_ACTION_SUPL);
         final BroadcastReceiver receiver = new BroadcastReceiver() {
                     private int remaining = count;
                     public void onReceive(Context context, Intent intent) {
@@ -1422,56 +1442,28 @@
         final NetworkRequest legacyRequest = new NetworkRequest(legacyCaps, TYPE_MOBILE_SUPL,
                 ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST);
 
-        // Send request and check that the legacy broadcast for SUPL is sent correctly.
+        // File request, withdraw it and make sure no broadcast is sent
+        final ConditionVariable cv2 = registerConnectivityBroadcast(1);
         final TestNetworkCallback callback = new TestNetworkCallback();
-        final ConditionVariable cv2 = registerConnectivityBroadcastThat(1,
-                intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL);
         mCm.requestNetwork(legacyRequest, callback);
         callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
-        waitFor(cv2);
-
-        // File another request, withdraw it and make sure no broadcast is sent
-        final ConditionVariable cv3 = registerConnectivityBroadcast(1);
-        final TestNetworkCallback callback2 = new TestNetworkCallback();
-        mCm.requestNetwork(legacyRequest, callback2);
-        callback2.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
-        mCm.unregisterNetworkCallback(callback2);
-        assertFalse(cv3.block(800)); // 800ms long enough to at least flake if this is sent
+        mCm.unregisterNetworkCallback(callback);
+        assertFalse(cv2.block(800)); // 800ms long enough to at least flake if this is sent
         // As the broadcast did not fire, the receiver was not unregistered. Do this now.
         mServiceContext.clearRegisteredReceivers();
 
-        // Withdraw the request and check that the broadcast for disconnection is sent.
-        final ConditionVariable cv4 = registerConnectivityBroadcastThat(1, intent ->
-                !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected()
-                        && intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL);
-        mCm.unregisterNetworkCallback(callback);
-        waitFor(cv4);
-
-        // Re-file the request and expect the connected broadcast again
-        final ConditionVariable cv5 = registerConnectivityBroadcastThat(1,
-                intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL);
-        final TestNetworkCallback callback3 = new TestNetworkCallback();
-        mCm.requestNetwork(legacyRequest, callback3);
-        callback3.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
-        waitFor(cv5);
-
-        // Disconnect the network and expect two disconnected broadcasts, one for SUPL and one
-        // for mobile. Use a small hack to check that both have been sent, but the order is
-        // not contractual.
+        // Disconnect the network and expect mobile disconnected broadcast. Use a small hack to
+        // check that has been sent.
         final AtomicBoolean vanillaAction = new AtomicBoolean(false);
-        final AtomicBoolean suplAction = new AtomicBoolean(false);
-        final ConditionVariable cv6 = registerConnectivityBroadcastThat(2, intent -> {
+        final ConditionVariable cv3 = registerConnectivityBroadcastThat(1, intent -> {
             if (intent.getAction().equals(CONNECTIVITY_ACTION)) {
                 vanillaAction.set(true);
-            } else if (intent.getAction().equals(CONNECTIVITY_ACTION_SUPL)) {
-                suplAction.set(true);
             }
             return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected();
         });
         mCellNetworkAgent.disconnect();
-        waitFor(cv6);
+        waitFor(cv3);
         assertTrue(vanillaAction.get());
-        assertTrue(suplAction.get());
     }
 
     @Test
@@ -2425,7 +2417,7 @@
         assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
         assertTrue(testFactory.getMyStartRequested());
 
-        testFactory.unregister();
+        testFactory.terminate();
         if (networkCallback != null) mCm.unregisterNetworkCallback(networkCallback);
         handlerThread.quit();
     }
@@ -2451,6 +2443,38 @@
     }
 
     @Test
+    public void testNetworkFactoryUnregister() throws Exception {
+        final NetworkCapabilities filter = new NetworkCapabilities();
+        filter.clearAll();
+
+        final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
+        handlerThread.start();
+
+        // Checks that calling setScoreFilter on a NetworkFactory immediately before closing it
+        // does not crash.
+        for (int i = 0; i < 100; i++) {
+            final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+                    mServiceContext, "testFactory", filter);
+            // Register the factory and don't be surprised when the default request arrives.
+            testFactory.expectAddRequestsWithScores(0);
+            testFactory.register();
+            testFactory.waitForNetworkRequests(1);
+
+            testFactory.setScoreFilter(42);
+            testFactory.terminate();
+
+            if (i % 2 == 0) {
+                try {
+                    testFactory.register();
+                    fail("Re-registering terminated NetworkFactory should throw");
+                } catch (IllegalStateException expected) {
+                }
+            }
+        }
+        handlerThread.quit();
+    }
+
+    @Test
     public void testNoMutableNetworkRequests() throws Exception {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
         NetworkRequest request1 = new NetworkRequest.Builder()
@@ -2750,9 +2774,6 @@
 
         // Expect NET_CAPABILITY_VALIDATED onAvailable callback.
         validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
-        // Expect no notification to be shown when captive portal disappears by itself
-        verify(mNotificationManager, never()).notifyAsUser(
-                anyString(), eq(NotificationType.LOGGED_IN.eventId), any(), any());
 
         // Break network connectivity.
         // Expect NET_CAPABILITY_VALIDATED onLost callback.
@@ -2814,8 +2835,6 @@
         mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
         validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
         captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
-        verify(mNotificationManager, times(1)).notifyAsUser(anyString(),
-                eq(NotificationType.LOGGED_IN.eventId), any(), eq(UserHandle.ALL));
 
         mCm.unregisterNetworkCallback(validatedCallback);
         mCm.unregisterNetworkCallback(captivePortalCallback);
@@ -2899,7 +2918,7 @@
         class ConfidentialMatchAllNetworkSpecifier extends NetworkSpecifier implements
                 Parcelable {
             @Override
-            public boolean satisfiedBy(NetworkSpecifier other) {
+            public boolean canBeSatisfiedBy(NetworkSpecifier other) {
                 return true;
             }
 
@@ -2927,7 +2946,7 @@
             }
 
             @Override
-            public boolean satisfiedBy(NetworkSpecifier other) {
+            public boolean canBeSatisfiedBy(NetworkSpecifier other) {
                 if (other instanceof LocalStringNetworkSpecifier) {
                     return TextUtils.equals(mString,
                             ((LocalStringNetworkSpecifier) other).mString);
@@ -3032,6 +3051,13 @@
         assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar);
     }
 
+    /**
+     * @return the context's attribution tag
+     */
+    private String getAttributionTag() {
+        return null;
+    }
+
     @Test
     public void testInvalidNetworkSpecifier() {
         assertThrows(IllegalArgumentException.class, () -> {
@@ -3044,11 +3070,15 @@
             networkCapabilities.addTransportType(TRANSPORT_WIFI)
                     .setNetworkSpecifier(new MatchAllNetworkSpecifier());
             mService.requestNetwork(networkCapabilities, null, 0, null,
-                    ConnectivityManager.TYPE_WIFI, mContext.getPackageName());
+                    ConnectivityManager.TYPE_WIFI, mContext.getPackageName(),
+                    getAttributionTag());
         });
 
         class NonParcelableSpecifier extends NetworkSpecifier {
-            public boolean satisfiedBy(NetworkSpecifier other) { return false; }
+            @Override
+            public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+                return false;
+            }
         };
         class ParcelableSpecifier extends NonParcelableSpecifier implements Parcelable {
             @Override public int describeContents() { return 0; }
@@ -3487,7 +3517,7 @@
         cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
         assertLength(1, mCm.getAllNetworks());
 
-        testFactory.unregister();
+        testFactory.terminate();
         mCm.unregisterNetworkCallback(cellNetworkCallback);
         handlerThread.quit();
     }
@@ -3728,7 +3758,7 @@
         mCm.requestNetwork(nr, networkCallback, timeoutMs);
 
         // pass timeout and validate that UNAVAILABLE is called
-        networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, null);
+        networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
 
         // create a network satisfying request - validate that request not triggered
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -3819,7 +3849,7 @@
             // Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
             testFactory.triggerUnfulfillable(requests.get(newRequestId));
 
-            networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, null);
+            networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
             testFactory.waitForRequests();
 
             // unregister network callback - a no-op (since already freed by the
@@ -3827,7 +3857,7 @@
             mCm.unregisterNetworkCallback(networkCallback);
         }
 
-        testFactory.unregister();
+        testFactory.terminate();
         handlerThread.quit();
     }
 
@@ -4888,6 +4918,29 @@
     }
 
     @Test
+    public void testDnsConfigurationTransTypesPushed() throws Exception {
+        // Clear any interactions that occur as a result of CS starting up.
+        reset(mMockDnsResolver);
+
+        final NetworkRequest request = new NetworkRequest.Builder()
+                .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(request, callback);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(false);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        verify(mMockDnsResolver, times(1)).createNetworkCache(
+                eq(mWiFiNetworkAgent.getNetwork().netId));
+        verify(mMockDnsResolver, times(2)).setResolverConfiguration(
+                mResolverParamsParcelCaptor.capture());
+        final ResolverParamsParcel resolverParams = mResolverParamsParcelCaptor.getValue();
+        assertContainsExactly(resolverParams.transportTypes, TRANSPORT_WIFI);
+        reset(mMockDnsResolver);
+    }
+
+    @Test
     public void testPrivateDnsNotification() throws Exception {
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
@@ -5342,8 +5395,6 @@
 
         // Even though the VPN is unvalidated, it becomes the default network for our app.
         callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
-        // TODO: this looks like a spurious callback.
-        callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
         callback.assertNoCallback();
 
         assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
@@ -5373,6 +5424,47 @@
     }
 
     @Test
+    public void testVpnStartsWithUnderlyingCaps() throws Exception {
+        final int uid = Process.myUid();
+
+        final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+                .removeCapability(NET_CAPABILITY_NOT_VPN)
+                .addTransportType(TRANSPORT_VPN)
+                .build();
+        mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+        vpnNetworkCallback.assertNoCallback();
+
+        // Connect cell. It will become the default network, and in the absence of setting
+        // underlying networks explicitly it will become the sole underlying network for the vpn.
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        mCellNetworkAgent.connect(true);
+
+        final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.connect();
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+                false /* isStrictMode */);
+
+        vpnNetworkCallback.expectAvailableCallbacks(vpnNetworkAgent.getNetwork(),
+                false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS);
+        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent.getNetwork(), TIMEOUT_MS,
+                nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED));
+
+        final NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+        assertTrue(nc.hasTransport(TRANSPORT_VPN));
+        assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        assertTrue(nc.hasCapability(NET_CAPABILITY_VALIDATED));
+        assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+        assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+    }
+
+    @Test
     public void testVpnSetUnderlyingNetworks() throws Exception {
         final int uid = Process.myUid();
 
@@ -5402,9 +5494,12 @@
         assertFalse(nc.hasTransport(TRANSPORT_WIFI));
         // For safety reasons a VPN without underlying networks is considered metered.
         assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+        // A VPN without underlying networks is not suspended.
+        assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
         // Connect cell and use it as an underlying network.
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mCellNetworkAgent.connect(true);
 
         mService.setUnderlyingNetworksForVpn(
@@ -5413,10 +5508,12 @@
         vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
-                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         mWiFiNetworkAgent.connect(true);
 
         mService.setUnderlyingNetworksForVpn(
@@ -5425,7 +5522,8 @@
         vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
-                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
         // Don't disconnect, but note the VPN is not using wifi any more.
         mService.setUnderlyingNetworksForVpn(
@@ -5434,16 +5532,36 @@
         vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
-                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
-        // Use Wifi but not cell. Note the VPN is now unmetered.
+        // Remove NOT_SUSPENDED from the only network and observe VPN is now suspended.
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+                (caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+        vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, vpnNetworkAgent);
+
+        // Add NOT_SUSPENDED again and observe VPN is no longer suspended.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+                (caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+        vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, vpnNetworkAgent);
+
+        // Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
         mService.setUnderlyingNetworksForVpn(
                 new Network[] { mWiFiNetworkAgent.getNetwork() });
 
         vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
-                && caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+                && caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
         // Use both again.
         mService.setUnderlyingNetworksForVpn(
@@ -5452,7 +5570,37 @@
         vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
                 (caps) -> caps.hasTransport(TRANSPORT_VPN)
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
-                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+
+        // Cell is suspended again. As WiFi is not, this should not cause a callback.
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        vpnNetworkCallback.assertNoCallback();
+
+        // Stop using WiFi. The VPN is suspended again.
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mCellNetworkAgent.getNetwork() });
+        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+                (caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && caps.hasTransport(TRANSPORT_CELLULAR)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+        // While the SUSPENDED callback should in theory be sent here, it is not. This is
+        // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
+        // been public and are deprecated and slated for removal, there is no sense in spending
+        // resources fixing this bug now.
+
+        // Use both again.
+        mService.setUnderlyingNetworksForVpn(
+                new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
+
+        vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+                (caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+        // As above, the RESUMED callback not being sent here is a bug, but not a bug that's
+        // worth anybody's time to fix.
 
         // Disconnect cell. Receive update without even removing the dead network from the
         // underlying networks – it's dead anyway. Not metered any more.
@@ -5926,6 +6074,9 @@
         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 String kOtherNat64PrefixString = "64:ff9b::";
+        final IpPrefix kOtherNat64Prefix = new IpPrefix(
+                InetAddress.getByName(kOtherNat64PrefixString), 96);
         final RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, myIpv6.getAddress(),
                                                      MOBILE_IFNAME);
         final RouteInfo ipv6Subnet = new RouteInfo(myIpv6, null, MOBILE_IFNAME);
@@ -6037,6 +6188,25 @@
             verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
                     TYPE_MOBILE);
         }
+        reset(mMockNetd);
+
+        // Change the NAT64 prefix without first removing it.
+        // Expect clatd to be stopped and started with the new prefix.
+        mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
+                kOtherNat64PrefixString, 96);
+        networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+                (lp) -> lp.getStackedLinks().size() == 0);
+        verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+        assertRoutesRemoved(cellNetId, stackedDefault);
+
+        verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString());
+        networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+                (lp) -> lp.getNat64Prefix().equals(kOtherNat64Prefix));
+        clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
+        networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+                (lp) -> lp.getStackedLinks().size() == 1);
+        assertRoutesAdded(cellNetId, stackedDefault);
+        reset(mMockNetd);
 
         // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
         // linkproperties are cleaned up.
@@ -6052,7 +6222,7 @@
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
         LinkProperties expected = new LinkProperties(cellLp);
-        expected.setNat64Prefix(kNat64Prefix);
+        expected.setNat64Prefix(kOtherNat64Prefix);
         assertEquals(expected, actualLpAfterIpv4);
         assertEquals(0, actualLpAfterIpv4.getStackedLinks().size());
         assertRoutesRemoved(cellNetId, stackedDefault);
@@ -6071,7 +6241,7 @@
 
         // Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
         mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
-                kNat64PrefixString, 96);
+                kOtherNat64PrefixString, 96);
         networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
                 (lp) -> lp.getNat64Prefix() == null);
 
@@ -6088,7 +6258,6 @@
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
 
-
         // Clat iface comes up. Expect stacked link to be added.
         clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
         networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
@@ -6115,6 +6284,178 @@
         mCm.unregisterNetworkCallback(networkCallback);
     }
 
+    private void expectNat64PrefixChange(TestableNetworkCallback callback,
+            TestNetworkAgentWrapper agent, IpPrefix prefix) {
+        callback.expectLinkPropertiesThat(agent, x -> Objects.equals(x.getNat64Prefix(), prefix));
+    }
+
+    @Test
+    public void testNat64PrefixMultipleSources() throws Exception {
+        final String iface = "wlan0";
+        final String pref64FromRaStr = "64:ff9b::";
+        final String pref64FromDnsStr = "2001:db8:64::";
+        final IpPrefix pref64FromRa = new IpPrefix(InetAddress.getByName(pref64FromRaStr), 96);
+        final IpPrefix pref64FromDns = new IpPrefix(InetAddress.getByName(pref64FromDnsStr), 96);
+        final IpPrefix newPref64FromRa = new IpPrefix("2001:db8:64:64:64:64::/96");
+
+        final NetworkRequest request = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(request, callback);
+
+        final LinkProperties baseLp = new LinkProperties();
+        baseLp.setInterfaceName(iface);
+        baseLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+        baseLp.addDnsServer(InetAddress.getByName("2001:4860:4860::6464"));
+
+        reset(mMockNetd, mMockDnsResolver);
+        InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver);
+
+        // If a network already has a NAT64 prefix on connect, clatd is started immediately and
+        // prefix discovery is never started.
+        LinkProperties lp = new LinkProperties(baseLp);
+        lp.setNat64Prefix(pref64FromRa);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
+        mCellNetworkAgent.connect(false);
+        final Network network = mCellNetworkAgent.getNetwork();
+        int netId = network.getNetId();
+        callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+        callback.assertNoCallback();
+        assertEquals(pref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
+
+        // If the RA prefix is withdrawn, clatd is stopped and prefix discovery is started.
+        lp.setNat64Prefix(null);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+        inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+        inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
+
+        // If the RA prefix appears while DNS discovery is in progress, discovery is stopped and
+        // clatd is started with the prefix from the RA.
+        lp.setNat64Prefix(pref64FromRa);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
+        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
+
+        // Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS
+        // discovery has succeeded.
+        lp.setNat64Prefix(null);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+        inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+        inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
+
+        mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
+                pref64FromDnsStr, 96);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+
+        // If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix
+        // discovery is not stopped, and there are no callbacks.
+        lp.setNat64Prefix(pref64FromDns);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        callback.assertNoCallback();
+        inOrder.verify(mMockNetd, never()).clatdStop(iface);
+        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+        // If the RA is later withdrawn, nothing happens again.
+        lp.setNat64Prefix(null);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        callback.assertNoCallback();
+        inOrder.verify(mMockNetd, never()).clatdStop(iface);
+        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+        // If the RA prefix changes, clatd is restarted and prefix discovery is stopped.
+        lp.setNat64Prefix(pref64FromRa);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa);
+        inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+
+        // Stopping prefix discovery results in a prefix removed notification.
+        mService.mNetdEventCallback.onNat64PrefixEvent(netId, false /* added */,
+                pref64FromDnsStr, 96);
+
+        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+
+        // If the RA prefix changes, clatd is restarted and prefix discovery is not started.
+        lp.setNat64Prefix(newPref64FromRa);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, newPref64FromRa);
+        inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+        inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, newPref64FromRa.toString());
+        inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+
+        // If the RA prefix changes to the same value, nothing happens.
+        lp.setNat64Prefix(newPref64FromRa);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        callback.assertNoCallback();
+        assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
+        inOrder.verify(mMockNetd, never()).clatdStop(iface);
+        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+        // The transition between no prefix and DNS prefix is tested in testStackedLinkProperties.
+
+        // If the same prefix is learned first by DNS and then by RA, and clat is later stopped,
+        // (e.g., because the network disconnects) setPrefix64(netid, "") is never called.
+        lp.setNat64Prefix(null);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, null);
+        inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
+        inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
+        mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */,
+                pref64FromDnsStr, 96);
+        expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns);
+        inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any());
+
+        lp.setNat64Prefix(pref64FromDns);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        callback.assertNoCallback();
+        inOrder.verify(mMockNetd, never()).clatdStop(iface);
+        inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+        inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+        // When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but
+        // before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that
+        // clat has been stopped, or the test will be flaky.
+        ConditionVariable cv = registerConnectivityBroadcast(1);
+        mCellNetworkAgent.disconnect();
+        callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+        waitFor(cv);
+
+        inOrder.verify(mMockNetd).clatdStop(iface);
+        inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
+        inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
+
+        mCm.unregisterNetworkCallback(callback);
+    }
+
     @Test
     public void testDataActivityTracking() throws Exception {
         final TestNetworkCallback networkCallback = new TestNetworkCallback();
@@ -6316,6 +6657,7 @@
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName("tun0");
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
         final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
@@ -6341,6 +6683,7 @@
     public void testLegacyVpnDoesNotResultInInterfaceFilteringRule() throws Exception {
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName("tun0");
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
@@ -6372,6 +6715,7 @@
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName("tun0");
         lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
         final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
@@ -6408,6 +6752,7 @@
         reset(mMockNetd);
         lp = new LinkProperties();
         lp.setInterfaceName("tun1");
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         vpnNetworkAgent.sendLinkProperties(lp);
         waitForIdle();
@@ -6420,6 +6765,7 @@
     public void testUidUpdateChangesInterfaceFilteringRule() throws Exception {
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName("tun0");
+        lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
         lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
         // The uid range needs to cover the test app so the network is visible to it.
         final UidRange vpnRange = UidRange.createForUser(VPN_USER);
@@ -6674,17 +7020,45 @@
         }
     }
 
+    private void assertRouteInfoParcelMatches(RouteInfo route, RouteInfoParcel parcel) {
+        assertEquals(route.getDestination().toString(), parcel.destination);
+        assertEquals(route.getInterface(), parcel.ifName);
+        assertEquals(route.getMtu(), parcel.mtu);
+
+        switch (route.getType()) {
+            case RouteInfo.RTN_UNICAST:
+                if (route.hasGateway()) {
+                    assertEquals(route.getGateway().getHostAddress(), parcel.nextHop);
+                } else {
+                    assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+                }
+                break;
+            case RouteInfo.RTN_UNREACHABLE:
+                assertEquals(INetd.NEXTHOP_UNREACHABLE, parcel.nextHop);
+                break;
+            case RouteInfo.RTN_THROW:
+                assertEquals(INetd.NEXTHOP_THROW, parcel.nextHop);
+                break;
+            default:
+                assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+                break;
+        }
+    }
+
     private void assertRoutesAdded(int netId, RouteInfo... routes) throws Exception {
-        InOrder inOrder = inOrder(mNetworkManagementService);
+        ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+        verify(mMockNetd, times(routes.length)).networkAddRouteParcel(eq(netId), captor.capture());
         for (int i = 0; i < routes.length; i++) {
-            inOrder.verify(mNetworkManagementService).addRoute(eq(netId), eq(routes[i]));
+            assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
         }
     }
 
     private void assertRoutesRemoved(int netId, RouteInfo... routes) throws Exception {
-        InOrder inOrder = inOrder(mNetworkManagementService);
+        ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+        verify(mMockNetd, times(routes.length)).networkRemoveRouteParcel(eq(netId),
+                captor.capture());
         for (int i = 0; i < routes.length; i++) {
-            inOrder.verify(mNetworkManagementService).removeRoute(eq(netId), eq(routes[i]));
+            assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
         }
     }
 
@@ -6702,16 +7076,12 @@
 
         verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
         verify(mConnectivityDiagnosticsCallback).asBinder();
-        assertTrue(
-                mService.mConnectivityDiagnosticsCallbacks.containsKey(
-                        mConnectivityDiagnosticsCallback));
+        assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
 
         mService.unregisterConnectivityDiagnosticsCallback(mConnectivityDiagnosticsCallback);
         verify(mIBinder, timeout(TIMEOUT_MS))
                 .unlinkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
-        assertFalse(
-                mService.mConnectivityDiagnosticsCallbacks.containsKey(
-                        mConnectivityDiagnosticsCallback));
+        assertFalse(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
         verify(mConnectivityDiagnosticsCallback, atLeastOnce()).asBinder();
     }
 
@@ -6729,9 +7099,7 @@
 
         verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
         verify(mConnectivityDiagnosticsCallback).asBinder();
-        assertTrue(
-                mService.mConnectivityDiagnosticsCallbacks.containsKey(
-                        mConnectivityDiagnosticsCallback));
+        assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
 
         // Register the same callback again
         mService.registerConnectivityDiagnosticsCallback(
@@ -6740,9 +7108,7 @@
         // Block until all other events are done processing.
         HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
 
-        assertTrue(
-                mService.mConnectivityDiagnosticsCallbacks.containsKey(
-                        mConnectivityDiagnosticsCallback));
+        assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
     }
 
     @Test
@@ -6750,7 +7116,7 @@
         final NetworkAgentInfo naiWithoutUid =
                 new NetworkAgentInfo(
                         null, null, null, null, null, new NetworkCapabilities(), 0,
-                        mServiceContext, null, null, mService, null, null, null, 0);
+                        mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
 
         mServiceContext.setPermission(
                 android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
@@ -6762,11 +7128,27 @@
     }
 
     @Test
+    public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception {
+        final NetworkAgentInfo naiWithoutUid =
+                new NetworkAgentInfo(
+                        null, null, null, null, null, new NetworkCapabilities(), 0,
+                        mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
+
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+        assertFalse(
+                "Mismatched uid/package name should not pass the location permission check",
+                mService.checkConnectivityDiagnosticsPermissions(
+                        Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid,
+                        mContext.getOpPackageName()));
+    }
+
+    @Test
     public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
         final NetworkAgentInfo naiWithoutUid =
                 new NetworkAgentInfo(
                         null, null, null, null, null, new NetworkCapabilities(), 0,
-                        mServiceContext, null, null, mService, null, null, null, 0);
+                        mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
 
         mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
@@ -6779,10 +7161,11 @@
 
     @Test
     public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception {
+        final Network network = new Network(NET_ID);
         final NetworkAgentInfo naiWithoutUid =
                 new NetworkAgentInfo(
-                        null, null, null, null, null, new NetworkCapabilities(), 0,
-                        mServiceContext, null, null, mService, null, null, null, 0);
+                        null, null, network, null, null, new NetworkCapabilities(), 0,
+                        mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID);
 
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
@@ -6794,21 +7177,29 @@
         info.ownerUid = Process.myUid();
         info.vpnIface = "interface";
         mMockVpn.setVpnInfo(info);
+        mMockVpn.overrideUnderlyingNetworks(new Network[] {network});
         assertTrue(
                 "Active VPN permission not applied",
                 mService.checkConnectivityDiagnosticsPermissions(
                         Process.myPid(), Process.myUid(), naiWithoutUid,
                         mContext.getOpPackageName()));
+
+        mMockVpn.overrideUnderlyingNetworks(null);
+        assertFalse(
+                "VPN shouldn't receive callback on non-underlying network",
+                mService.checkConnectivityDiagnosticsPermissions(
+                        Process.myPid(), Process.myUid(), naiWithoutUid,
+                        mContext.getOpPackageName()));
     }
 
     @Test
     public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception {
         final NetworkCapabilities nc = new NetworkCapabilities();
-        nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+        nc.setAdministratorUids(new int[] {Process.myUid()});
         final NetworkAgentInfo naiWithUid =
                 new NetworkAgentInfo(
                         null, null, null, null, null, nc, 0, mServiceContext, null, null,
-                        mService, null, null, null, 0);
+                        mService, null, null, null, 0, INVALID_UID);
 
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
@@ -6826,11 +7217,11 @@
     public void testCheckConnectivityDiagnosticsPermissionsFails() throws Exception {
         final NetworkCapabilities nc = new NetworkCapabilities();
         nc.setOwnerUid(Process.myUid());
-        nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+        nc.setAdministratorUids(new int[] {Process.myUid()});
         final NetworkAgentInfo naiWithUid =
                 new NetworkAgentInfo(
                         null, null, null, null, null, nc, 0, mServiceContext, null, null,
-                        mService, null, null, null, 0);
+                        mService, null, null, null, 0, INVALID_UID);
 
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
@@ -6844,6 +7235,38 @@
                         mContext.getOpPackageName()));
     }
 
+    @Test
+    public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport()
+            throws Exception {
+        // Set up the Network, which leads to a ConnectivityReport being cached for the network.
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(callback);
+        final LinkProperties linkProperties = new LinkProperties();
+        linkProperties.setInterfaceName(INTERFACE_NAME);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, linkProperties);
+        mCellNetworkAgent.connect(true);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        callback.assertNoCallback();
+
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+        when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+        mServiceContext.setPermission(
+                android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+        mService.registerConnectivityDiagnosticsCallback(
+                mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+
+        // Block until all other events are done processing.
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+        verify(mConnectivityDiagnosticsCallback)
+                .onConnectivityReportAvailable(argThat(report -> {
+                    return INTERFACE_NAME.equals(report.getLinkProperties().getInterfaceName())
+                            && report.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR);
+                }));
+    }
+
     private void setUpConnectivityDiagnosticsCallback() throws Exception {
         final NetworkRequest request = new NetworkRequest.Builder().build();
         when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
@@ -6867,18 +7290,19 @@
     }
 
     @Test
-    public void testConnectivityDiagnosticsCallbackOnConnectivityReport() throws Exception {
+    public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable()
+            throws Exception {
         setUpConnectivityDiagnosticsCallback();
 
         // Block until all other events are done processing.
         HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
 
         // Verify onConnectivityReport fired
-        verify(mConnectivityDiagnosticsCallback).onConnectivityReport(
+        verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
                 argThat(report -> {
                     final NetworkCapabilities nc = report.getNetworkCapabilities();
                     return nc.getUids() == null
-                            && nc.getAdministratorUids().isEmpty()
+                            && nc.getAdministratorUids().length == 0
                             && nc.getOwnerUid() == Process.INVALID_UID;
                 }));
     }
@@ -6899,7 +7323,7 @@
                 argThat(report -> {
                     final NetworkCapabilities nc = report.getNetworkCapabilities();
                     return nc.getUids() == null
-                            && nc.getAdministratorUids().isEmpty()
+                            && nc.getAdministratorUids().length == 0
                             && nc.getOwnerUid() == Process.INVALID_UID;
                 }));
     }
@@ -6929,4 +7353,60 @@
         verify(mConnectivityDiagnosticsCallback)
                 .onNetworkConnectivityReported(eq(n), eq(noConnectivity));
     }
+
+    @Test
+    public void testRouteAddDeleteUpdate() throws Exception {
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+        final TestNetworkCallback networkCallback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(request, networkCallback);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        reset(mMockNetd);
+        mCellNetworkAgent.connect(false);
+        networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+        final int netId = mCellNetworkAgent.getNetwork().netId;
+
+        final String iface = "rmnet_data0";
+        final InetAddress gateway = InetAddress.getByName("fe80::5678");
+        RouteInfo direct = RouteInfo.makeHostRoute(gateway, iface);
+        RouteInfo rio1 = new RouteInfo(new IpPrefix("2001:db8:1::/48"), gateway, iface);
+        RouteInfo rio2 = new RouteInfo(new IpPrefix("2001:db8:2::/48"), gateway, iface);
+        RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, gateway, iface);
+        RouteInfo defaultWithMtu = new RouteInfo(null, gateway, iface, RouteInfo.RTN_UNICAST,
+                                                 1280 /* mtu */);
+
+        // Send LinkProperties and check that we ask netd to add routes.
+        LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(iface);
+        lp.addRoute(direct);
+        lp.addRoute(rio1);
+        lp.addRoute(defaultRoute);
+        mCellNetworkAgent.sendLinkProperties(lp);
+        networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, x -> x.getRoutes().size() == 3);
+
+        assertRoutesAdded(netId, direct, rio1, defaultRoute);
+        reset(mMockNetd);
+
+        // Send updated LinkProperties and check that we ask netd to add, remove, update routes.
+        assertTrue(lp.getRoutes().contains(defaultRoute));
+        lp.removeRoute(rio1);
+        lp.addRoute(rio2);
+        lp.addRoute(defaultWithMtu);
+        // Ensure adding the same route with a different MTU replaces the previous route.
+        assertFalse(lp.getRoutes().contains(defaultRoute));
+        assertTrue(lp.getRoutes().contains(defaultWithMtu));
+
+        mCellNetworkAgent.sendLinkProperties(lp);
+        networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+                x -> x.getRoutes().contains(rio2));
+
+        assertRoutesRemoved(netId, rio1);
+        assertRoutesAdded(netId, rio2);
+
+        ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+        verify(mMockNetd).networkUpdateRouteParcel(eq(netId), captor.capture());
+        assertRouteInfoParcelMatches(defaultWithMtu, captor.getValue());
+
+
+        mCm.unregisterNetworkCallback(networkCallback);
+    }
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 71b72b8..529d03c 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -46,6 +46,7 @@
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.os.Binder;
+import android.os.INetworkManagementService;
 import android.os.ParcelFileDescriptor;
 import android.system.Os;
 import android.test.mock.MockContext;
@@ -135,6 +136,7 @@
     };
 
     INetd mMockNetd;
+    INetworkManagementService mNetworkManager;
     PackageManager mMockPkgMgr;
     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
     IpSecService mIpSecService;
@@ -160,9 +162,10 @@
     @Before
     public void setUp() throws Exception {
         mMockNetd = mock(INetd.class);
+        mNetworkManager = mock(INetworkManagementService.class);
         mMockPkgMgr = mock(PackageManager.class);
         mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
-        mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+        mIpSecService = new IpSecService(mMockContext, mNetworkManager, mMockIpSecSrvConfig);
 
         // Injecting mock netd
         when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
@@ -544,6 +547,16 @@
 
     @Test
     public void testApplyTransportModeTransform() throws Exception {
+        verifyApplyTransportModeTransformCommon(false);
+    }
+
+    @Test
+    public void testApplyTransportModeTransformReleasedSpi() throws Exception {
+        verifyApplyTransportModeTransformCommon(true);
+    }
+
+    public void verifyApplyTransportModeTransformCommon(
+                boolean closeSpiBeforeApply) throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
         addAuthAndCryptToIpSecConfig(ipSecConfig);
@@ -551,6 +564,39 @@
         IpSecTransformResponse createTransformResp =
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
 
+        if (closeSpiBeforeApply) {
+            mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+        }
+
+        Socket socket = new Socket();
+        socket.bind(null);
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
+
+        int resourceId = createTransformResp.resourceId;
+        mIpSecService.applyTransportModeTransform(pfd, IpSecManager.DIRECTION_OUT, resourceId);
+
+        verify(mMockNetd)
+                .ipSecApplyTransportModeTransform(
+                        eq(pfd),
+                        eq(mUid),
+                        eq(IpSecManager.DIRECTION_OUT),
+                        anyString(),
+                        anyString(),
+                        eq(TEST_SPI));
+    }
+
+    @Test
+    public void testApplyTransportModeTransformWithClosedSpi() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+
+        // Close SPI record
+        mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+
         Socket socket = new Socket();
         socket.bind(null);
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
@@ -609,6 +655,7 @@
                         anyInt(),
                         anyInt(),
                         anyInt());
+        verify(mNetworkManager).setInterfaceUp(createTunnelResp.interfaceName);
     }
 
     @Test
@@ -656,6 +703,15 @@
 
     @Test
     public void testApplyTunnelModeTransform() throws Exception {
+        verifyApplyTunnelModeTransformCommon(false);
+    }
+
+    @Test
+    public void testApplyTunnelModeTransformReleasedSpi() throws Exception {
+        verifyApplyTunnelModeTransformCommon(true);
+    }
+
+    public void verifyApplyTunnelModeTransformCommon(boolean closeSpiBeforeApply) throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
@@ -666,6 +722,49 @@
         IpSecTunnelInterfaceResponse createTunnelResp =
                 createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
 
+        if (closeSpiBeforeApply) {
+            mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+        }
+
+        int transformResourceId = createTransformResp.resourceId;
+        int tunnelResourceId = createTunnelResp.resourceId;
+        mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
+                transformResourceId, "blessedPackage");
+
+        for (int selAddrFamily : ADDRESS_FAMILIES) {
+            verify(mMockNetd)
+                    .ipSecUpdateSecurityPolicy(
+                            eq(mUid),
+                            eq(selAddrFamily),
+                            eq(IpSecManager.DIRECTION_OUT),
+                            anyString(),
+                            anyString(),
+                            eq(TEST_SPI),
+                            anyInt(), // iKey/oKey
+                            anyInt(), // mask
+                            eq(tunnelResourceId));
+        }
+
+        ipSecConfig.setXfrmInterfaceId(tunnelResourceId);
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+    }
+
+
+    @Test
+    public void testApplyTunnelModeTransformWithClosedSpi() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr, "blessedPackage");
+
+        // Close SPI record
+        mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+
         int transformResourceId = createTransformResp.resourceId;
         int tunnelResourceId = createTunnelResp.resourceId;
         mIpSecService.applyTunnelModeTransform(tunnelResourceId, IpSecManager.DIRECTION_OUT,
diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
index 22a2c94..788e4ef 100644
--- a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.INetworkManagementService;
 import android.os.RemoteException;
 
 import androidx.test.filters.SmallTest;
@@ -61,7 +62,8 @@
     public void setUp() throws Exception {
         mMockContext = mock(Context.class);
         mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
-        mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+        mIpSecService = new IpSecService(
+                mMockContext, mock(INetworkManagementService.class), mMockIpSecSrvConfig);
     }
 
     private void assertResourceState(
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 4a35015..536e983 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -42,6 +42,7 @@
 import android.net.IpSecSpiResponse;
 import android.net.IpSecUdpEncapResponse;
 import android.os.Binder;
+import android.os.INetworkManagementService;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.system.ErrnoException;
@@ -115,6 +116,7 @@
     }
 
     Context mMockContext;
+    INetworkManagementService mMockNetworkManager;
     INetd mMockNetd;
     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
     IpSecService mIpSecService;
@@ -122,9 +124,10 @@
     @Before
     public void setUp() throws Exception {
         mMockContext = mock(Context.class);
+        mMockNetworkManager = mock(INetworkManagementService.class);
         mMockNetd = mock(INetd.class);
         mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
-        mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+        mIpSecService = new IpSecService(mMockContext, mMockNetworkManager, mMockIpSecSrvConfig);
 
         // Injecting mock netd
         when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
@@ -132,7 +135,7 @@
 
     @Test
     public void testIpSecServiceCreate() throws InterruptedException {
-        IpSecService ipSecSrv = IpSecService.create(mMockContext);
+        IpSecService ipSecSrv = IpSecService.create(mMockContext, mMockNetworkManager);
         assertNotNull(ipSecSrv);
     }
 
@@ -604,8 +607,8 @@
     @Test
     public void testOpenUdpEncapSocketTagsSocket() throws Exception {
         IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
-        IpSecService testIpSecService =
-                new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger);
+        IpSecService testIpSecService = new IpSecService(
+                mMockContext, mMockNetworkManager, mMockIpSecSrvConfig, mockTagger);
 
         IpSecUdpEncapResponse udpEncapResp =
                 testIpSecService.openUdpEncapsulationSocket(0, new Binder());
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
index 42d4cf3..a10a3c8 100644
--- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
+// Don't warn about deprecated types anywhere in this test, because LegacyTypeTracker's very reason
+// for existence is to power deprecated APIs. The annotation has to apply to the whole file because
+// otherwise warnings will be generated by the imports of deprecated constants like TYPE_xxx.
+@file:Suppress("DEPRECATION")
+
 package com.android.server
 
 import android.net.ConnectivityManager.TYPE_ETHERNET
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 8fa0ab9..0a603b8 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -18,35 +18,54 @@
 
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.NetworkCapabilities.MAX_TRANSPORT;
+import static android.net.NetworkCapabilities.MIN_TRANSPORT;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 
+import static com.android.testutils.MiscAssertsKt.assertContainsExactly;
+import static com.android.testutils.MiscAssertsKt.assertContainsStringsExactly;
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.net.IDnsResolver;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.ResolverOptionsParcel;
+import android.net.ResolverParamsParcel;
 import android.net.RouteInfo;
 import android.net.shared.PrivateDnsConfig;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
+import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.MessageUtils;
 import com.android.internal.util.test.FakeSettingsProvider;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -66,8 +85,11 @@
     static final int TEST_NETID = 100;
     static final int TEST_NETID_ALTERNATE = 101;
     static final int TEST_NETID_UNTRACKED = 102;
-    final boolean IS_DEFAULT = true;
-    final boolean NOT_DEFAULT = false;
+    static final int TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
+    static final int TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25;
+    static final int TEST_DEFAULT_MIN_SAMPLES = 8;
+    static final int TEST_DEFAULT_MAX_SAMPLES = 64;
+    static final int[] TEST_TRANSPORT_TYPES = {TRANSPORT_WIFI, TRANSPORT_VPN};
 
     DnsManager mDnsManager;
     MockContentResolver mContentResolver;
@@ -76,6 +98,35 @@
     @Mock IDnsResolver mMockDnsResolver;
     @Mock MockableSystemProperties mSystemProperties;
 
+    private void assertResolverOptionsEquals(
+            @NonNull ResolverOptionsParcel actual,
+            @NonNull ResolverOptionsParcel expected) {
+        assertEquals(actual.hosts, expected.hosts);
+        assertEquals(actual.tcMode, expected.tcMode);
+        assertFieldCountEquals(2, ResolverOptionsParcel.class);
+    }
+
+    private void assertResolverParamsEquals(@NonNull ResolverParamsParcel actual,
+            @NonNull ResolverParamsParcel expected) {
+        assertEquals(actual.netId, expected.netId);
+        assertEquals(actual.sampleValiditySeconds, expected.sampleValiditySeconds);
+        assertEquals(actual.successThreshold, expected.successThreshold);
+        assertEquals(actual.minSamples, expected.minSamples);
+        assertEquals(actual.maxSamples, expected.maxSamples);
+        assertEquals(actual.baseTimeoutMsec, expected.baseTimeoutMsec);
+        assertEquals(actual.retryCount, expected.retryCount);
+        assertContainsStringsExactly(actual.servers, expected.servers);
+        assertContainsStringsExactly(actual.domains, expected.domains);
+        assertEquals(actual.tlsName, expected.tlsName);
+        assertContainsStringsExactly(actual.tlsServers, expected.tlsServers);
+        assertContainsStringsExactly(actual.tlsFingerprints, expected.tlsFingerprints);
+        assertEquals(actual.caCertificate, expected.caCertificate);
+        assertEquals(actual.tlsConnectTimeoutMs, expected.tlsConnectTimeoutMs);
+        assertResolverOptionsEquals(actual.resolverOptions, expected.resolverOptions);
+        assertContainsExactly(actual.transportTypes, expected.transportTypes);
+        assertFieldCountEquals(16, ResolverParamsParcel.class);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -103,8 +154,13 @@
         lp.addDnsServer(InetAddress.getByName("4.4.4.4"));
 
         // Send a validation event that is tracked on the alternate netId
-        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
-        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID_ALTERNATE, lp, NOT_DEFAULT);
+        mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+        mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+        mDnsManager.flushVmDnsCache();
+        mDnsManager.updateTransportsForNetwork(TEST_NETID_ALTERNATE, TEST_TRANSPORT_TYPES);
+        mDnsManager.noteDnsServersForNetwork(TEST_NETID_ALTERNATE, lp);
+        mDnsManager.flushVmDnsCache();
         mDnsManager.updatePrivateDnsValidation(
                 new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_ALTERNATE,
                 InetAddress.parseNumericAddress("4.4.4.4"), "", true));
@@ -135,7 +191,10 @@
                     InetAddress.parseNumericAddress("6.6.6.6"),
                     InetAddress.parseNumericAddress("2001:db8:66:66::1")
                     }));
-        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+        mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+        mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+        mDnsManager.flushVmDnsCache();
         fixedLp = new LinkProperties(lp);
         mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
         assertTrue(fixedLp.isPrivateDnsActive());
@@ -168,7 +227,10 @@
         // be tracked.
         LinkProperties lp = new LinkProperties();
         lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
-        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+        mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+        mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+        mDnsManager.flushVmDnsCache();
         mDnsManager.updatePrivateDnsValidation(
                 new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
                 InetAddress.parseNumericAddress("3.3.3.3"), "", true));
@@ -179,7 +241,10 @@
         // Validation event has untracked netId
         mDnsManager.updatePrivateDns(new Network(TEST_NETID),
                 mDnsManager.getPrivateDnsConfig());
-        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+        mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+        mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+        mDnsManager.flushVmDnsCache();
         mDnsManager.updatePrivateDnsValidation(
                 new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED,
                 InetAddress.parseNumericAddress("3.3.3.3"), "", true));
@@ -225,7 +290,10 @@
         Settings.Global.putString(mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OFF);
         mDnsManager.updatePrivateDns(new Network(TEST_NETID),
                 mDnsManager.getPrivateDnsConfig());
-        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+        mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+        mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+        mDnsManager.flushVmDnsCache();
         mDnsManager.updatePrivateDnsValidation(
                 new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
                 InetAddress.parseNumericAddress("3.3.3.3"), "", true));
@@ -258,4 +326,57 @@
         assertEquals("strictmode.com", cfgStrict.hostname);
         assertEquals(new InetAddress[0], cfgStrict.ips);
     }
+
+    @Test
+    public void testSendDnsConfiguration() throws Exception {
+        reset(mMockDnsResolver);
+        mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+                mDnsManager.getPrivateDnsConfig());
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(TEST_IFACENAME);
+        lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
+        lp.addDnsServer(InetAddress.getByName("4.4.4.4"));
+        mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
+        mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
+        mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
+        mDnsManager.flushVmDnsCache();
+
+        final ArgumentCaptor<ResolverParamsParcel> resolverParamsParcelCaptor =
+                ArgumentCaptor.forClass(ResolverParamsParcel.class);
+        verify(mMockDnsResolver, times(1)).setResolverConfiguration(
+                resolverParamsParcelCaptor.capture());
+        final ResolverParamsParcel actualParams = resolverParamsParcelCaptor.getValue();
+        final ResolverParamsParcel expectedParams = new ResolverParamsParcel();
+        expectedParams.netId = TEST_NETID;
+        expectedParams.sampleValiditySeconds = TEST_DEFAULT_SAMPLE_VALIDITY_SECONDS;
+        expectedParams.successThreshold = TEST_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
+        expectedParams.minSamples = TEST_DEFAULT_MIN_SAMPLES;
+        expectedParams.maxSamples = TEST_DEFAULT_MAX_SAMPLES;
+        expectedParams.servers = new String[]{"3.3.3.3", "4.4.4.4"};
+        expectedParams.domains = new String[]{};
+        expectedParams.tlsName = "";
+        expectedParams.tlsServers = new String[]{"3.3.3.3", "4.4.4.4"};
+        expectedParams.transportTypes = TEST_TRANSPORT_TYPES;
+        expectedParams.resolverOptions = new ResolverOptionsParcel();
+        assertResolverParamsEquals(actualParams, expectedParams);
+    }
+
+    @Test
+    public void testTransportTypesEqual() throws Exception {
+        SparseArray<String> ncTransTypes = MessageUtils.findMessageNames(
+                new Class[] { NetworkCapabilities.class }, new String[]{ "TRANSPORT_" });
+        SparseArray<String> dnsTransTypes = MessageUtils.findMessageNames(
+                new Class[] { IDnsResolver.class }, new String[]{ "TRANSPORT_" });
+        assertEquals(0, MIN_TRANSPORT);
+        assertEquals(MAX_TRANSPORT + 1, ncTransTypes.size());
+        // TRANSPORT_UNKNOWN in IDnsResolver is defined to -1 and only for resolver.
+        assertEquals("TRANSPORT_UNKNOWN", dnsTransTypes.get(-1));
+        assertEquals(ncTransTypes.size(), dnsTransTypes.size() - 1);
+        for (int i = MIN_TRANSPORT; i < MAX_TRANSPORT; i++) {
+            String name = ncTransTypes.get(i, null);
+            assertNotNull("Could not find NetworkCapabilies.TRANSPORT_* constant equal to "
+                    + i, name);
+            assertEquals(name, dnsTransTypes.get(i));
+        }
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 24a8717..aafa18a 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -38,6 +38,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkProvider;
+import android.os.Binder;
 import android.os.INetworkManagementService;
 import android.text.format.DateUtils;
 
@@ -354,7 +355,7 @@
         caps.addTransportType(transport);
         NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
                 caps, 50, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS,
-                NetworkProvider.ID_NONE);
+                NetworkProvider.ID_NONE, Binder.getCallingUid());
         nai.everValidated = true;
         return nai;
     }
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index 9b24887..5046b65 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -58,8 +58,10 @@
 
     static final String BASE_IFACE = "test0";
     static final String STACKED_IFACE = "v4-test0";
+    static final LinkAddress V6ADDR = new LinkAddress("2001:db8:1::f00/64");
     static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
     static final String NAT64_PREFIX = "64:ff9b::/96";
+    static final String OTHER_NAT64_PREFIX = "2001:db8:0:64::/96";
     static final int NETID = 42;
 
     @Mock ConnectivityService mConnectivity;
@@ -81,6 +83,14 @@
         };
     }
 
+    private void markNetworkConnected() {
+        mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
+    }
+
+    private void markNetworkDisconnected() {
+        mNai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, "", "");
+    }
+
     @Before
     public void setUp() throws Exception {
         mLooper = new TestLooper();
@@ -92,6 +102,7 @@
         mNai.linkProperties.setInterfaceName(BASE_IFACE);
         mNai.networkInfo = new NetworkInfo(null);
         mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+        markNetworkConnected();
         when(mNai.connService()).thenReturn(mConnectivity);
         when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
         when(mNai.handler()).thenReturn(mHandler);
@@ -139,7 +150,7 @@
             for (NetworkInfo.DetailedState state : supportedDetailedStates) {
                 mNai.networkInfo.setDetailedState(state, "reason", "extraInfo");
 
-                mNai.linkProperties.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
+                mNai.linkProperties.setNat64Prefix(new IpPrefix(OTHER_NAT64_PREFIX));
                 assertRequiresClat(false, mNai);
                 assertShouldStartClat(false, mNai);
 
@@ -176,12 +187,21 @@
         }
     }
 
-    @Test
-    public void testNormalStartAndStop() throws Exception {
+    private void makeClatUnnecessary(boolean dueToDisconnect) {
+        if (dueToDisconnect) {
+            markNetworkDisconnected();
+        } else {
+            mNai.linkProperties.addLinkAddress(ADDR);
+        }
+    }
+
+    private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
         Nat464Xlat nat = makeNat464Xlat();
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        mNai.linkProperties.addLinkAddress(V6ADDR);
+
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         // Start clat.
         nat.start();
@@ -200,6 +220,7 @@
         assertRunning(nat);
 
         // Stop clat (Network disconnects, IPv4 addr appears, ...).
+        makeClatUnnecessary(dueToDisconnect);
         nat.stop();
 
         verify(mNetd).clatdStop(eq(BASE_IFACE));
@@ -217,12 +238,24 @@
         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
+    @Test
+    public void testNormalStartAndStopDueToDisconnect() throws Exception {
+        checkNormalStartAndStop(true);
+    }
+
+    @Test
+    public void testNormalStartAndStopDueToIpv4Addr() throws Exception {
+        checkNormalStartAndStop(false);
+    }
+
     private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
         Nat464Xlat nat = makeNat464Xlat();
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
         InOrder inOrder = inOrder(mNetd, mConnectivity);
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        mNai.linkProperties.addLinkAddress(V6ADDR);
+
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         nat.start();
 
@@ -309,7 +342,7 @@
         Nat464Xlat nat = makeNat464Xlat();
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         nat.start();
 
@@ -344,11 +377,12 @@
         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
-    @Test
-    public void testStopBeforeClatdStarts() throws Exception {
+    private void checkStopBeforeClatdStarts(boolean dueToDisconnect) throws Exception {
         Nat464Xlat nat = makeNat464Xlat();
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         nat.start();
 
@@ -356,6 +390,7 @@
         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
 
         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
+        makeClatUnnecessary(dueToDisconnect);
         nat.stop();
 
         verify(mNetd).clatdStop(eq(BASE_IFACE));
@@ -377,10 +412,21 @@
     }
 
     @Test
-    public void testStopAndClatdNeverStarts() throws Exception {
+    public void testStopDueToDisconnectBeforeClatdStarts() throws Exception {
+        checkStopBeforeClatdStarts(true);
+    }
+
+    @Test
+    public void testStopDueToIpv4AddrBeforeClatdStarts() throws Exception {
+        checkStopBeforeClatdStarts(false);
+    }
+
+    private void checkStopAndClatdNeverStarts(boolean dueToDisconnect) throws Exception {
         Nat464Xlat nat = makeNat464Xlat();
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         nat.start();
 
@@ -388,6 +434,7 @@
         verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
 
         // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
+        makeClatUnnecessary(dueToDisconnect);
         nat.stop();
 
         verify(mNetd).clatdStop(eq(BASE_IFACE));
@@ -398,6 +445,57 @@
         verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
     }
 
+    @Test
+    public void testStopDueToDisconnectAndClatdNeverStarts() throws Exception {
+        checkStopAndClatdNeverStarts(true);
+    }
+
+    @Test
+    public void testStopDueToIpv4AddressAndClatdNeverStarts() throws Exception {
+        checkStopAndClatdNeverStarts(false);
+    }
+
+    @Test
+    public void testNat64PrefixPreference() throws Exception {
+        final IpPrefix prefixFromDns = new IpPrefix(NAT64_PREFIX);
+        final IpPrefix prefixFromRa = new IpPrefix(OTHER_NAT64_PREFIX);
+
+        Nat464Xlat nat = makeNat464Xlat();
+
+        final LinkProperties emptyLp = new LinkProperties();
+        LinkProperties fixedupLp;
+
+        fixedupLp = new LinkProperties();
+        nat.setNat64PrefixFromDns(prefixFromDns);
+        nat.fixupLinkProperties(emptyLp, fixedupLp);
+        assertEquals(prefixFromDns, fixedupLp.getNat64Prefix());
+
+        fixedupLp = new LinkProperties();
+        nat.setNat64PrefixFromRa(prefixFromRa);
+        nat.fixupLinkProperties(emptyLp, fixedupLp);
+        assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
+
+        fixedupLp = new LinkProperties();
+        nat.setNat64PrefixFromRa(null);
+        nat.fixupLinkProperties(emptyLp, fixedupLp);
+        assertEquals(prefixFromDns, fixedupLp.getNat64Prefix());
+
+        fixedupLp = new LinkProperties();
+        nat.setNat64PrefixFromRa(prefixFromRa);
+        nat.fixupLinkProperties(emptyLp, fixedupLp);
+        assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
+
+        fixedupLp = new LinkProperties();
+        nat.setNat64PrefixFromDns(null);
+        nat.fixupLinkProperties(emptyLp, fixedupLp);
+        assertEquals(prefixFromRa, fixedupLp.getNat64Prefix());
+
+        fixedupLp = new LinkProperties();
+        nat.setNat64PrefixFromRa(null);
+        nat.fixupLinkProperties(emptyLp, fixedupLp);
+        assertEquals(null, fixedupLp.getNat64Prefix());
+    }
+
     static void assertIdle(Nat464Xlat nat) {
         assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
     }
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index d57f225..47db5d4 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -238,20 +238,6 @@
     }
 
     @Test
-    public void testSameLevelNotifications() {
-        final int id = 101;
-        final String tag = NetworkNotificationManager.tagFor(id);
-
-        mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
-        verify(mNotificationManager, times(1))
-                .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
-
-        mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false);
-        verify(mNotificationManager, times(1))
-                .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any());
-    }
-
-    @Test
     public void testClearNotificationByType() {
         final int id = 101;
         final String tag = NetworkNotificationManager.tagFor(id);
@@ -259,31 +245,25 @@
         // clearNotification(int id, NotificationType notifyType) will check if given type is equal
         // to previous type or not. If they are equal then clear the notification; if they are not
         // equal then return.
-
-        mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
+        mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false);
         verify(mNotificationManager, times(1))
-                .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
+                .notifyAsUser(eq(tag), eq(NO_INTERNET.eventId), any(), any());
 
-        // Previous notification is LOGGED_IN and given type is LOGGED_IN too. The notification
+        // Previous notification is NO_INTERNET and given type is NO_INTERNET too. The notification
         // should be cleared.
-        mManager.clearNotification(id, LOGGED_IN);
+        mManager.clearNotification(id, NO_INTERNET);
         verify(mNotificationManager, times(1))
-                .cancelAsUser(eq(tag), eq(LOGGED_IN.eventId), any());
+                .cancelAsUser(eq(tag), eq(NO_INTERNET.eventId), any());
 
-        mManager.showNotification(id, LOGGED_IN, mWifiNai, mCellNai, null, false);
-        verify(mNotificationManager, times(2))
-                .notifyAsUser(eq(tag), eq(LOGGED_IN.eventId), any(), any());
-
-        // LOST_INTERNET notification popup after LOGGED_IN notification.
-        mManager.showNotification(id, LOST_INTERNET, mWifiNai, mCellNai, null, false);
+        // SIGN_IN is popped-up.
+        mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false);
         verify(mNotificationManager, times(1))
-                .notifyAsUser(eq(tag), eq(LOST_INTERNET.eventId), any(), any());
+                .notifyAsUser(eq(tag), eq(SIGN_IN.eventId), any(), any());
 
-        // Previous notification is LOST_INTERNET and given type is LOGGED_IN. The notification
-        // shouldn't be cleared.
-        mManager.clearNotification(id, LOGGED_IN);
-        // LOST_INTERNET shouldn't be cleared.
+        // The notification type is not matching previous one, PARTIAL_CONNECTIVITY won't be
+        // cleared.
+        mManager.clearNotification(id, PARTIAL_CONNECTIVITY);
         verify(mNotificationManager, never())
-                .cancelAsUser(eq(tag), eq(LOST_INTERNET.eventId), any());
+                .cancelAsUser(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId), any());
     }
 }
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 1994d1f..f8d8a56 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -25,6 +25,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
@@ -606,6 +607,7 @@
                         .addCapability(NET_CAPABILITY_NOT_METERED)
                         .addCapability(NET_CAPABILITY_NOT_ROAMING)
                         .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                        .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
                         .setLinkUpstreamBandwidthKbps(20));
         setMockedNetworks(networks);
 
@@ -621,6 +623,7 @@
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
         Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager,
@@ -635,6 +638,7 @@
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+        assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
         Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
@@ -646,6 +650,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
         Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
@@ -657,6 +662,7 @@
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
 
         Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager,
@@ -671,6 +677,7 @@
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+        assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
     }
 
     /**
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
index 28785f7..3aafe0b 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
@@ -41,6 +41,7 @@
     static final String TEST_IFACE = "test0";
     static final String TEST_IFACE2 = "test1";
     static final String TUN_IFACE = "test_nss_tun0";
+    static final String TUN_IFACE2 = "test_nss_tun1";
 
     static final int UID_RED = 1001;
     static final int UID_BLUE = 1002;
@@ -107,10 +108,14 @@
         assertEquals("unexpected operations", operations, entry.operations);
     }
 
-    VpnInfo createVpnInfo(String[] underlyingIfaces) {
+    static VpnInfo createVpnInfo(String[] underlyingIfaces) {
+        return createVpnInfo(TUN_IFACE, underlyingIfaces);
+    }
+
+    static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
         VpnInfo info = new VpnInfo();
         info.ownerUid = UID_VPN;
-        info.vpnIface = TUN_IFACE;
+        info.vpnIface = vpnIface;
         info.underlyingIfaces = underlyingIfaces;
         return info;
     }
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 8f90f13..551498f 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -319,33 +319,33 @@
             assertEntry(18322, 75, 15031, 75, history.getValues(i++, null));
             assertEntry(527798, 761, 78570, 652, history.getValues(i++, null));
             assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
-            assertEntry(10747, 50, 16838, 55, history.getValues(i++, null));
-            assertEntry(10747, 49, 16838, 54, history.getValues(i++, null));
+            assertEntry(10747, 50, 16839, 55, history.getValues(i++, null));
+            assertEntry(10747, 49, 16837, 54, history.getValues(i++, null));
             assertEntry(89191, 151, 18021, 140, history.getValues(i++, null));
             assertEntry(89190, 150, 18020, 139, history.getValues(i++, null));
-            assertEntry(3821, 22, 4525, 26, history.getValues(i++, null));
-            assertEntry(3820, 22, 4524, 26, history.getValues(i++, null));
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+            assertEntry(3821, 23, 4525, 26, history.getValues(i++, null));
+            assertEntry(3820, 21, 4524, 26, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
+            assertEntry(8289, 36, 6864, 39, history.getValues(i++, null));
+            assertEntry(8289, 34, 6862, 37, history.getValues(i++, null));
             assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
             assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
-            assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
-            assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
-            assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
-            assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
-            assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
-            assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+            assertEntry(11378, 49, 9261, 50, history.getValues(i++, null));
+            assertEntry(11377, 48, 9261, 48, history.getValues(i++, null));
+            assertEntry(201766, 328, 41808, 291, history.getValues(i++, null));
+            assertEntry(201764, 328, 41807, 290, history.getValues(i++, null));
+            assertEntry(106106, 219, 39918, 202, history.getValues(i++, null));
+            assertEntry(106105, 216, 39916, 200, history.getValues(i++, null));
             assertEquals(history.size(), i);
 
             // Slice from middle should be untouched
             history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
                     TIME_B + HOUR_IN_MILLIS); i = 0;
-            assertEntry(3821, 22, 4525, 26, history.getValues(i++, null));
-            assertEntry(3820, 22, 4524, 26, history.getValues(i++, null));
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+            assertEntry(3821, 23, 4525, 26, history.getValues(i++, null));
+            assertEntry(3820, 21, 4524, 26, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
             assertEquals(history.size(), i);
         }
 
@@ -373,25 +373,25 @@
             assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
             // Cycle point; start data normalization
             assertEntry(7507, 0, 11763, 0, history.getValues(i++, null));
-            assertEntry(7507, 0, 11763, 0, history.getValues(i++, null));
+            assertEntry(7507, 0, 11762, 0, history.getValues(i++, null));
             assertEntry(62309, 0, 12589, 0, history.getValues(i++, null));
             assertEntry(62309, 0, 12588, 0, history.getValues(i++, null));
             assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
             assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
             // Anchor point; end data normalization
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
+            assertEntry(8289, 36, 6864, 39, history.getValues(i++, null));
+            assertEntry(8289, 34, 6862, 37, history.getValues(i++, null));
             assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
             assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
             // Cycle point
-            assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
-            assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
-            assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
-            assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
-            assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
-            assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+            assertEntry(11378, 49, 9261, 50, history.getValues(i++, null));
+            assertEntry(11377, 48, 9261, 48, history.getValues(i++, null));
+            assertEntry(201766, 328, 41808, 291, history.getValues(i++, null));
+            assertEntry(201764, 328, 41807, 290, history.getValues(i++, null));
+            assertEntry(106106, 219, 39918, 202, history.getValues(i++, null));
+            assertEntry(106105, 216, 39916, 200, history.getValues(i++, null));
             assertEquals(history.size(), i);
 
             // Slice from middle should be augmented
@@ -399,8 +399,8 @@
                     TIME_B + HOUR_IN_MILLIS); i = 0;
             assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
             assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
             assertEquals(history.size(), i);
         }
 
@@ -427,34 +427,34 @@
             assertEntry(527798, 761, 78570, 652, history.getValues(i++, null));
             assertEntry(527797, 760, 78570, 651, history.getValues(i++, null));
             // Cycle point; start data normalization
-            assertEntry(15015, 0, 23526, 0, history.getValues(i++, null));
-            assertEntry(15015, 0, 23526, 0, history.getValues(i++, null));
+            assertEntry(15015, 0, 23527, 0, history.getValues(i++, null));
+            assertEntry(15015, 0, 23524, 0, history.getValues(i++, null));
             assertEntry(124619, 0, 25179, 0, history.getValues(i++, null));
             assertEntry(124618, 0, 25177, 0, history.getValues(i++, null));
             assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
             assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
             // Anchor point; end data normalization
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
-            assertEntry(8289, 35, 6863, 38, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
+            assertEntry(8289, 36, 6864, 39, history.getValues(i++, null));
+            assertEntry(8289, 34, 6862, 37, history.getValues(i++, null));
             assertEntry(113914, 174, 18364, 157, history.getValues(i++, null));
             assertEntry(113913, 173, 18364, 157, history.getValues(i++, null));
             // Cycle point
-            assertEntry(11378, 49, 9261, 49, history.getValues(i++, null));
-            assertEntry(11377, 48, 9261, 49, history.getValues(i++, null));
-            assertEntry(201765, 328, 41808, 291, history.getValues(i++, null));
-            assertEntry(201765, 328, 41807, 290, history.getValues(i++, null));
-            assertEntry(106106, 218, 39917, 201, history.getValues(i++, null));
-            assertEntry(106105, 217, 39917, 201, history.getValues(i++, null));
+            assertEntry(11378, 49, 9261, 50, history.getValues(i++, null));
+            assertEntry(11377, 48, 9261, 48, history.getValues(i++, null));
+            assertEntry(201766, 328, 41808, 291, history.getValues(i++, null));
+            assertEntry(201764, 328, 41807, 290, history.getValues(i++, null));
+            assertEntry(106106, 219, 39918, 202, history.getValues(i++, null));
+            assertEntry(106105, 216, 39916, 200, history.getValues(i++, null));
 
             // Slice from middle should be augmented
             history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
                     TIME_B + HOUR_IN_MILLIS); i = 0;
             assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
             assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
-            assertEntry(91686, 159, 18575, 146, history.getValues(i++, null));
-            assertEntry(91685, 159, 18575, 146, history.getValues(i++, null));
+            assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
+            assertEntry(91685, 159, 18574, 146, history.getValues(i++, null));
             assertEquals(history.size(), i);
         }
     }
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index a21f509..4473492 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -104,7 +104,7 @@
     }
 
     @Test
-    public void vpnRewriteTrafficThroughItself() throws Exception {
+    public void testVpnRewriteTrafficThroughItself() throws Exception {
         VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
         mFactory.updateVpnInfos(vpnInfos);
 
@@ -133,7 +133,7 @@
     }
 
     @Test
-    public void vpnWithClat() throws Exception {
+    public void testVpnWithClat() throws Exception {
         VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})};
         mFactory.updateVpnInfos(vpnInfos);
         mFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE);
@@ -166,7 +166,7 @@
     }
 
     @Test
-    public void vpnWithOneUnderlyingIface() throws Exception {
+    public void testVpnWithOneUnderlyingIface() throws Exception {
         VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
         mFactory.updateVpnInfos(vpnInfos);
 
@@ -189,7 +189,7 @@
     }
 
     @Test
-    public void vpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
+    public void testVpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
         // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
         VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
         mFactory.updateVpnInfos(vpnInfos);
@@ -217,7 +217,7 @@
     }
 
     @Test
-    public void vpnWithOneUnderlyingIface_withCompression() throws Exception {
+    public void testVpnWithOneUnderlyingIface_withCompression() throws Exception {
         // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
         VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
         mFactory.updateVpnInfos(vpnInfos);
@@ -238,7 +238,7 @@
     }
 
     @Test
-    public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception {
+    public void testVpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception {
         // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
         // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
         // Additionally, VPN is duplicating traffic across both WiFi and Cell.
@@ -264,7 +264,47 @@
     }
 
     @Test
-    public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception {
+    public void testConcurrentVpns() throws Exception {
+        // Assume two VPNs are connected on two different network interfaces. VPN1 is using
+        // TEST_IFACE and VPN2 is using TEST_IFACE2.
+        final VpnInfo[] vpnInfos = new VpnInfo[] {
+                createVpnInfo(TUN_IFACE, new String[] {TEST_IFACE}),
+                createVpnInfo(TUN_IFACE2, new String[] {TEST_IFACE2})};
+        mFactory.updateVpnInfos(vpnInfos);
+
+        // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+        // overhead per packet):
+        // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
+        // over VPN1.
+        // 700 bytes (70 packets) were sent, and 3000 bytes (300 packets) were received by UID_RED
+        // over VPN2.
+        // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
+        // over VPN1.
+        // 250 bytes (25 packets) were sent, and 500 bytes (50 packets) were received by UID_BLUE
+        // over VPN2.
+        // VPN1 sent 1650 bytes (150 packets), and received 3300 (300 packets) over TEST_IFACE.
+        // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
+        // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN.
+        // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
+        // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN.
+        // VPN2 sent 1045 bytes (95 packets), and received 3850 (350 packets) over TEST_IFACE2.
+        // Of 1045 bytes sent over Cell, expect 700 bytes attributed to UID_RED, 250 bytes
+        // attributed to UID_BLUE, and 95 bytes attributed to UID_VPN.
+        // Of 3850 bytes received over Cell, expect 3000 bytes attributed to UID_RED, 500 bytes
+        // attributed to UID_BLUE, and 350 bytes attributed to UID_VPN.
+        final NetworkStats tunStats =
+                parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_two_vpn);
+
+        assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
+        assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
+        assertValues(tunStats, TEST_IFACE2, UID_RED, 3000L, 300L, 700L, 70L);
+        assertValues(tunStats, TEST_IFACE2, UID_BLUE, 500L, 50L, 250L, 25L);
+        assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 150L, 0L);
+        assertValues(tunStats, TEST_IFACE2, UID_VPN, 350L, 0L, 95L, 0L);
+    }
+
+    @Test
+    public void testVpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception {
         // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
         // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
         // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
@@ -291,7 +331,7 @@
     }
 
     @Test
-    public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception {
+    public void testVpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception {
         // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
         // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
         // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
@@ -314,7 +354,7 @@
     }
 
     @Test
-    public void vpnWithIncorrectUnderlyingIface() throws Exception {
+    public void testVpnWithIncorrectUnderlyingIface() throws Exception {
         // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
         // but has declared only WiFi (TEST_IFACE) in its underlying network set.
         VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index f0e5774..a6f7a36 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -240,7 +240,7 @@
 
         // Baseline
         NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+                .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
         NetworkStats uidSnapshot = null;
 
         mStatsObservers.updateStats(
@@ -264,14 +264,14 @@
 
         // Baseline
         NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+                .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
         NetworkStats uidSnapshot = null;
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
+                .insertEntry(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
         waitForObserverToIdle();
@@ -294,14 +294,14 @@
 
         // Baseline
         NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+                .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
         NetworkStats uidSnapshot = null;
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
+                .insertEntry(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
                         BASE_BYTES + THRESHOLD_BYTES, 22L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
@@ -326,14 +326,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
@@ -359,14 +359,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
@@ -391,14 +391,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
@@ -424,14 +424,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+                .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
                         ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+                .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
                         ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 36deca3..a1bb0d5 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -42,6 +42,7 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkStatsHistory.FIELD_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
 import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.net.TrafficStats.UID_REMOVED;
@@ -59,6 +60,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -104,7 +106,7 @@
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
 import com.android.testutils.HandlerUtilsKt;
-import com.android.testutils.TestableNetworkStatsProvider;
+import com.android.testutils.TestableNetworkStatsProviderBinder;
 
 import libcore.io.IoUtils;
 
@@ -121,6 +123,7 @@
 import java.time.Clock;
 import java.time.ZoneOffset;
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * Tests for {@link NetworkStatsService}.
@@ -163,6 +166,8 @@
     private @Mock NetworkStatsSettings mSettings;
     private @Mock IBinder mBinder;
     private @Mock AlarmManager mAlarmManager;
+    @Mock
+    private NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
     private HandlerThread mHandlerThread;
 
     private NetworkStatsService mService;
@@ -195,8 +200,8 @@
         mHandlerThread = new HandlerThread("HandlerThread");
         final NetworkStatsService.Dependencies deps = makeDependencies();
         mService = new NetworkStatsService(mServiceContext, mNetManager, mAlarmManager, wakeLock,
-                mClock, mServiceContext.getSystemService(TelephonyManager.class), mSettings,
-                mStatsFactory, new NetworkStatsObservers(), mStatsDir, getBaseDir(mStatsDir), deps);
+                mClock, mSettings, mStatsFactory, new NetworkStatsObservers(), mStatsDir,
+                getBaseDir(mStatsDir), deps);
 
         mElapsedRealtime = 0L;
 
@@ -225,6 +230,14 @@
             public HandlerThread makeHandlerThread() {
                 return mHandlerThread;
             }
+
+            @Override
+            public NetworkStatsSubscriptionsMonitor makeSubscriptionsMonitor(
+                    @NonNull Context context, @NonNull Executor executor,
+                    @NonNull NetworkStatsService service) {
+
+                return mNetworkStatsSubscriptionsMonitor;
+            }
         };
     }
 
@@ -263,7 +276,7 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+                .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -276,7 +289,7 @@
         incrementCurrentTime(DAY_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 4096L, 4L, 8192L, 8L));
+                .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -306,13 +319,13 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1024L, 8L, 2048L, 16L));
+                .insertEntry(TEST_IFACE, 1024L, 8L, 2048L, 16L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
         mService.setUidForeground(UID_RED, false);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
         mService.setUidForeground(UID_RED, true);
@@ -375,7 +388,7 @@
         incrementCurrentTime(2 * HOUR_IN_MILLIS);
         expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 512L, 4L, 512L, 4L));
+                .insertEntry(TEST_IFACE, 512L, 4L, 512L, 4L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -415,11 +428,11 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+                .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 10);
 
         forcePollAndWaitForIdle();
@@ -437,11 +450,11 @@
         expectDefaultSettings();
         states = new NetworkState[] {buildMobile3gState(IMSI_2)};
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+                .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
 
         mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
         forcePollAndWaitForIdle();
@@ -451,12 +464,12 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L));
+                .insertEntry(TEST_IFACE, 2176L, 17L, 1536L, 12L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
         mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10);
 
         forcePollAndWaitForIdle();
@@ -488,12 +501,13 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+                .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
-                .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+                        4096L, 258L, 512L, 32L, 0L)
+                .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xFAAD, 10);
 
         forcePollAndWaitForIdle();
@@ -509,12 +523,13 @@
         // special "removed" bucket.
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+                .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
-                .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+                        4096L, 258L, 512L, 32L, 0L)
+                .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
         final Intent intent = new Intent(ACTION_UID_REMOVED);
         intent.putExtra(EXTRA_UID, UID_BLUE);
         mServiceContext.sendBroadcast(intent);
@@ -532,7 +547,7 @@
     }
 
     @Test
-    public void testUid3g4gCombinedByTemplate() throws Exception {
+    public void testUid3gWimaxCombinedByTemplate() throws Exception {
         // pretend that network comes online
         expectDefaultSettings();
         NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
@@ -546,8 +561,8 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 5);
 
         forcePollAndWaitForIdle();
@@ -556,14 +571,14 @@
         assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5);
 
 
-        // now switch over to 4g network
+        // now switch over to wimax network
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
-        states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)};
+        states = new NetworkState[] {buildWimaxState(TEST_IFACE2)};
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
 
         mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
         forcePollAndWaitForIdle();
@@ -574,10 +589,10 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
         mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
 
         forcePollAndWaitForIdle();
@@ -587,6 +602,88 @@
     }
 
     @Test
+    public void testMobileStatsByRatType() throws Exception {
+        final NetworkTemplate template3g =
+                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
+        final NetworkTemplate template4g =
+                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE);
+        final NetworkTemplate template5g =
+                buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR);
+        final NetworkState[] states = new NetworkState[]{buildMobile3gState(IMSI_1)};
+
+        // 3G network comes online.
+        expectNetworkStatsSummary(buildEmptyStats());
+        expectNetworkStatsUidDetail(buildEmptyStats());
+
+        setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS);
+        mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+                new VpnInfo[0]);
+
+        // Create some traffic.
+        incrementCurrentTime(MINUTE_IN_MILLIS);
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+                        12L, 18L, 14L, 1L, 0L)));
+        forcePollAndWaitForIdle();
+
+        // Verify 3g templates gets stats.
+        assertUidTotal(sTemplateImsi1, UID_RED, 12L, 18L, 14L, 1L, 0);
+        assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0);
+        assertUidTotal(template4g, UID_RED, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0);
+
+        // 4G network comes online.
+        incrementCurrentTime(MINUTE_IN_MILLIS);
+        setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_LTE);
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+                // Append more traffic on existing 3g stats entry.
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+                        16L, 22L, 17L, 2L, 0L))
+                // Add entry that is new on 4g.
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
+                        33L, 27L, 8L, 10L, 1L)));
+        forcePollAndWaitForIdle();
+
+        // Verify ALL_MOBILE template gets all. 3g template counters do not increase.
+        assertUidTotal(sTemplateImsi1, UID_RED, 49L, 49L, 25L, 12L, 1);
+        assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0);
+        // Verify 4g template counts appended stats on existing entry and newly created entry.
+        assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1);
+        // Verify 5g template doesn't get anything since no traffic is generated on 5g.
+        assertUidTotal(template5g, UID_RED, 0L, 0L, 0L, 0L, 0);
+
+        // 5g network comes online.
+        incrementCurrentTime(MINUTE_IN_MILLIS);
+        setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_NR);
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+                // Existing stats remains.
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+                        16L, 22L, 17L, 2L, 0L))
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE,
+                        33L, 27L, 8L, 10L, 1L))
+                // Add some traffic on 5g.
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+                5L, 13L, 31L, 9L, 2L)));
+        forcePollAndWaitForIdle();
+
+        // Verify ALL_MOBILE template gets all.
+        assertUidTotal(sTemplateImsi1, UID_RED, 54L, 62L, 56L, 21L, 3);
+        // 3g/4g template counters do not increase.
+        assertUidTotal(template3g, UID_RED, 12L, 18L, 14L, 1L, 0);
+        assertUidTotal(template4g, UID_RED, 4L + 33L, 4L + 27L, 3L + 8L, 1L + 10L, 1);
+        // Verify 5g template gets the 5g count.
+        assertUidTotal(template5g, UID_RED, 5L, 13L, 31L, 9L, 2);
+    }
+
+    // TODO: support per IMSI state
+    private void setMobileRatTypeAndWaitForIdle(int ratType) {
+        when(mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(anyString()))
+                .thenReturn(ratType);
+        mService.handleOnCollapsedRatTypeChanged();
+        HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
+    }
+
+    @Test
     public void testSummaryForAllUid() throws Exception {
         // pretend that network comes online
         expectDefaultSettings();
@@ -601,9 +698,9 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         forcePollAndWaitForIdle();
@@ -618,9 +715,10 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+                        2048L, 16L, 1024L, 8L, 0L));
         forcePollAndWaitForIdle();
 
         // first verify entire history present
@@ -664,9 +762,9 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3));
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL);
@@ -714,11 +812,11 @@
                 .thenReturn(augmentedIfaceFilter);
         when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL)))
                 .thenReturn(new NetworkStats(getElapsedRealtime(), 1)
-                        .addEntry(uidStats));
+                        .insertEntry(uidStats));
         when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
                 .thenReturn(new NetworkStats(getElapsedRealtime(), 2)
-                        .addEntry(tetheredStats1)
-                        .addEntry(tetheredStats2));
+                        .insertEntry(tetheredStats1)
+                        .insertEntry(tetheredStats2));
 
         NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
 
@@ -755,8 +853,8 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         forcePollAndWaitForIdle();
@@ -770,10 +868,10 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
         mService.setUidForeground(UID_RED, true);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
 
@@ -814,9 +912,9 @@
         // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
         // We layer them on top by inspecting the iface properties.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
@@ -853,9 +951,9 @@
         // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
         // on top by inspecting the iface properties.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
                         DEFAULT_NETWORK_YES,  128L, 2L, 128L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
         forcePollAndWaitForIdle();
 
@@ -888,17 +986,17 @@
 
         // Traffic seen by kernel counters (includes software tethering).
         final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1536L, 12L, 384L, 3L);
+                .insertEntry(TEST_IFACE, 1536L, 12L, 384L, 3L);
         // Hardware tethering traffic, not seen by kernel counters.
         final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 512L, 4L, 128L, 1L);
+                .insertEntry(TEST_IFACE, 512L, 4L, 128L, 1L);
 
         // Traffic for UID_RED.
         final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
         // All tethering traffic, both hardware and software.
         final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
+                .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
                         0L);
 
         expectNetworkStatsSummary(ifaceStats, tetherStatsHardware);
@@ -957,7 +1055,7 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+                .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -972,7 +1070,7 @@
         incrementCurrentTime(DAY_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
+                .insertEntry(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -1018,7 +1116,8 @@
         expectNetworkStatsUidDetail(buildEmptyStats());
 
         // Register custom provider and retrieve callback.
-        final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider();
+        final TestableNetworkStatsProviderBinder provider =
+                new TestableNetworkStatsProviderBinder();
         final INetworkStatsProviderCallback cb =
                 mService.registerNetworkStatsProvider("TEST", provider);
         assertNotNull(cb);
@@ -1026,18 +1125,18 @@
         mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
 
         // Verifies that one requestStatsUpdate will be called during iface update.
-        provider.expectStatsUpdate(0 /* unused */);
+        provider.expectOnRequestStatsUpdate(0 /* unused */);
 
         // Create some initial traffic and report to the service.
         incrementCurrentTime(HOUR_IN_MILLIS);
         final NetworkStats expectedStats = new NetworkStats(0L, 1)
-                .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
                         TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
                         128L, 2L, 128L, 2L, 1L))
-                .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
                         0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
                         64L, 1L, 64L, 1L, 1L));
-        cb.onStatsUpdated(0 /* unused */, expectedStats, expectedStats);
+        cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats);
 
         // Make another empty mutable stats object. This is necessary since the new NetworkStats
         // object will be used to compare with the old one in NetworkStatsRecoder, two of them
@@ -1047,8 +1146,8 @@
         forcePollAndWaitForIdle();
 
         // Verifies that one requestStatsUpdate and setAlert will be called during polling.
-        provider.expectStatsUpdate(0 /* unused */);
-        provider.expectSetAlert(MB_IN_BYTES);
+        provider.expectOnRequestStatsUpdate(0 /* unused */);
+        provider.expectOnSetAlert(MB_IN_BYTES);
 
         // Verifies that service recorded history, does not verify uid tag part.
         assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
@@ -1076,19 +1175,20 @@
         mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
 
         // Register custom provider and retrieve callback.
-        final TestableNetworkStatsProvider provider = new TestableNetworkStatsProvider();
+        final TestableNetworkStatsProviderBinder provider =
+                new TestableNetworkStatsProviderBinder();
         final INetworkStatsProviderCallback cb =
                 mService.registerNetworkStatsProvider("TEST", provider);
         assertNotNull(cb);
 
         // Simulates alert quota of the provider has been reached.
-        cb.onAlertReached();
+        cb.notifyAlertReached();
         HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
 
         // Verifies that polling is triggered by alert reached.
-        provider.expectStatsUpdate(0 /* unused */);
+        provider.expectOnRequestStatsUpdate(0 /* unused */);
         // Verifies that global alert will be re-armed.
-        provider.expectSetAlert(MB_IN_BYTES);
+        provider.expectOnSetAlert(MB_IN_BYTES);
     }
 
     private static File getBaseDir(File statsDir) {
@@ -1194,6 +1294,7 @@
         when(mSettings.getPollInterval()).thenReturn(HOUR_IN_MILLIS);
         when(mSettings.getPollDelay()).thenReturn(0L);
         when(mSettings.getSampleEnabled()).thenReturn(true);
+        when(mSettings.getCombineSubtypeEnabled()).thenReturn(false);
 
         final Config config = new Config(bucketDuration, deleteAge, deleteAge);
         when(mSettings.getDevConfig()).thenReturn(config);
@@ -1239,6 +1340,7 @@
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
+        capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
         return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
     }
 
@@ -1256,10 +1358,11 @@
         final NetworkCapabilities capabilities = new NetworkCapabilities();
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
+        capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
         return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
     }
 
-    private static NetworkState buildMobile4gState(String iface) {
+    private static NetworkState buildWimaxState(@NonNull String iface) {
         final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
         info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
new file mode 100644
index 0000000..eb0513b
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn
@@ -0,0 +1,9 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
+4 test_nss_tun1 0x0 1001 0 3000 300 700 70 0 0 0 0 0 0 0 0 0 0 0 0
+5 test_nss_tun1 0x0 1002 0 500 50 250 25 0 0 0 0 0 0 0 0 0 0 0 0
+6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+7 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
+8 test1 0x0 1004 0 3850 350 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+9 test1 0x0 1004 1 0 0 1045 95 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 8d99ac7..7218ae3 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -222,6 +222,10 @@
         return sLoopers.get(test);
     }
 
+    public static void remove(Object test) {
+        sLoopers.remove(test);
+    }
+
     static class LooperFrameworkMethod extends FrameworkMethod {
         private HandlerThread mHandlerThread;
 
diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h
index b4ea624..6e9dc3d 100644
--- a/tools/aapt/ConfigDescription.h
+++ b/tools/aapt/ConfigDescription.h
@@ -34,8 +34,8 @@
         size = sizeof(android::ResTable_config);
     }
 
-    ConfigDescription(const ConfigDescription&o) {
-        *static_cast<android::ResTable_config*>(this) = o;
+    ConfigDescription(const ConfigDescription&o)
+        : android::ResTable_config(o) {
     }
 
     ConfigDescription& operator=(const android::ResTable_config& o) {
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 53372bf..a2709bd 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -47,6 +47,7 @@
             cflags: ["-D_DARWIN_UNLIMITED_STREAMS"],
         },
     },
+    header_libs: ["jni_headers"],
     static_libs: [
         "libandroidfw",
         "libutils",
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index b4b6ff1..304bc49 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -27,7 +27,7 @@
 
 static ApiVersion sDevelopmentSdkLevel = 10000;
 static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>({
-    "Q", "R"
+    "Q", "R", "S"
 });
 
 static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index f354bb6..7afb000 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1659,13 +1659,24 @@
       return 1;
     }
 
-    // First extract the Package name without modifying it (via --rename-manifest-package).
+    // First extract the package name without modifying it (via --rename-manifest-package).
     if (Maybe<AppInfo> maybe_app_info =
             ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
+      // Extract the package name from the manifest ignoring the value of --rename-manifest-package.
       const AppInfo& app_info = maybe_app_info.value();
       context_->SetCompilationPackage(app_info.package);
     }
 
+    // Determine the package name under which to merge resources.
+    if (options_.rename_resources_package) {
+      if (!options_.custom_java_package) {
+        // Generate the R.java under the original package name instead of the package name specified
+        // through --rename-resources-package.
+        options_.custom_java_package = context_->GetCompilationPackage();
+      }
+      context_->SetCompilationPackage(options_.rename_resources_package.value());
+    }
+
     // Now that the compilation package is set, load the dependencies. This will also extract
     // the Android framework's versionCode and versionName, if they exist.
     if (!LoadSymbolsFromIncludePaths()) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 7c58385..e62e0a6b 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -43,6 +43,7 @@
   bool output_to_directory = false;
   bool auto_add_overlay = false;
   OutputFormat output_format = OutputFormat::kApk;
+  Maybe<std::string> rename_resources_package;
 
   // Java/Proguard options.
   Maybe<std::string> generate_java_class_path;
@@ -244,10 +245,16 @@
         &options_.auto_add_overlay);
     AddOptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
         &options_.manifest_fixer_options.rename_manifest_package);
+    AddOptionalFlag("--rename-resources-package", "Renames the package in resources table",
+        &options_.rename_resources_package);
     AddOptionalFlag("--rename-instrumentation-target-package",
         "Changes the name of the target package for instrumentation. Most useful\n"
             "when used in conjunction with --rename-manifest-package.",
         &options_.manifest_fixer_options.rename_instrumentation_target_package);
+    AddOptionalFlag("--rename-overlay-target-package",
+        "Changes the name of the target package for overlay. Most useful\n"
+            "when used in conjunction with --rename-manifest-package.",
+        &options_.manifest_fixer_options.rename_overlay_target_package);
     AddOptionalFlagList("-0", "File extensions not to compress.",
         &options_.extensions_to_not_compress);
     AddOptionalSwitch("--no-compress", "Do not compress any resources.",
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 49909f6..06303c2 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -236,6 +236,16 @@
     }
   }
 
+  if (options_.rename_overlay_target_package) {
+    if (!util::IsJavaPackageName(options_.rename_overlay_target_package.value())) {
+      diag->Error(DiagMessage()
+                  << "invalid overlay target package override '"
+                  << options_.rename_overlay_target_package.value()
+                  << "'");
+      return false;
+    }
+  }
+
   // Common <intent-filter> actions.
   xml::XmlNodeAction intent_filter_action;
   intent_filter_action["action"].Action(RequiredNameIsNotEmpty);
@@ -337,7 +347,17 @@
   manifest_action["instrumentation"]["meta-data"] = meta_data_action;
 
   manifest_action["original-package"];
-  manifest_action["overlay"];
+  manifest_action["overlay"].Action([&](xml::Element* el) -> bool {
+    if (!options_.rename_overlay_target_package) {
+      return true;
+    }
+
+    if (xml::Attribute* attr =
+            el->FindAttribute(xml::kSchemaAndroid, "targetPackage")) {
+      attr->value = options_.rename_overlay_target_package.value();
+    }
+    return true;
+  });
   manifest_action["protected-broadcast"];
   manifest_action["adopt-permissions"];
   manifest_action["uses-permission"];
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 3ef57d0..ec4367b 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -44,6 +44,10 @@
   // <instrumentation>.
   Maybe<std::string> rename_instrumentation_target_package;
 
+  // The Android package to use instead of the one defined in 'android:targetPackage' in
+  // <overlay>.
+  Maybe<std::string> rename_overlay_target_package;
+
   // The version name to set if 'android:versionName' is not defined in <manifest> or if
   // replace_version is set.
   Maybe<std::string> version_name_default;
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 3f1ee36..3aba4e2 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -325,6 +325,32 @@
   EXPECT_THAT(attr->value, StrEq("com.android"));
 }
 
+TEST_F(ManifestFixerTest,
+       RenameManifestOverlayPackageAndFullyQualifyTarget) {
+  ManifestFixerOptions options;
+  options.rename_overlay_target_package = std::string("com.android");
+
+  std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+                package="android">
+        <overlay android:targetName="Customization" android:targetPackage="android" />
+      </manifest>)EOF",
+                                                            options);
+  ASSERT_THAT(doc, NotNull());
+
+  xml::Element* manifest_el = doc->root.get();
+  ASSERT_THAT(manifest_el, NotNull());
+
+  xml::Element* overlay_el =
+      manifest_el->FindChild({}, "overlay");
+  ASSERT_THAT(overlay_el, NotNull());
+
+  xml::Attribute* attr =
+      overlay_el->FindAttribute(xml::kSchemaAndroid, "targetPackage");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("com.android"));
+}
+
 TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
   ManifestFixerOptions options;
   options.version_name_default = std::string("Beta");
diff --git a/tools/bit/adb.cpp b/tools/bit/adb.cpp
index fa7d3d4..f521a63 100644
--- a/tools/bit/adb.cpp
+++ b/tools/bit/adb.cpp
@@ -200,7 +200,7 @@
 
 static int
 skip_unknown_field(int fd, uint64_t tag, char* scratch, int scratchSize) {
-    bool done;
+    bool done = false;
     int err;
     uint64_t size;
     switch (tag & 0x7) {
diff --git a/tools/dump-coverage/Android.bp b/tools/dump-coverage/Android.bp
index 4519ce3..94356eb 100644
--- a/tools/dump-coverage/Android.bp
+++ b/tools/dump-coverage/Android.bp
@@ -19,6 +19,7 @@
     name: "libdumpcoverage",
     srcs: ["dump_coverage.cc"],
     header_libs: [
+        "jni_headers",
         "libopenjdkjvmti_headers",
     ],
 
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index 0b2077d..de6b478 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -18,10 +18,10 @@
 """
 import argparse
 from collections import defaultdict
-import os
-import sys
-import re
 import functools
+import os
+import re
+import sys
 
 # Names of flags recognized by the `hiddenapi` tool.
 FLAG_WHITELIST = "whitelist"
@@ -30,6 +30,7 @@
 FLAG_GREYLIST_MAX_O = "greylist-max-o"
 FLAG_GREYLIST_MAX_P = "greylist-max-p"
 FLAG_GREYLIST_MAX_Q = "greylist-max-q"
+FLAG_GREYLIST_MAX_R = "greylist-max-r"
 FLAG_CORE_PLATFORM_API = "core-platform-api"
 FLAG_PUBLIC_API = "public-api"
 FLAG_SYSTEM_API = "system-api"
@@ -43,13 +44,14 @@
     FLAG_GREYLIST_MAX_O,
     FLAG_GREYLIST_MAX_P,
     FLAG_GREYLIST_MAX_Q,
+    FLAG_GREYLIST_MAX_R,
 ]
 ALL_FLAGS = FLAGS_API_LIST + [
     FLAG_CORE_PLATFORM_API,
     FLAG_PUBLIC_API,
     FLAG_SYSTEM_API,
     FLAG_TEST_API,
-    ]
+]
 
 FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
 ALL_FLAGS_SET = set(ALL_FLAGS)
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 91f875e..ba1267b 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -368,10 +368,13 @@
         // Don't generate a variable twice
         if (!hasDefaultFlags[i]) variableNames[fieldName] = false;
     }
+    // hasDefaultFlags[i] has been initialized in the above for-loop,
+    // but clang-tidy analyzer still report uninitized values.
+    // So we use NOLINT to suppress those false positives.
 
     bool allDefaults = true;
     for (size_t i=0; i<fieldsInOrder.size(); i++) {
-        allDefaults &= hasDefaultFlags[i];
+        allDefaults &= hasDefaultFlags[i];  // NOLINT(clang-analyzer-core.uninitialized.Assign)
     }
 
     parents->erase(messageName); // erase the message type name when exit the message.
@@ -384,7 +387,7 @@
     printf("Privacy* %s[] = {\n", messageName.c_str());
     for (size_t i=0; i<fieldsInOrder.size(); i++) {
         const FieldDescriptor* field = fieldsInOrder[i];
-        if (hasDefaultFlags[i]) continue;
+        if (hasDefaultFlags[i]) continue; // NOLINT(clang-analyzer-core.uninitialized.Branch)
         printf("    &%s,\n", getFieldName(field).c_str());
         policyCount++;
     }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 933dded..ce49ab1 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -28,6 +28,7 @@
 import android.net.ProxyInfo;
 import android.net.StaticIpConfiguration;
 import android.net.Uri;
+import android.net.util.MacAddressUtils;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1037,7 +1038,7 @@
      * @return true if mac is good to use
      */
     public static boolean isValidMacAddressForRandomization(MacAddress mac) {
-        return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned()
+        return mac != null && !MacAddressUtils.isMulticastAddress(mac) && mac.isLocallyAssigned()
                 && !MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS).equals(mac);
     }
 
@@ -1051,7 +1052,7 @@
         int randomMacGenerationCount = 0;
         while (!isValidMacAddressForRandomization(mRandomizedMacAddress)
                 && randomMacGenerationCount < MAXIMUM_RANDOM_MAC_GENERATION_RETRY) {
-            mRandomizedMacAddress = MacAddress.createRandomUnicastAddress();
+            mRandomizedMacAddress = MacAddressUtils.createRandomUnicastAddress();
             randomMacGenerationCount++;
         }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 47d8f13..29e09a11 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -892,7 +892,7 @@
      * The RSSI (signal strength) has changed.
      *
      * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
-     * @see {@link #EXTRA_NEW_RSSI}
+     * @see #EXTRA_NEW_RSSI
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
index 20fbc9f..d384298 100644
--- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -104,7 +104,7 @@
     }
 
     @Override
-    public boolean satisfiedBy(@Nullable NetworkSpecifier other) {
+    public boolean canBeSatisfiedBy(@Nullable NetworkSpecifier other) {
         if (this == other) {
             return true;
         }
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index e1d3c43..f08935a 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -583,7 +583,7 @@
 
     /** @hide */
     @Override
-    public boolean satisfiedBy(NetworkSpecifier other) {
+    public boolean canBeSatisfiedBy(NetworkSpecifier other) {
         if (this == other) {
             return true;
         }
diff --git a/wifi/java/android/net/wifi/WpsInfo.java b/wifi/java/android/net/wifi/WpsInfo.java
index 00cb243..689ace5b 100644
--- a/wifi/java/android/net/wifi/WpsInfo.java
+++ b/wifi/java/android/net/wifi/WpsInfo.java
@@ -22,7 +22,7 @@
 /**
  * A class representing Wi-Fi Protected Setup
  *
- * {@see WifiP2pConfig}
+ * {@see android.net.wifi.p2p.WifiP2pConfig}
  */
 public class WpsInfo implements Parcelable {
 
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index 282fda8..3b26c6e 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -121,7 +121,7 @@
     }
 
     @Override
-    public boolean satisfiedBy(NetworkSpecifier other) {
+    public boolean canBeSatisfiedBy(NetworkSpecifier other) {
         if (!(other instanceof WifiAwareAgentNetworkSpecifier)) {
             return false;
         }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
index ad656e1..487f3d8 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -227,7 +227,7 @@
 
     /** @hide */
     @Override
-    public boolean satisfiedBy(NetworkSpecifier other) {
+    public boolean canBeSatisfiedBy(NetworkSpecifier other) {
         // MatchAllNetworkSpecifier is taken care in NetworkCapabilities#satisfiedBySpecifier.
         if (other instanceof WifiAwareAgentNetworkSpecifier) {
             return ((WifiAwareAgentNetworkSpecifier) other).satisfiesAwareNetworkSpecifier(this);
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index ba9fc78..15aa3cc 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.net.MacAddress;
+import android.net.util.MacAddressUtils;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.os.Parcel;
@@ -62,7 +63,8 @@
         config.updateIdentifier = "1234";
         config.fromWifiNetworkSpecifier = true;
         config.fromWifiNetworkSuggestion = true;
-        MacAddress macBeforeParcel = config.getOrCreateRandomizedMacAddress();
+        config.setRandomizedMacAddress(MacAddressUtils.createRandomUnicastAddress());
+        MacAddress macBeforeParcel = config.getRandomizedMacAddress();
         Parcel parcelW = Parcel.obtain();
         config.writeToParcel(parcelW, 0);
         byte[] bytes = parcelW.marshall();
@@ -75,7 +77,7 @@
 
         // lacking a useful config.equals, check two fields near the end.
         assertEquals(cookie, reconfig.getMoTree());
-        assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress());
+        assertEquals(macBeforeParcel, reconfig.getRandomizedMacAddress());
         assertEquals(config.updateIdentifier, reconfig.updateIdentifier);
         assertFalse(reconfig.trusted);
         assertTrue(config.fromWifiNetworkSpecifier);
@@ -211,7 +213,7 @@
         MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
         assertEquals(defaultMac, config.getRandomizedMacAddress());
 
-        MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress();
+        MacAddress macToChangeInto = MacAddressUtils.createRandomUnicastAddress();
         config.setRandomizedMacAddress(macToChangeInto);
         MacAddress macAfterChange = config.getRandomizedMacAddress();
 
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index 38040ea..b58b321 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -22,7 +22,6 @@
 
 import android.net.MacAddress;
 import android.net.MatchAllNetworkSpecifier;
-import android.net.NetworkRequest;
 import android.os.Parcel;
 import android.os.PatternMatcher;
 import android.util.Pair;
@@ -166,8 +165,8 @@
     public void testWifiNetworkAgentSpecifierSatisifiesNullAndAllMatch() {
         WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier();
 
-        assertTrue(specifier.satisfiedBy(null));
-        assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier()));
+        assertTrue(specifier.canBeSatisfiedBy(null));
+        assertTrue(specifier.canBeSatisfiedBy(new MatchAllNetworkSpecifier()));
     }
 
     /**
@@ -181,7 +180,7 @@
         WifiNetworkAgentSpecifier specifier1 = createDefaultNetworkAgentSpecifier();
         WifiNetworkAgentSpecifier specifier2 = createDefaultNetworkAgentSpecifier();
 
-        assertTrue(specifier2.satisfiedBy(specifier1));
+        assertTrue(specifier2.canBeSatisfiedBy(specifier1));
     }
 
     /**
@@ -208,8 +207,8 @@
                 wificonfigurationNetworkSpecifier,
                 TEST_UID, TEST_PACKAGE);
 
-        assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
-        assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+        assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier));
+        assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
     }
 
     /**
@@ -237,8 +236,8 @@
                 wificonfigurationNetworkSpecifier,
                 TEST_UID, TEST_PACKAGE);
 
-        assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
-        assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+        assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier));
+        assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
     }
 
     /**
@@ -266,8 +265,8 @@
                 wificonfigurationNetworkSpecifier,
                 TEST_UID, TEST_PACKAGE);
 
-        assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
-        assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+        assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier));
+        assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
     }
 
     /**
@@ -299,8 +298,8 @@
                 wificonfigurationNetworkSpecifier,
                 TEST_UID, TEST_PACKAGE);
 
-        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
-        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+        assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
     }
 
     /**
@@ -333,8 +332,8 @@
                 wificonfigurationNetworkSpecifier,
                 TEST_UID, TEST_PACKAGE);
 
-        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
-        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+        assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
     }
 
     /**
@@ -367,8 +366,8 @@
                 wificonfigurationNetworkSpecifier,
                 TEST_UID, TEST_PACKAGE);
 
-        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
-        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+        assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
     }
 
     /**
@@ -395,8 +394,8 @@
                 wificonfigurationNetworkSpecifier,
                 TEST_UID, TEST_PACKAGE);
 
-        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
-        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+        assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
     }
 
     /**
@@ -424,8 +423,8 @@
                 wificonfigurationNetworkSpecifier,
                 TEST_UID_1, TEST_PACKAGE_1);
 
-        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
-        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+        assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier));
     }
 
     private WifiConfiguration createDefaultWifiConfiguration() {
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index edb43d8..5261e7a 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -399,8 +399,8 @@
                         wifiConfiguration,
                         TEST_UID, TEST_PACKAGE_NAME);
 
-        assertTrue(specifier.satisfiedBy(null));
-        assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier()));
+        assertTrue(specifier.canBeSatisfiedBy(null));
+        assertTrue(specifier.canBeSatisfiedBy(new MatchAllNetworkSpecifier()));
     }
 
     /**
@@ -429,7 +429,7 @@
                         wifiConfiguration,
                         TEST_UID, TEST_PACKAGE_NAME);
 
-        assertTrue(specifier2.satisfiedBy(specifier1));
+        assertTrue(specifier2.canBeSatisfiedBy(specifier1));
     }
 
     /**
@@ -460,7 +460,7 @@
                         wifiConfiguration2,
                         TEST_UID, TEST_PACKAGE_NAME);
 
-        assertFalse(specifier2.satisfiedBy(specifier1));
+        assertFalse(specifier2.canBeSatisfiedBy(specifier1));
     }
 
     /**
@@ -489,7 +489,7 @@
                         wifiConfiguration,
                         TEST_UID, TEST_PACKAGE_NAME);
 
-        assertFalse(specifier2.satisfiedBy(specifier1));
+        assertFalse(specifier2.canBeSatisfiedBy(specifier1));
     }
 
     /**
@@ -517,7 +517,7 @@
                         wifiConfiguration,
                         TEST_UID, TEST_PACKAGE_NAME);
 
-        assertFalse(specifier2.satisfiedBy(specifier1));
+        assertFalse(specifier2.canBeSatisfiedBy(specifier1));
     }
 
     /**
@@ -546,6 +546,6 @@
                         wifiConfiguration,
                         TEST_UID, TEST_PACKAGE_NAME + "blah");
 
-        assertFalse(specifier2.satisfiedBy(specifier1));
+        assertFalse(specifier2.canBeSatisfiedBy(specifier1));
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index ef9c6a3..3f38543 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -79,7 +79,7 @@
     public void testEmptyDoesntMatchAnything() {
         WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier();
         WifiAwareNetworkSpecifier ns = getDummyNetworkSpecifier(6);
-        collector.checkThat("No match expected", ns.satisfiedBy(dut), equalTo(false));
+        collector.checkThat("No match expected", ns.canBeSatisfiedBy(dut), equalTo(false));
     }
 
     /**
@@ -91,8 +91,8 @@
         WifiAwareNetworkSpecifier nsThis = getDummyNetworkSpecifier(6);
         WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier(nsThis);
         WifiAwareNetworkSpecifier nsOther = getDummyNetworkSpecifier(8);
-        collector.checkThat("Match expected", nsThis.satisfiedBy(dut), equalTo(true));
-        collector.checkThat("No match expected", nsOther.satisfiedBy(dut), equalTo(false));
+        collector.checkThat("Match expected", nsThis.canBeSatisfiedBy(dut), equalTo(true));
+        collector.checkThat("No match expected", nsOther.canBeSatisfiedBy(dut), equalTo(false));
     }
 
     /**
@@ -113,9 +113,9 @@
         WifiAwareNetworkSpecifier nsOther = getDummyNetworkSpecifier(10000);
 
         for (WifiAwareNetworkSpecifier nsThis: nsSet) {
-            collector.checkThat("Match expected", nsThis.satisfiedBy(dut), equalTo(true));
+            collector.checkThat("Match expected", nsThis.canBeSatisfiedBy(dut), equalTo(true));
         }
-        collector.checkThat("No match expected", nsOther.satisfiedBy(dut), equalTo(false));
+        collector.checkThat("No match expected", nsOther.canBeSatisfiedBy(dut), equalTo(false));
     }
 
     /**
@@ -137,7 +137,7 @@
         WifiAwareAgentNetworkSpecifier newNs = new WifiAwareAgentNetworkSpecifier(
                 nsSet.toArray(new WifiAwareNetworkSpecifier[nsSet.size()]));
 
-        collector.checkThat("Match expected", oldNs.satisfiedBy(newNs), equalTo(true));
+        collector.checkThat("Match expected", oldNs.canBeSatisfiedBy(newNs), equalTo(true));
     }
 
     /**
@@ -159,7 +159,7 @@
         WifiAwareAgentNetworkSpecifier oldNs = new WifiAwareAgentNetworkSpecifier(
                 nsSet.toArray(new WifiAwareNetworkSpecifier[nsSet.size()]));
 
-        collector.checkThat("Match unexpected", oldNs.satisfiedBy(newNs), equalTo(false));
+        collector.checkThat("Match unexpected", oldNs.canBeSatisfiedBy(newNs), equalTo(false));
     }
 
     // utilities