Merge "Use Class#getSimpleName instead of KClass#getSimpleName"
diff --git a/Android.bp b/Android.bp
index baa715e..cfdfeb3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -687,7 +687,7 @@
 
 filegroup {
     name: "framework-ike-shared-srcs",
-    visibility: ["//frameworks/opt/net/ike"],
+    visibility: ["//packages/modules/IPsec"],
     srcs: [
         "core/java/android/annotation/StringDef.java",
         "core/java/android/net/annotations/PolicyDirection.java",
@@ -1132,6 +1132,7 @@
     "--hide MissingPermission --hide BroadcastBehavior " +
     "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
     "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
+    "--error NoSettingsProvider " +
     "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
     "--api-lint-ignore-prefix android.icu. " +
     "--api-lint-ignore-prefix java. " +
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 60f56de..029699e 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -66,7 +66,7 @@
         ":opt-telephony-srcs",
         ":opt-net-voip-srcs",
         ":art-module-public-api-stubs-source",
-        ":conscrypt.module.public.api.stubs.source",
+        ":conscrypt.module.public.api{.public.stubs.source}",
         ":android_icu4j_public_api_files",
         "test-mock/src/**/*.java",
         "test-runner/src/**/*.java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 4a77463..c80d134 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -49,6 +49,7 @@
         ":opt-net-voip-srcs",
         ":art-module-public-api-stubs-source",
         ":android_icu4j_public_api_files",
+        "**/package.html",
     ],
     libs: ["framework-internal-utils"],
     installable: false,
@@ -65,7 +66,7 @@
     name: "metalava-full-api-stubs-default",
     defaults: ["metalava-base-api-stubs-default"],
     srcs: [
-        ":conscrypt.module.public.api.stubs.source",
+        ":conscrypt.module.public.api{.public.stubs.source}",
         ":framework-updatable-sources",
     ],
     sdk_version: "core_platform",
diff --git a/apex/Android.bp b/apex/Android.bp
index 3a63c805..380b4c6 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -71,6 +71,15 @@
     // stubs libraries.
     libs: ["framework-annotations-lib"],
 
+    // Framework modules are not generally shared libraries, i.e. they are not
+    // intended, and must not be allowed, to be used in a <uses-library> manifest
+    // entry.
+    shared_library: false,
+
+    // Prevent dependencies that do not specify an sdk_version from accessing the
+    // implementation library by default and force them to use stubs instead.
+    default_to_stubs: true,
+
     // 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.
diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp
deleted file mode 100644
index fdb078e..0000000
--- a/apex/sdkextensions/Android.bp
+++ /dev/null
@@ -1,84 +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 {
-    default_visibility: [":__subpackages__"],
-}
-
-apex {
-    name: "com.android.sdkext",
-    defaults: [ "com.android.sdkext-defaults" ],
-    binaries: [ "derive_sdk" ],
-    prebuilts: [ "cur_sdkinfo" ],
-    manifest: "manifest.json",
-    min_sdk_version: "current",
-}
-
-apex_defaults {
-    name: "com.android.sdkext-defaults",
-    updatable: true,
-    min_sdk_version: "R",
-    java_libs: [ "framework-sdkextensions" ],
-    prebuilts: [
-        "derive_sdk.rc",
-    ],
-    key: "com.android.sdkext.key",
-    certificate: ":com.android.sdkext.certificate",
-}
-
-sdk {
-    name: "sdkextensions-sdk",
-    java_sdk_libs: [ "framework-sdkextensions" ],
-}
-
-apex_key {
-    name: "com.android.sdkext.key",
-    public_key: "com.android.sdkext.avbpubkey",
-    private_key: "com.android.sdkext.pem",
-}
-
-android_app_certificate {
-    name: "com.android.sdkext.certificate",
-    certificate: "com.android.sdkext",
-}
-
-python_binary_host {
-    name: "gen_sdkinfo",
-    srcs: [
-        "sdk.proto",
-        "gen_sdkinfo.py",
-    ],
-    proto: {
-        canonical_path_from_root: false,
-    },
-    version: {
-        py3: {
-            embedded_launcher: true,
-        },
-    },
-}
-
-gensrcs {
-    name: "cur_sdkinfo_src",
-    srcs: [""],
-    tools: [ "gen_sdkinfo" ],
-    cmd: "$(location) -v 0 -o $(out)",
-}
-
-prebuilt_etc {
-    name: "cur_sdkinfo",
-    src: ":cur_sdkinfo_src",
-    filename: "sdkinfo.binarypb",
-    installable: false,
-}
diff --git a/apex/sdkextensions/OWNERS b/apex/sdkextensions/OWNERS
deleted file mode 100644
index a6e5522..0000000
--- a/apex/sdkextensions/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-dariofreni@google.com
-hansson@google.com
diff --git a/apex/sdkextensions/TEST_MAPPING b/apex/sdkextensions/TEST_MAPPING
deleted file mode 100644
index 3dc1b9f..0000000
--- a/apex/sdkextensions/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "CtsSdkExtensionsTestCases"
-    },
-    {
-      "name": "sdkextensions_e2e_tests"
-    }
-  ]
-}
diff --git a/apex/sdkextensions/com.android.sdkext.avbpubkey b/apex/sdkextensions/com.android.sdkext.avbpubkey
deleted file mode 100644
index 8f47741..0000000
--- a/apex/sdkextensions/com.android.sdkext.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/apex/sdkextensions/com.android.sdkext.pem b/apex/sdkextensions/com.android.sdkext.pem
deleted file mode 100644
index 8164601..0000000
--- a/apex/sdkextensions/com.android.sdkext.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKQIBAAKCAgEAr72pTSavrziDP54AtQZlRclDxJf9HXRZwFRbYx9hWZ4z7ZtO
-pNBDPvPJCiAOVUsILgCQhBUolz2dyLob25Fd0PVp0n9ibIPEQYjTfHjOK40qb57N
-LhEp2ceGiAfsywPSi0TH1PQ6JgbCe/RM4TefI/sj3gYJPka3ksMvylhMIgUVLgME
-kYizhzwHqlLMspB858SioREZdGwcdZrMMIajGFU69Q9ZRDBzhPvxyKhYoObcOtk1
-uVaiE/fNoi3wKGJz2l2vhUuNrQW7MWlVMag+Qes4YACUTk9LZrOVFEJFjWc8xGUi
-ABtfKGs5JgNr/sWnhvifLn8lJuf0/BJmjD+L5QwXYs2cS7gcZJtTM12J94r0Twgw
-wF2lNmIxAE9sYqj5Rh3dIlTPE5vMUECmQEGjIBB/hzT65VxVqSjU/IlS506JTg3p
-IOQtZ15cUzTBpda4jrvqcq6RNVvgBCu2bV5D8Z4z9FUlPyvD+Zq/6lcoJfLtznAs
-G2463hyPAHTGBIcZ5p5bTuGxoAb6ivyqo4b9Qi4yYA6je9HJmuy8T3Mn5JROoeu9
-BH1K54r/mpT4TQPwuKUvRRtBAV2OPHjo+zp0Gd4Y6rxDYxEIdfEae7pQr/QExSPB
-q/QCr9RhixR1mO373LHuja+MxdAxIxugb2HTS61PQo+PbYrhJMcVuxTwJOECAwEA
-AQKCAgAH7ToRrMkH0ji5SdsmTx+KQkW4PFLCXVke/68PjX7KmAQnl3W4oVwnHr/W
-oROEbVn1GTlre7jU+YaAY0SWZrwgjLE1OWGrG1ZizlUbrCdAd6GOX09J4KROml1L
-DXB0x7tbZMLOrCVjSbLD/ITrM6MN8Gnxvbv0/yOQjxU8vzbP4gLOjHxMRCo001RV
-Ll7lPvcjTQ84zJilU6sE8vJ6zdfVZSK/ou2X0cekG+kP7+fvefo8/UcbEPlGhUrV
-IdVPPQGUu90K2hmN0FBdLi8Vik0klAN68Qu/bHwuKbNzsnmIoztucFFUR+fG3u84
-87aPS0L/J3+mjT2Tv6qhJANUGBmrK/h7MkelpKXlRTCITJLX9xP7hfSbJ4f6aLVq
-ZYPPciGxSBbUDgAwvPtOlMDzccg7YsYyiBBO28wh8MN97rePmc0z6nGmjeXhcbCC
-QktG50VYFCyqp5muKgqQmRfRjHFHLWs8GEqgxMeEL3U3HjYfCYr+6E8Sr5OnOBeH
-3buCi1+zgnNYCvbamgY/OJmW7f9h5O31hxmTplc2E1ZuxUGQZthabt1rN3bmNkyf
-KUmPwnIYkDkWBSV5lzyQExfS3/EVvj0EnHhx8faamimNrGo8xCcfnLT3c0WEFVmo
-yIyVRX3EpXJFM2JkeJ21/IEZXTzHSoNxk12CBG8i8lLSflWSMQKCAQEA2ZqVnOuV
-SZfLCUYUUh8Hvhc5zONstfq7ma1Zsttsdaj9t68nLRiBDvLOGnMjDkYZzoSd4fyl
-oy+YqWGBqcqa5kg1NOCH0I46p9d8RcWAfDnB4sqbLgWh70qsvni6igRijmsMDvkA
-U9HeEdPaLCjQ4UXw7GQvN5rRxuRt+OSqV3tV/Pk9JYyYjz7faC8dmbKDrWHHuOvm
-/9y6Xy+L5IgftykNlUeddSCIoMOAadM7BiRjsrHnOYBQ8xBcn0OYafpIswItrgVi
-IrsPJaBFidx8QYK4MVibyka6U0cm28OocDSPtSk/4jrnCEEhLjFUnWwuMXuBGlrd
-W7wP/muoJqb1VwKCAQEAzsAT90kkOCvAcrfGRE3KkUjwWAsQyP8u2+27JIQPqrpW
-GfWAzJXFt80TSp0Zf/Lrq3/SQ9n4AaL4K9dcMoreedoQN9C9JI7zPtZAWNrJVUcV
-dq2gZjBQ78+oK7uQgvFNWxga5D+Nh+Y+9Tp537fc5HIh0Y13PgsxxPk2OnZJTvLX
-HM5H7Aua9ssmqChsrKihuUsDSPozfBz+H7FNHEdKMqVLqJJSK6m0uMxuLovdVfka
-5S7iBMjEGZc46Iz3ckE0pdOiQLooNqfEQqFe5Uou/KZxxKI1NW25rEEBTVyQWt+2
-BNUCfKP7noZ45u5sUY3eJrgI7BrPEDbNS81WYaLchwKCAQA8Q4mHyd6wYO+EA/qA
-u8NDK9+AFMP4qhXme5HJ7Obetwx9IG7zGEQ1xZy6yoQ84cEn5qZq/bNJvFbFIhHs
-2gWIHRtPJ5e1dI5eCVmLYSUyQjSmAIJ1fm3YfY/VuE3BB3HcC11tkBw9GnQr78YO
-UMd4fAw7C4vgFGpgcMbcFUfvrmKkCsqaaZOeqETq75F9DWlWTSwo1HxHA/RBhENz
-6RcPfLkcTJcY5wevrjUUGcHQ86cAyDBHRngkuLVODkRZpU0Y9lN8TFVfVPre6sIX
-ag6nffJRCD8tB+V2RtBGMKunV4ctHt1oY/Oz34W260aJynoIjjG1ANEpJK4xQdNx
-0O9FAoIBAQCz2AGGKemHswdEwveEkuaSWpA3Bekj7lYkmTchHH9EU7JyAkx3qhDD
-QXB2hxGXawf1tsqAmypQwiJ+gGeCz6mW9UkGRF1DX9XX4yc2I5rew2a4RXAxc/Xz
-pP70i8O5I43Wn7FEusOyY2aAis1Y/eb4EQ+56QTAw5wXa3DwidRbCIJ2XDnT6oRy
-CWUnAYMG7ek/9TB2Wq5OWCn2B5S79IdmZsLZb+5qbMT3u1xcwO1Xy8jJc27IGpv6
-ZsDqCTV1/aJ+XQnWpBg28tiV3Sle6pjUzTRJh5AhWcEZRbKMSOiJI/CBY4k2Qq6t
-xuuEdgFjL7T+mTepqehUglcyiPuLEtAhAoIBAQDDQ5pTFOlunfYzm+CIvvImAgy7
-vrEabJYABATytAbXRMMbrKoEdU2ApEDyEW7PgysDnYLAZ+icObnJlBTYvNdlWFly
-XiviGVfpjFWDT9U/gUwFQu2lfEjoiivoGS92USHUy4UMVHiiznnm20VLLkgd3xna
-HUNSDdHIEgzOTmDsKJwMfA9zGckx23JJimPR5ekv6vr6QllYtECs4lTC1gVQAp2f
-5daxHRbkmO6gw1RgQADLkAnYz3aI1jNuHm5VyAZGt/d3JCtZ3Wwwejll8uJ4J09G
-oEtqyY9RVeHK50bLO4lyAXFiE+J6qqXjsGC20cpxeZYW5llMY/dhA6WV4YXV
------END RSA PRIVATE KEY-----
diff --git a/apex/sdkextensions/com.android.sdkext.pk8 b/apex/sdkextensions/com.android.sdkext.pk8
deleted file mode 100644
index ccc0bf4..0000000
--- a/apex/sdkextensions/com.android.sdkext.pk8
+++ /dev/null
Binary files differ
diff --git a/apex/sdkextensions/com.android.sdkext.x509.pem b/apex/sdkextensions/com.android.sdkext.x509.pem
deleted file mode 100644
index 45d2ade..0000000
--- a/apex/sdkextensions/com.android.sdkext.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGIzCCBAugAwIBAgIUXuDL7QvzQh7S6rihWz2KRvCFVT0wDQYJKoZIhvcNAQEL
-BQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
-DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
-b2lkMRswGQYDVQQDDBJjb20uYW5kcm9pZC5zZGtleHQxIjAgBgkqhkiG9w0BCQEW
-E2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjAyMTQyNDM0WhgPNDc1NzEwMjgx
-NDI0MzRaMIGfMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
-A1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwH
-QW5kcm9pZDEbMBkGA1UEAwwSY29tLmFuZHJvaWQuc2RrZXh0MSIwIAYJKoZIhvcN
-AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
-MIICCgKCAgEAxFvZZ6ES1oqAu1K74/ZxnC3SOhHnLISLBgJEe7DqtdpuNFAwvdVO
-RL/HULhDbjYlOhpU2x3SavDIZZ2lRfiS9Q+M25WftxTRHVjBcpgwbV77TVxPKlAa
-tVN2lUVOY+s4QAVMNIXjC4kCKK/pCQtacH715EtdV47fWdg/Nx4iP/Aord8k3KGI
-9iI2ZOUjaugTRxu5lKRNDrv0bw5rEzyYmDyMud+kR/iS3/5oog57wPE0ffAkZXWE
-p3L2Cejre3ekCizsvVh6EmH6ForKLtL6f0z5Zir1f4R9+YcENspTlJR3pDhg7y3I
-uTQT/iDCtV0l+g2PjGZPEeAQHND3+kDQR7Sno/WC1Nhws6vcu1MdrC+kIh1ewx4y
-8moy/yqb5M98PJDzTSi/AOTB/OiqLXo/T8rjLBmirs9y3fTT6gJ6qXxOWgt8dle9
-7TBfa84Xi8uVY61c+A+YI0nLal7QDPsP3RPv5sJSQ9x9YnweVfD9Q0EOi52sSNu+
-QuN/kcUrMgPgha20VhfH/CkkPDyIp6aZyHHM69MIl+cYEm8vPa5uy3dosuRomT0f
-I4HOBjULDIuj+rIi+Rg3qHvmpuejwZXI/FBNWIhLEUG3ytgksjMaBoYAYflGdrcj
-BQexuF3EO+j4uo7JGjNcaT7wRoCH9gt29VHckDg2qz6VWXrlpmME4UkCAwEAAaNT
-MFEwHQYDVR0OBBYEFISN2nmUHllgPZMZ62U7mU3ZxzlXMB8GA1UdIwQYMBaAFISN
-2nmUHllgPZMZ62U7mU3ZxzlXMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
-BQADggIBAFHIwyBNIVyHXUsDUdcjxfojXQsF/BCL9ehE3pgdkvDfQanaIREWn0nc
-oCFDFkYMRqaXOGC5TKq4OCjXOLsdfODt8HQ3F9J1B0ghQ5tfOdw7xDugNAszqP/Q
-h7kpvqLTycjrqOeZ5KjxEEYtP/KlUmALgOKcTcSH+XhWyxhjF4j24T9F2yJRr3/A
-r1NGU/djH953bHKC8OpJ2teUpDLA4TxVp/EhslH2eVigF80c/w74QPLEWkD9zv/4
-YeRg/R5N83zHs99NtlWMIeHfK6fUbzMyaSZtvm+jK20tkByQb/OQRed+drk25MtL
-68IRvxqri367qRScdpTZbu0ByLO4X8gFdubRUWr+tcO4pZX+DJRVriExbOkU2xhS
-Vtslq23V/hwTuUNm1CXjR70mPS13BTmHrIQDqLoIw/WTQlGh+vxnlAFRIHM3KB2c
-OdzUBu+NcB4aZEd0KKtct600A0DKPr1MQPb5jDq9wEtPSQYwMF0nRFNnXltbrXMd
-4hhwnhKr74fVMUmb+7eQP56XE/Nk4D+npMO54vv1pav+DI2/nxCND9BOLBweY38p
-Tvd2RjesMok0zXuVXiCIu4GEpwo7WkSnv25xrb0Ey2M8QWnGNnCcX7Kv6ip3RdWy
-HiN0G8RJrs/yNEVSDRx8ZhtwTpXVPQxbARbmhNF4/fnolElkmrMP
------END CERTIFICATE-----
diff --git a/apex/sdkextensions/derive_sdk/Android.bp b/apex/sdkextensions/derive_sdk/Android.bp
deleted file mode 100644
index 41eae09..0000000
--- a/apex/sdkextensions/derive_sdk/Android.bp
+++ /dev/null
@@ -1,55 +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.
-
-cc_defaults {
-    name: "derive_sdk-defaults",
-    srcs: [
-        "derive_sdk.cpp",
-        "sdk.proto",
-    ],
-    proto: {
-        type: "lite",
-        static: true,
-    },
-    min_sdk_version: "current",
-    shared_libs: ["liblog"],
-    // static c++/libbase for smaller size
-    stl: "c++_static",
-    static_libs: ["libbase"],
-}
-
-cc_binary {
-    name: "derive_sdk",
-    defaults: [ "derive_sdk-defaults" ],
-    apex_available: [ "com.android.sdkext" ],
-    visibility: [ "//frameworks/base/apex/sdkextensions" ]
-}
-
-// Work around testing using a 64-bit test suite on 32-bit test device by
-// using a prefer32 version of derive_sdk in testing.
-cc_binary {
-    name: "derive_sdk_prefer32",
-    defaults: [ "derive_sdk-defaults" ],
-    compile_multilib: "prefer32",
-    stem: "derive_sdk",
-    apex_available: [ "test_com.android.sdkext" ],
-    visibility: [ "//frameworks/base/apex/sdkextensions/testing" ],
-    installable: false,
-}
-
-prebuilt_etc {
-    name: "derive_sdk.rc",
-    src: "derive_sdk.rc",
-    installable: false,
-}
diff --git a/apex/sdkextensions/derive_sdk/derive_sdk.cpp b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
deleted file mode 100644
index 900193a..0000000
--- a/apex/sdkextensions/derive_sdk/derive_sdk.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "derive_sdk"
-
-#include <algorithm>
-#include <dirent.h>
-#include <iostream>
-#include <sys/stat.h>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-
-#include "frameworks/base/apex/sdkextensions/derive_sdk/sdk.pb.h"
-
-using com::android::sdkext::proto::SdkVersion;
-
-int main(int, char**) {
-    std::unique_ptr<DIR, decltype(&closedir)> apex(opendir("/apex"), closedir);
-    if (!apex) {
-        LOG(ERROR) << "Could not read /apex";
-        return EXIT_FAILURE;
-    }
-    struct dirent* de;
-    std::vector<std::string> paths;
-    while ((de = readdir(apex.get()))) {
-        std::string name = de->d_name;
-        if (name[0] == '.' || name.find('@') != std::string::npos) {
-            // Skip <name>@<ver> dirs, as they are bind-mounted to <name>
-            continue;
-        }
-        std::string path = "/apex/" + name + "/etc/sdkinfo.binarypb";
-        struct stat statbuf;
-        if (stat(path.c_str(), &statbuf) == 0) {
-            paths.push_back(path);
-        }
-    }
-
-    std::vector<int> versions;
-    for (const auto& path : paths) {
-        std::string contents;
-        if (!android::base::ReadFileToString(path, &contents, true)) {
-            LOG(ERROR) << "failed to read " << path;
-            continue;
-        }
-        SdkVersion sdk_version;
-        if (!sdk_version.ParseFromString(contents)) {
-            LOG(ERROR) << "failed to parse " << path;
-            continue;
-        }
-        LOG(INFO) << "Read version " << sdk_version.version() << " from " << path;
-        versions.push_back(sdk_version.version());
-    }
-    auto itr = std::min_element(versions.begin(), versions.end());
-    std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
-
-    if (!android::base::SetProperty("build.version.extensions.r", prop_value)) {
-        LOG(ERROR) << "failed to set sdk_info prop";
-        return EXIT_FAILURE;
-    }
-
-    LOG(INFO) << "R extension version is " << prop_value;
-    return EXIT_SUCCESS;
-}
diff --git a/apex/sdkextensions/derive_sdk/derive_sdk.rc b/apex/sdkextensions/derive_sdk/derive_sdk.rc
deleted file mode 100644
index 18f021c..0000000
--- a/apex/sdkextensions/derive_sdk/derive_sdk.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service derive_sdk /apex/com.android.sdkext/bin/derive_sdk
-    user nobody
-    group nobody
-    oneshot
-    disabled
diff --git a/apex/sdkextensions/derive_sdk/sdk.proto b/apex/sdkextensions/derive_sdk/sdk.proto
deleted file mode 100644
index d15b935..0000000
--- a/apex/sdkextensions/derive_sdk/sdk.proto
+++ /dev/null
@@ -1,25 +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.
- */
-
-syntax = "proto3";
-package com.android.sdkext.proto;
-
-option java_outer_classname = "SdkProto";
-option optimize_for = LITE_RUNTIME;
-
-message SdkVersion {
-  int32 version = 1;
-}
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
deleted file mode 100644
index b8aad7d..0000000
--- a/apex/sdkextensions/framework/Android.bp
+++ /dev/null
@@ -1,49 +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 {
-    default_visibility: [ ":__pkg__" ]
-}
-
-filegroup {
-    name: "framework-sdkextensions-sources",
-    srcs: [
-        "java/**/*.java",
-    ],
-    path: "java",
-    visibility: [ "//frameworks/base" ] // For the "global" stubs.
-}
-
-java_sdk_library {
-    name: "framework-sdkextensions",
-    srcs: [ ":framework-sdkextensions-sources" ],
-    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,
-    visibility: [
-        "//frameworks/base/apex/sdkextensions",
-        "//frameworks/base/apex/sdkextensions/testing",
-    ],
-    hostdex: true, // for hiddenapi check
-    apex_available: [
-        "com.android.sdkext",
-        "test_com.android.sdkext",
-    ],
-}
diff --git a/apex/sdkextensions/framework/api/current.txt b/apex/sdkextensions/framework/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/module-lib-current.txt b/apex/sdkextensions/framework/api/module-lib-current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/module-lib-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/module-lib-removed.txt b/apex/sdkextensions/framework/api/module-lib-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/module-lib-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/removed.txt b/apex/sdkextensions/framework/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/sdkextensions/framework/api/system-current.txt b/apex/sdkextensions/framework/api/system-current.txt
deleted file mode 100644
index bbff4c5..0000000
--- a/apex/sdkextensions/framework/api/system-current.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-// 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
deleted file mode 100644
index d802177..0000000
--- a/apex/sdkextensions/framework/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// 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
deleted file mode 100644
index c268ff4..0000000
--- a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.ext;
-
-import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.os.Build.VERSION_CODES;
-import android.os.SystemProperties;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Methods for interacting with the extension SDK.
- *
- * This class provides information about the extension SDK version present
- * on this device. Use the {@link #getExtensionVersion(int) getExtension} to
- * query for the extension version for the given SDK version.
-
- * @hide
- */
-@SystemApi
-public class SdkExtensions {
-
-    private static final int R_EXTENSION_INT;
-    static {
-        R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
-    }
-
-    /**
-     * Values suitable as parameters for {@link #getExtensionVersion(int)}.
-     * @hide
-     */
-    @IntDef(value = { VERSION_CODES.R })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SdkVersion {}
-
-    private SdkExtensions() { }
-
-    /**
-     * Return the version of the extension to the given SDK.
-     *
-     * @param sdk the SDK version to get the extension version of.
-     * @see SdkVersion
-     * @throws IllegalArgumentException if sdk is not an sdk version with extensions
-     */
-    public static int getExtensionVersion(@SdkVersion int sdk) {
-        if (sdk < VERSION_CODES.R) {
-            throw new IllegalArgumentException(String.valueOf(sdk) + " does not have extensions");
-        }
-
-        if (sdk == VERSION_CODES.R) {
-            return R_EXTENSION_INT;
-        }
-        return 0;
-    }
-
-}
diff --git a/apex/sdkextensions/framework/java/android/os/ext/package.html b/apex/sdkextensions/framework/java/android/os/ext/package.html
deleted file mode 100644
index 34c1697..0000000
--- a/apex/sdkextensions/framework/java/android/os/ext/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-Provides APIs to interface with the SDK extensions.
-</BODY>
-</HTML>
diff --git a/apex/sdkextensions/gen_sdkinfo.py b/apex/sdkextensions/gen_sdkinfo.py
deleted file mode 100644
index 5af478b..0000000
--- a/apex/sdkextensions/gen_sdkinfo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import sdk_pb2
-import sys
-
-if __name__ == '__main__':
-  argv = sys.argv[1:]
-  if not len(argv) == 4 or sorted([argv[0], argv[2]]) != ['-o', '-v']:
-    print('usage: gen_sdkinfo -v <version> -o <output-file>')
-    sys.exit(1)
-
-  for i in range(len(argv)):
-    if sys.argv[i] == '-o':
-      filename = sys.argv[i+1]
-    if sys.argv[i] == '-v':
-      version = int(sys.argv[i+1])
-
-  proto = sdk_pb2.SdkVersion()
-  proto.version = version
-  with open(filename, 'wb') as f:
-    f.write(proto.SerializeToString())
diff --git a/apex/sdkextensions/manifest.json b/apex/sdkextensions/manifest.json
deleted file mode 100644
index 048f5c4..0000000
--- a/apex/sdkextensions/manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "com.android.sdkext",
-  "version": 1
-}
diff --git a/apex/sdkextensions/sdk.proto b/apex/sdkextensions/sdk.proto
deleted file mode 100644
index d15b935..0000000
--- a/apex/sdkextensions/sdk.proto
+++ /dev/null
@@ -1,25 +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.
- */
-
-syntax = "proto3";
-package com.android.sdkext.proto;
-
-option java_outer_classname = "SdkProto";
-option optimize_for = LITE_RUNTIME;
-
-message SdkVersion {
-  int32 version = 1;
-}
diff --git a/apex/sdkextensions/testing/Android.bp b/apex/sdkextensions/testing/Android.bp
deleted file mode 100644
index f2f5b32..0000000
--- a/apex/sdkextensions/testing/Android.bp
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-apex_test {
-    name: "test_com.android.sdkext",
-    visibility: [ "//system/apex/tests" ],
-    defaults: ["com.android.sdkext-defaults"],
-    manifest: "test_manifest.json",
-    prebuilts: [ "sdkinfo_45" ],
-    file_contexts: ":com.android.sdkext-file_contexts",
-    installable: false, // Should never be installed on the systemimage
-    multilib: {
-        prefer32: {
-            binaries: ["derive_sdk_prefer32"],
-        },
-    },
-    // The automated test infra ends up building this apex for 64+32-bit and
-    // then installs it on a 32-bit-only device. Work around this weirdness
-    // by preferring 32-bit.
-    compile_multilib: "prefer32",
-}
-
-genrule {
-    name: "sdkinfo_45_src",
-    out: [ "sdkinfo.binarypb" ],
-    tools: [ "gen_sdkinfo" ],
-    cmd: "$(location) -v 45 -o $(out)",
-}
-
-prebuilt_etc {
-    name: "sdkinfo_45",
-    src: ":sdkinfo_45_src",
-    filename: "sdkinfo.binarypb",
-    installable: false,
-}
diff --git a/apex/sdkextensions/testing/test_manifest.json b/apex/sdkextensions/testing/test_manifest.json
deleted file mode 100644
index 1b4a2b0..0000000
--- a/apex/sdkextensions/testing/test_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "com.android.sdkext",
-  "version": 2147483647
-}
diff --git a/api/system-current.txt b/api/system-current.txt
index 4a1bf0d..26153ca 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6572,6 +6572,7 @@
     field public static final String NAMESPACE_APP_COMPAT = "app_compat";
     field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
     field public static final String NAMESPACE_AUTOFILL = "autofill";
+    field public static final String NAMESPACE_BLUETOOTH = "bluetooth";
     field public static final String NAMESPACE_CONNECTIVITY = "connectivity";
     field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
     field public static final String NAMESPACE_DEX_BOOT = "dex_boot";
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 306b8af..ffde094 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -144,6 +144,13 @@
 
 
 NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
+
+
+
+NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED:
+    New setting keys are not allowed. (Field: TETHER_OFFLOAD_DISABLED)
+NoSettingsProvider: android.provider.Settings.Global#TETHER_SUPPORTED:
+    New setting keys are not allowed. (Field: TETHER_SUPPORTED)
     
 
 
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 46f3aeb..47d87c50 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -2298,6 +2298,75 @@
 NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
     
 NoClone: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0:
+
+
+
+NoSettingsProvider: android.provider.Settings.Global#AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
+    New setting keys are not allowed. (Field: AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES)
+NoSettingsProvider: android.provider.Settings.Global#AUTOMATIC_POWER_SAVE_MODE:
+    New setting keys are not allowed. (Field: AUTOMATIC_POWER_SAVE_MODE)
+NoSettingsProvider: android.provider.Settings.Global#BATTERY_SAVER_CONSTANTS:
+    New setting keys are not allowed. (Field: BATTERY_SAVER_CONSTANTS)
+NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD:
+    New setting keys are not allowed. (Field: DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD)
+NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_ENABLED:
+    New setting keys are not allowed. (Field: DYNAMIC_POWER_SAVINGS_ENABLED)
+NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS:
+    New setting keys are not allowed. (Field: HIDDEN_API_BLACKLIST_EXEMPTIONS)
+NoSettingsProvider: android.provider.Settings.Global#LOCATION_GLOBAL_KILL_SWITCH:
+    New setting keys are not allowed. (Field: LOCATION_GLOBAL_KILL_SWITCH)
+NoSettingsProvider: android.provider.Settings.Global#LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST:
+    New setting keys are not allowed. (Field: LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST)
+NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE:
+    New setting keys are not allowed. (Field: LOW_POWER_MODE)
+NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE_STICKY:
+    New setting keys are not allowed. (Field: LOW_POWER_MODE_STICKY)
+NoSettingsProvider: android.provider.Settings.Global#NOTIFICATION_BUBBLES:
+    New setting keys are not allowed. (Field: NOTIFICATION_BUBBLES)
+NoSettingsProvider: android.provider.Settings.Global#OVERLAY_DISPLAY_DEVICES:
+    New setting keys are not allowed. (Field: OVERLAY_DISPLAY_DEVICES)
+NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED:
+    New setting keys are not allowed. (Field: TETHER_OFFLOAD_DISABLED)
+NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE:
+    New setting keys are not allowed. (Field: USE_OPEN_WIFI_PACKAGE)
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
+    New setting keys are not allowed. (Field: ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED)
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
+    New setting keys are not allowed. (Field: ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_FEATURE_FIELD_CLASSIFICATION:
+    New setting keys are not allowed. (Field: AUTOFILL_FEATURE_FIELD_CLASSIFICATION)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_SERVICE:
+    New setting keys are not allowed. (Field: AUTOFILL_SERVICE)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT:
+    New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE:
+    New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE:
+    New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_VALUE_LENGTH:
+    New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MAX_VALUE_LENGTH)
+NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MIN_VALUE_LENGTH:
+    New setting keys are not allowed. (Field: AUTOFILL_USER_DATA_MIN_VALUE_LENGTH)
+NoSettingsProvider: android.provider.Settings.Secure#CONTENT_CAPTURE_ENABLED:
+    New setting keys are not allowed. (Field: CONTENT_CAPTURE_ENABLED)
+NoSettingsProvider: android.provider.Settings.Secure#DISABLED_PRINT_SERVICES:
+    New setting keys are not allowed. (Field: DISABLED_PRINT_SERVICES)
+NoSettingsProvider: android.provider.Settings.Secure#DOZE_ALWAYS_ON:
+    New setting keys are not allowed. (Field: DOZE_ALWAYS_ON)
+NoSettingsProvider: android.provider.Settings.Secure#ENABLED_VR_LISTENERS:
+    New setting keys are not allowed. (Field: ENABLED_VR_LISTENERS)
+NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_DELAY_MILLIS:
+    New setting keys are not allowed. (Field: LOCATION_ACCESS_CHECK_DELAY_MILLIS)
+NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_INTERVAL_MILLIS:
+    New setting keys are not allowed. (Field: LOCATION_ACCESS_CHECK_INTERVAL_MILLIS)
+NoSettingsProvider: android.provider.Settings.Secure#NOTIFICATION_BADGING:
+    New setting keys are not allowed. (Field: NOTIFICATION_BADGING)
+NoSettingsProvider: android.provider.Settings.Secure#SYNC_PARENT_SOUNDS:
+    New setting keys are not allowed. (Field: SYNC_PARENT_SOUNDS)
+NoSettingsProvider: android.provider.Settings.Secure#USER_SETUP_COMPLETE:
+    New setting keys are not allowed. (Field: USER_SETUP_COMPLETE)
+NoSettingsProvider: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
+    New setting keys are not allowed. (Field: VOICE_INTERACTION_SERVICE)
     
 
 
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/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 407ff04..c8ca618e 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -25,6 +25,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.content.pm.PackageManager;
 import android.os.Process;
 import android.security.Credentials;
 import android.security.KeyStore;
@@ -647,6 +649,7 @@
          * @param serverAddr the server that the VPN should connect to
          * @param identity the identity string to be used for IKEv2 authentication
          */
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder(@NonNull String serverAddr, @NonNull String identity) {
             checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "serverAddr");
             checkNotNull(identity, MISSING_PARAM_MSG_TMPL, "identity");
@@ -680,6 +683,7 @@
          *     unrecognized format
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setAuthUsernamePassword(
                 @NonNull String user,
                 @NonNull String pass,
@@ -715,6 +719,7 @@
          *     unrecognized format
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setAuthDigitalSignature(
                 @NonNull X509Certificate userCert,
                 @NonNull PrivateKey key,
@@ -745,6 +750,7 @@
          * @return this {@link Builder} object to facilitate chaining of method calls
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setAuthPsk(@NonNull byte[] psk) {
             checkNotNull(psk, MISSING_PARAM_MSG_TMPL, "psk");
 
@@ -768,6 +774,7 @@
          * @return this {@link Builder} object to facilitate chaining of method calls
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setBypassable(boolean isBypassable) {
             mIsBypassable = isBypassable;
             return this;
@@ -782,6 +789,7 @@
          * @return this {@link Builder} object to facilitate chaining of method calls
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setProxy(@Nullable ProxyInfo proxy) {
             mProxyInfo = proxy;
             return this;
@@ -798,6 +806,7 @@
          * @throws IllegalArgumentException if the value is not at least the minimum IPv6 MTU (1280)
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setMaxMtu(int mtu) {
             // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
             // networks, the VPN must provide a link fulfilling the stricter of the two conditions
@@ -825,6 +834,7 @@
          * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setMetered(boolean isMetered) {
             mIsMetered = isMetered;
             return this;
@@ -852,6 +862,7 @@
          * @see IpSecAlgorithm
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
             checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
             validateAllowedAlgorithms(algorithmNames);
@@ -870,6 +881,7 @@
          * @hide
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Builder restrictToTestNetworks() {
             mIsRestrictedToTestNetworks = true;
             return this;
@@ -881,6 +893,7 @@
          * @throws IllegalArgumentException if any of the required keys or values were invalid
          */
         @NonNull
+        @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
         public Ikev2VpnProfile build() {
             return new Ikev2VpnProfile(
                     mType,
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 57d5d03..0fbffba 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -679,13 +679,14 @@
      */
     public void restrictCapabilitesForTestNetwork(int creatorUid) {
         final long originalCapabilities = mNetworkCapabilities;
+        final long originalTransportTypes = mTransportTypes;
         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);
+        mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
+                | (1 << TRANSPORT_TEST);
         mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
         mNetworkSpecifier = originalSpecifier;
         mSignalStrength = originalSignalStrength;
@@ -787,6 +788,13 @@
     };
 
     /**
+     * Allowed transports on a test network, in addition to TRANSPORT_TEST.
+     */
+    private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
+            // Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
+            | 1 << TRANSPORT_ETHERNET;
+
+    /**
      * Adds the given transport type to this {@code NetworkCapability} instance.
      * 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.
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index b7fb280..34e48eb 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -16,8 +16,6 @@
 
 package android.net;
 
-import static android.os.Process.CLAT_UID;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1047,73 +1045,54 @@
     }
 
     /**
-     * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
+     * Calculate and apply adjustments to captured statistics for 464xlat traffic.
      *
-     * <p>This mutates both base and stacked traffic stats, to account respectively for
-     * double-counted traffic and IPv4/IPv6 header size difference.
+     * <p>This mutates stacked traffic stats, to account for IPv4/IPv6 header size difference.
      *
-     * <p>For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
-     * packet on the stacked interface, and once as translated to an IPv6 packet on the
-     * base interface. For correct stats accounting on the base interface, if using xt_qtaguid,
-     * every rx 464xlat packet needs to be subtracted from the root UID on the base interface
-     * (http://b/12249687, http:/b/33681750), and every tx 464xlat packet which was counted onto
-     * clat uid should be ignored.
+     * <p>UID stats, which are only accounted on the stacked interface, need to be increased
+     * by 20 bytes/packet to account for translation overhead.
      *
-     * As for eBPF, the per uid stats is collected by different hook, the rx packets on base
-     * interface will not be counted. Thus, the adjustment on root uid is not needed. However, the
-     * tx traffic counted in the same way xt_qtaguid does, so the traffic on clat uid still
-     * needs to be ignored.
+     * <p>The potential additional overhead of 8 bytes/packet for ip fragments is ignored.
+     *
+     * <p>Interface stats need to sum traffic on both stacked and base interface because:
+     *   - eBPF offloaded packets appear only on the stacked interface
+     *   - Non-offloaded ingress packets appear only on the stacked interface
+     *     (due to iptables raw PREROUTING drop rules)
+     *   - Non-offloaded egress packets appear only on the stacked interface
+     *     (due to ignoring traffic from clat daemon by uid match)
+     * (and of course the 20 bytes/packet overhead needs to be applied to stacked interface stats)
      *
      * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
      * {@code ConcurrentHashMap}
      * @param baseTraffic Traffic on the base interfaces. Will be mutated.
      * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
-     * @param useBpfStats True if eBPF is in use.
      * @hide
      */
     public static void apply464xlatAdjustments(NetworkStats baseTraffic,
-            NetworkStats stackedTraffic, Map<String, String> stackedIfaces, boolean useBpfStats) {
-        // Total 464xlat traffic to subtract from uid 0 on all base interfaces.
-        // stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
-        final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
-
+            NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
         // For recycling
         Entry entry = null;
-        Entry adjust = new NetworkStats.Entry(IFACE_ALL, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
-
         for (int i = 0; i < stackedTraffic.size; i++) {
             entry = stackedTraffic.getValues(i, entry);
-            if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
-                continue;
-            }
-            final String baseIface = stackedIfaces.get(entry.iface);
-            if (baseIface == null) {
-                continue;
-            }
-            // Subtract xt_qtaguid 464lat rx traffic seen for the root UID on the current base
-            // interface. As for eBPF, the per uid stats is collected by different hook, the rx
-            // packets on base interface will not be counted.
-            adjust.iface = baseIface;
-            if (!useBpfStats) {
-                adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
-                adjust.rxPackets = -entry.rxPackets;
-            }
-            adjustments.combineValues(adjust);
+            if (entry == null) continue;
+            if (entry.iface == null) continue;
+            if (!entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) continue;
 
             // For 464xlat traffic, per uid stats only counts the bytes of the native IPv4 packet
             // sent on the stacked interface with prefix "v4-" and drops the IPv6 header size after
             // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
             // difference for all packets (http://b/12249687, http:/b/33681750).
+            //
+            // Note: this doesn't account for LRO/GRO/GSO/TSO (ie. >mtu) traffic correctly, nor
+            // does it correctly account for the 8 extra bytes in the IPv6 fragmentation header.
+            //
+            // While the ebpf code path does try to simulate proper post segmentation packet
+            // counts, we have nothing of the sort of xt_qtaguid stats.
             entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
             entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA;
             stackedTraffic.setValues(i, entry);
         }
-
-        // Traffic on clat uid is v6 tx traffic that is already counted with app uid on the stacked
-        // v4 interface, so it needs to be removed to avoid double-counting.
-        baseTraffic.removeUids(new int[] {CLAT_UID});
-        baseTraffic.combineAllValues(adjustments);
     }
 
     /**
@@ -1125,8 +1104,8 @@
      * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
      * @hide
      */
-    public void apply464xlatAdjustments(Map<String, String> stackedIfaces, boolean useBpfStats) {
-        apply464xlatAdjustments(this, this, stackedIfaces, useBpfStats);
+    public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
+        apply464xlatAdjustments(this, this, stackedIfaces);
     }
 
     /**
diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java
new file mode 100644
index 0000000..c37a2ff
--- /dev/null
+++ b/core/java/android/os/ParcelableHolder.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.MathUtils;
+
+/**
+ * Parcelable containing the other Parcelable object.
+ * @hide
+ */
+public final class ParcelableHolder implements Parcelable {
+    /**
+     * This is set by {@link #setParcelable}.
+     * {@link #mParcelable} and {@link #mParcel} are mutually exclusive
+     * if {@link ParcelableHolder} contains value, otherwise, both are null.
+     */
+    private Parcelable mParcelable;
+    /**
+     * This is set by {@link #readFromParcel}.
+     * {@link #mParcelable} and {@link #mParcel} are mutually exclusive
+     * if {@link ParcelableHolder} contains value, otherwise, both are null.
+     */
+    private Parcel mParcel;
+    private boolean mIsStable = false;
+
+    public ParcelableHolder(boolean isStable) {
+        mIsStable = isStable;
+    }
+
+    private ParcelableHolder() {
+
+    }
+
+    /**
+     * {@link ParcelableHolder}'s stability is determined by the parcelable
+     * which contains this ParcelableHolder.
+     * For more detail refer to {@link Parcelable#isStable}.
+     */
+    @Override
+    public boolean isStable() {
+        return mIsStable;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ParcelableHolder> CREATOR =
+            new Parcelable.Creator<ParcelableHolder>() {
+                @NonNull
+                @Override
+                public ParcelableHolder createFromParcel(@NonNull Parcel parcel) {
+                    ParcelableHolder parcelable = new ParcelableHolder();
+                    parcelable.readFromParcel(parcel);
+                    return parcelable;
+                }
+
+                @NonNull
+                @Override
+                public ParcelableHolder[] newArray(int size) {
+                    return new ParcelableHolder[size];
+                }
+            };
+
+
+    /**
+     * Write a parcelable into ParcelableHolder, the previous parcelable will be removed.
+     * @return {@code false} if the parcelable's stability is more unstable ParcelableHolder.
+     */
+    public synchronized boolean setParcelable(@Nullable Parcelable p) {
+        if (p != null && this.isStable() && !p.isStable()) {
+            return false;
+        }
+        mParcelable = p;
+        if (mParcel != null) {
+            mParcel.recycle();
+            mParcel = null;
+        }
+        return true;
+    }
+
+    /**
+     * @return the parcelable that was written by {@link #setParcelable} or {@link #readFromParcel},
+     *         or {@code null} if the parcelable has not been written, or T is different from
+     *         the type written by (@link #setParcelable}.
+     */
+    @Nullable
+    public synchronized <T extends Parcelable> T getParcelable(@NonNull Class<T> clazz) {
+        if (mParcel == null) {
+            if (!clazz.isInstance(mParcelable)) {
+                return null;
+            }
+            return (T) mParcelable;
+        }
+
+        mParcel.setDataPosition(0);
+
+        T parcelable = mParcel.readParcelable(clazz.getClassLoader());
+        if (!clazz.isInstance(parcelable)) {
+            return null;
+        }
+        mParcelable = parcelable;
+
+        mParcel.recycle();
+        mParcel = null;
+        return parcelable;
+    }
+
+    /**
+     * Read ParcelableHolder from a parcel.
+     */
+    public synchronized void readFromParcel(@NonNull Parcel parcel) {
+        this.mIsStable = parcel.readBoolean();
+
+        mParcelable = null;
+
+        if (mParcel == null) {
+            mParcel = Parcel.obtain();
+        }
+        mParcel.setDataPosition(0);
+        mParcel.setDataSize(0);
+
+        int dataSize = parcel.readInt();
+        if (dataSize < 0) {
+            throw new IllegalArgumentException("dataSize from parcel is negative");
+        }
+        int dataStartPos = parcel.dataPosition();
+
+        mParcel.appendFrom(parcel, dataStartPos, dataSize);
+        parcel.setDataPosition(MathUtils.addOrThrow(dataStartPos, dataSize));
+    }
+
+    @Override
+    public synchronized void writeToParcel(@NonNull Parcel parcel, int flags) {
+        parcel.writeBoolean(this.mIsStable);
+
+        if (mParcel != null) {
+            parcel.writeInt(mParcel.dataSize());
+            parcel.appendFrom(mParcel, 0, mParcel.dataSize());
+            return;
+        }
+
+        int sizePos = parcel.dataPosition();
+        parcel.writeInt(0);
+        int dataStartPos = parcel.dataPosition();
+        parcel.writeParcelable(mParcelable, 0);
+        int dataSize = parcel.dataPosition() - dataStartPos;
+
+        parcel.setDataPosition(sizePos);
+        parcel.writeInt(dataSize);
+        parcel.setDataPosition(MathUtils.addOrThrow(parcel.dataPosition(), dataSize));
+    }
+
+    @Override
+    public synchronized int describeContents() {
+        if (mParcel != null) {
+            return mParcel.hasFileDescriptors() ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
+        }
+        if (mParcelable != null) {
+            return mParcelable.describeContents();
+        }
+        return 0;
+    }
+}
diff --git a/core/java/android/os/RegistrantList.java b/core/java/android/os/RegistrantList.java
index 53e0ae4..b36734b 100644
--- a/core/java/android/os/RegistrantList.java
+++ b/core/java/android/os/RegistrantList.java
@@ -66,6 +66,10 @@
         }
     }
 
+    public synchronized void removeAll() {
+        registrants.clear();
+    }
+
     @UnsupportedAppUsage
     public synchronized int
     size()
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index c837b93..5e1b223 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -110,6 +110,14 @@
     public static final String NAMESPACE_AUTOFILL = "autofill";
 
     /**
+     * Namespace for all Bluetooth related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_BLUETOOTH = "bluetooth";
+
+    /**
      * Namespace for all networking connectivity related features.
      *
      * @hide
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/view/textclassifier/OWNERS b/core/java/android/view/textclassifier/OWNERS
index 893a083..be4fbaa 100644
--- a/core/java/android/view/textclassifier/OWNERS
+++ b/core/java/android/view/textclassifier/OWNERS
@@ -7,4 +7,8 @@
 joannechung@google.com
 svetoslavganov@google.com
 eugeniom@google.com
-samsellem@google.com
\ No newline at end of file
+samsellem@google.com
+adamhe@google.com
+augale@google.com
+lpeter@google.com
+tymtsai@google.com
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 741a65e..2e15212 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -367,11 +367,17 @@
                 null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/);
         hidlManager.addDependency(hidlBase);
 
+        SharedLibraryInfo androidTestBase = new SharedLibraryInfo(
+                "/system/framework/android.test.base.jar", null /*packageName*/,
+                null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN,
+                null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/);
+
         ApplicationLoaders.getDefault().createAndCacheNonBootclasspathSystemClassLoaders(
                 new SharedLibraryInfo[]{
                     // ordered dependencies first
                     hidlBase,
                     hidlManager,
+                    androidTestBase,
                 });
     }
 
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 9bc4adc..27415c5 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -231,7 +231,10 @@
         "system/media/private/camera/include",
     ],
 
-    header_libs: ["bionic_libc_platform_headers"],
+    header_libs: [
+        "bionic_libc_platform_headers",
+        "dnsproxyd_protocol_headers",
+    ],
 
     static_libs: [
         "libasync_safe",
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index ba7fe7f..03b9793 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -27,12 +27,13 @@
 #include <netinet/ip.h>
 #include <netinet/udp.h>
 
+#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
 #include <android_runtime/AndroidRuntime.h>
 #include <cutils/properties.h>
-#include <utils/misc.h>
-#include <utils/Log.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
 
 #include "NetdClient.h"
 #include "core_jni_helpers.h"
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index d723ecc..eb8d26e 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -475,7 +475,11 @@
         if (fd < 0) return NULL;
         fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
         if (fd < 0) return NULL;
-        return jniCreateFileDescriptor(env, fd);
+        jobject jifd = jniCreateFileDescriptor(env, fd);
+        if (jifd == NULL) {
+            close(fd);
+        }
+        return jifd;
     }
     return NULL;
 }
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
index c33405d..a812c35 100644
--- a/core/jni/android_os_SharedMemory.cpp
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -69,7 +69,11 @@
         return nullptr;
     }
 
-    return jniCreateFileDescriptor(env, fd);
+    jobject jifd = jniCreateFileDescriptor(env, fd);
+    if (jifd == nullptr) {
+        close(fd);
+    }
+    return jifd;
 }
 
 jint SharedMemory_nGetSize(JNIEnv* env, jobject, jobject fileDescriptor) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b2aef78..48a764e 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -483,11 +483,8 @@
 
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
 
-    std::vector<uint8_t> byteData(parcel->dataSize());
-    memcpy(byteData.data(), parcel->data(), parcel->dataSize());
-
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
-    transaction->setMetadata(ctrl, id, std::move(byteData));
+    transaction->setMetadata(ctrl, id, *parcel);
 }
 
 static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1a2dfce..2fb281c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4584,12 +4584,12 @@
     <!-- @SystemApi Allows to access all app shortcuts.
          @hide -->
     <permission android:name="android.permission.ACCESS_SHORTCUTS"
-        android:protectionLevel="signature|textClassifier" />
+        android:protectionLevel="signature|appPredictor" />
 
     <!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
          @hide -->
     <permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
-        android:protectionLevel="signature|textClassifier" />
+        android:protectionLevel="signature|appPredictor" />
 
     <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
          @hide <p>Not for use by third-party applications. -->
diff --git a/data/keyboards/Vendor_0f0d_Product_00c1.kl b/data/keyboards/Vendor_0f0d_Product_00c1.kl
new file mode 100644
index 0000000..c74512a
--- /dev/null
+++ b/data/keyboards/Vendor_0f0d_Product_00c1.kl
@@ -0,0 +1,55 @@
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Device name: HORI CO.,LTD. HORIPAD S
+# HORIPAD for Nintendo Switch, USB game controller
+# https://hori.co.uk/horipad-for-nintendo-switch/
+
+# Button labeled as "Y" but should really produce keycode "X"
+key 0x130 BUTTON_X
+# Button labeled as "B" but should really produce keycode "A"
+key 0x131 BUTTON_A
+# Button labeled as "A" but should really produce keycode "B"
+key 0x132 BUTTON_B
+# Button labeled as "X" but should really product keycode "Y"
+key 0x133 BUTTON_Y
+
+key 0x134 BUTTON_L1
+key 0x135 BUTTON_R1
+key 0x136 BUTTON_L2
+key 0x137 BUTTON_R2
+
+# Minus
+key 0x138 BUTTON_SELECT
+# Plus
+key 0x139 BUTTON_START
+
+# Analog stick buttons
+key 0x13a BUTTON_THUMBL
+key 0x13b BUTTON_THUMBR
+
+# Home
+key 0x13c HOME
+# Capture
+key 0x13d BUTTON_MODE
+
+# Left analog stick
+axis 0x00 X
+axis 0x01 Y
+# Right analog stick
+axis 0x02 Z
+axis 0x05 RZ
+# D-pad
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 89d3cc4..16f2917 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -20,6 +20,7 @@
     ],
 
     shared_libs: [
+        "libbinder",
         "libcutils",
         "liblog",
         "libutils",
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index abf0837..5c2ef15 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -257,19 +257,24 @@
 void PointerController::setPresentation(Presentation presentation) {
     AutoMutex _l(mLock);
 
-    if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) {
-        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                &mLocked.animationResources, mLocked.viewport.displayId);
+    if (mLocked.presentation == presentation) {
+        return;
     }
 
-    if (mLocked.presentation != presentation) {
-        mLocked.presentation = presentation;
-        mLocked.presentationChanged = true;
+    mLocked.presentation = presentation;
+    mLocked.presentationChanged = true;
 
-        if (presentation != PRESENTATION_SPOT) {
-            fadeOutAndReleaseAllSpotsLocked();
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
+    if (presentation == PRESENTATION_POINTER) {
+        if (mLocked.additionalMouseResources.empty()) {
+            mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                                  &mLocked.animationResources,
+                                                  mLocked.viewport.displayId);
         }
-
+        fadeOutAndReleaseAllSpotsLocked();
         updatePointerLocked();
     }
 }
@@ -291,6 +296,9 @@
 #endif
 
     AutoMutex _l(mLock);
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
 
     std::vector<Spot*> newSpots;
     std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
@@ -337,6 +345,9 @@
 #endif
 
     AutoMutex _l(mLock);
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
 
     fadeOutAndReleaseAllSpotsLocked();
 }
@@ -758,6 +769,10 @@
 }
 
 void PointerController::loadResourcesLocked() REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
     mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId);
     mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
 
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 52305b8..ebc622b 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -100,6 +100,7 @@
     virtual int32_t getDisplayId() const;
     virtual void fade(Transition transition);
     virtual void unfade(Transition transition);
+    virtual void setDisplayViewport(const DisplayViewport& viewport);
 
     virtual void setPresentation(Presentation presentation);
     virtual void setSpots(const PointerCoords* spotCoords,
@@ -108,7 +109,6 @@
 
     void updatePointerIcon(int32_t iconId);
     void setCustomPointerIcon(const SpriteIcon& icon);
-    void setDisplayViewport(const DisplayViewport& viewport);
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
     void reloadPointerResources();
 
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3..fd386e9 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -245,7 +245,8 @@
         if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
                 || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
                         | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
-                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID
+                        | DIRTY_ICON_STYLE))))) {
             needApplyTransaction = true;
 
             if (wantSurfaceVisibleAndDrawn
@@ -274,6 +275,21 @@
                         update.state.transformationMatrix.dtdy);
             }
 
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible
+                            || (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
+                Parcel p;
+                p.writeInt32(update.state.icon.style);
+                p.writeFloat(update.state.icon.hotSpotX);
+                p.writeFloat(update.state.icon.hotSpotY);
+
+                // Pass cursor metadata in the sprite surface so that when Android is running as a
+                // client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
+                // update mouse cursor in the host OS.
+                t.setMetadata(
+                        update.state.surfaceControl, METADATA_MOUSE_CURSOR, p);
+            }
+
             int32_t surfaceLayer = mOverlayLayer + update.state.layer;
             if (wantSurfaceVisibleAndDrawn
                     && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
@@ -397,9 +413,14 @@
         } else {
             dirty = DIRTY_BITMAP;
         }
+
+        if (mLocked.state.icon.style != icon.style) {
+            mLocked.state.icon.style = icon.style;
+            dirty |= DIRTY_ICON_STYLE;
+        }
     } else if (mLocked.state.icon.isValid()) {
         mLocked.state.icon.bitmap.reset();
-        dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
+        dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE;
     } else {
         return; // setting to invalid icon and already invalid so nothing to do
     }
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f5..79a904f 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -55,11 +55,12 @@
  * Icon that a sprite displays, including its hotspot.
  */
 struct SpriteIcon {
-    inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
-    inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
-            bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
+    inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { }
+    inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
+            bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
 
     SkBitmap bitmap;
+    int32_t style;
     float hotSpotX;
     float hotSpotY;
 
@@ -69,11 +70,12 @@
             bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
                     0, 0);
         }
-        return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
+        return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY);
     }
 
     inline void reset() {
         bitmap.reset();
+        style = 0;
         hotSpotX = 0;
         hotSpotY = 0;
     }
@@ -149,15 +151,15 @@
     SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
 
     /* Creates a new sprite, initially invisible. */
-    sp<Sprite> createSprite();
+    virtual sp<Sprite> createSprite();
 
     /* Opens or closes a transaction to perform a batch of sprite updates as part of
      * a single operation such as setPosition and setAlpha.  It is not necessary to
      * open a transaction when updating a single property.
      * Calls to openTransaction() nest and must be matched by an equal number
      * of calls to closeTransaction(). */
-    void openTransaction();
-    void closeTransaction();
+    virtual void openTransaction();
+    virtual void closeTransaction();
 
 private:
     enum {
@@ -174,6 +176,7 @@
         DIRTY_VISIBILITY = 1 << 5,
         DIRTY_HOTSPOT = 1 << 6,
         DIRTY_DISPLAY_ID = 1 << 7,
+        DIRTY_ICON_STYLE = 1 << 8,
     };
 
     /* Describes the state of a sprite.
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
new file mode 100644
index 0000000..fe74c62
--- /dev/null
+++ b/libs/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+    "presubmit": [
+        {
+            "name": "libinputservice_test"
+        }
+    ]
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
new file mode 100644
index 0000000..e83b2a7
--- /dev/null
+++ b/libs/input/tests/Android.bp
@@ -0,0 +1,45 @@
+// 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.
+
+cc_test {
+    name: "libinputservice_test",
+    srcs: [
+        "PointerController_test.cpp",
+    ],
+    shared_libs: [
+        "libinputservice",
+        "libgui",
+        "libhwui",
+        "libutils",
+    ],
+    static_libs: [
+        "libgmock",
+        "libgtest",
+    ],
+    header_libs: [
+        "libbase_headers",
+        "libinputflinger_headers",
+    ],
+    include_dirs: [
+        "frameworks/base/libs",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+}
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
new file mode 100644
index 0000000..a157426
--- /dev/null
+++ b/libs/input/tests/PointerController_test.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mocks/MockSprite.h"
+#include "mocks/MockSpriteController.h"
+
+#include <input/PointerController.h>
+#include <input/SpriteController.h>
+
+#include <atomic>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+enum TestCursorType {
+    CURSOR_TYPE_DEFAULT = 0,
+    CURSOR_TYPE_HOVER,
+    CURSOR_TYPE_TOUCH,
+    CURSOR_TYPE_ANCHOR,
+    CURSOR_TYPE_ADDITIONAL,
+    CURSOR_TYPE_ADDITIONAL_ANIM,
+    CURSOR_TYPE_CUSTOM = -1,
+};
+
+using ::testing::AllOf;
+using ::testing::Field;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::Test;
+
+std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
+    return std::make_pair(type * 10, type * 10 + 5);
+}
+
+class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
+public:
+    virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
+    virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
+    virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
+            std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
+    virtual int32_t getDefaultPointerIconId() override;
+    virtual int32_t getCustomPointerIconId() override;
+
+    bool allResourcesAreLoaded();
+    bool noResourcesAreLoaded();
+
+private:
+    void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
+
+    bool pointerIconLoaded{false};
+    bool pointerResourcesLoaded{false};
+    bool additionalMouseResourcesLoaded{false};
+};
+
+void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
+    loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
+    pointerIconLoaded = true;
+}
+
+void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
+        int32_t) {
+    loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
+    loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
+    loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
+    pointerResourcesLoaded = true;
+}
+
+void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
+        std::map<int32_t, SpriteIcon>* outResources,
+        std::map<int32_t, PointerAnimation>* outAnimationResources,
+        int32_t) {
+    SpriteIcon icon;
+    PointerAnimation anim;
+
+    // CURSOR_TYPE_ADDITIONAL doesn't have animation resource.
+    int32_t cursorType = CURSOR_TYPE_ADDITIONAL;
+    loadPointerIconForType(&icon, cursorType);
+    (*outResources)[cursorType] = icon;
+
+    // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource.
+    cursorType = CURSOR_TYPE_ADDITIONAL_ANIM;
+    loadPointerIconForType(&icon, cursorType);
+    anim.animationFrames.push_back(icon);
+    anim.durationPerFrame = 10;
+    (*outResources)[cursorType] = icon;
+    (*outAnimationResources)[cursorType] = anim;
+
+    additionalMouseResourcesLoaded = true;
+}
+
+int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
+    return CURSOR_TYPE_DEFAULT;
+}
+
+int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
+    return CURSOR_TYPE_CUSTOM;
+}
+
+bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
+    return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
+}
+
+bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
+    return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
+}
+
+void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
+    icon->style = type;
+    std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
+    icon->hotSpotX = hotSpot.first;
+    icon->hotSpotY = hotSpot.second;
+}
+class PointerControllerTest : public Test {
+protected:
+    PointerControllerTest();
+    ~PointerControllerTest();
+
+    void ensureDisplayViewportIsSet();
+
+    sp<MockSprite> mPointerSprite;
+    sp<MockPointerControllerPolicyInterface> mPolicy;
+    sp<MockSpriteController> mSpriteController;
+    sp<PointerController> mPointerController;
+
+private:
+    void loopThread();
+
+    std::atomic<bool> mRunning = true;
+    class MyLooper : public Looper {
+    public:
+        MyLooper() : Looper(false) {}
+        ~MyLooper() = default;
+    };
+    sp<MyLooper> mLooper;
+    std::thread mThread;
+};
+
+PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
+        mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
+
+    mSpriteController = new NiceMock<MockSpriteController>(mLooper);
+    mPolicy = new MockPointerControllerPolicyInterface();
+
+    EXPECT_CALL(*mSpriteController, createSprite())
+            .WillOnce(Return(mPointerSprite));
+
+    mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+}
+
+PointerControllerTest::~PointerControllerTest() {
+    mRunning.store(false, std::memory_order_relaxed);
+    mThread.join();
+}
+
+void PointerControllerTest::ensureDisplayViewportIsSet() {
+    DisplayViewport viewport;
+    viewport.displayId = ADISPLAY_ID_DEFAULT;
+    viewport.logicalRight = 1600;
+    viewport.logicalBottom = 1200;
+    viewport.physicalRight = 800;
+    viewport.physicalBottom = 600;
+    viewport.deviceWidth = 400;
+    viewport.deviceHeight = 300;
+    mPointerController->setDisplayViewport(viewport);
+
+    // The first call to setDisplayViewport should trigger the loading of the necessary resources.
+    EXPECT_TRUE(mPolicy->allResourcesAreLoaded());
+}
+
+void PointerControllerTest::loopThread() {
+    Looper::setForThread(mLooper);
+
+    while (mRunning.load(std::memory_order_relaxed)) {
+        mLooper->pollOnce(100);
+    }
+}
+
+TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
+    ensureDisplayViewportIsSet();
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+    std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
+    EXPECT_CALL(*mPointerSprite, setVisible(true));
+    EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+    EXPECT_CALL(*mPointerSprite, setIcon(
+            AllOf(
+                    Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
+                    Field(&SpriteIcon::hotSpotX, hotspot.first),
+                    Field(&SpriteIcon::hotSpotY, hotspot.second))));
+    mPointerController->reloadPointerResources();
+}
+
+TEST_F(PointerControllerTest, updatePointerIcon) {
+    ensureDisplayViewportIsSet();
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+    int32_t type = CURSOR_TYPE_ADDITIONAL;
+    std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
+    EXPECT_CALL(*mPointerSprite, setVisible(true));
+    EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+    EXPECT_CALL(*mPointerSprite, setIcon(
+            AllOf(
+                    Field(&SpriteIcon::style, type),
+                    Field(&SpriteIcon::hotSpotX, hotspot.first),
+                    Field(&SpriteIcon::hotSpotY, hotspot.second))));
+    mPointerController->updatePointerIcon(type);
+}
+
+TEST_F(PointerControllerTest, setCustomPointerIcon) {
+    ensureDisplayViewportIsSet();
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+    int32_t style = CURSOR_TYPE_CUSTOM;
+    float hotSpotX = 15;
+    float hotSpotY = 20;
+
+    SpriteIcon icon;
+    icon.style = style;
+    icon.hotSpotX = hotSpotX;
+    icon.hotSpotY = hotSpotY;
+
+    EXPECT_CALL(*mPointerSprite, setVisible(true));
+    EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+    EXPECT_CALL(*mPointerSprite, setIcon(
+            AllOf(
+                    Field(&SpriteIcon::style, style),
+                    Field(&SpriteIcon::hotSpotX, hotSpotX),
+                    Field(&SpriteIcon::hotSpotY, hotSpotY))));
+    mPointerController->setCustomPointerIcon(icon);
+}
+
+TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
+    mPointerController->setPresentation(PointerController::PRESENTATION_POINTER);
+    mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
+    mPointerController->clearSpots();
+    mPointerController->setPosition(1.0f, 1.0f);
+    mPointerController->move(1.0f, 1.0f);
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->fade(PointerController::TRANSITION_IMMEDIATE);
+
+    EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
+
+    ensureDisplayViewportIsSet();
+}
+
+}  // namespace android
diff --git a/libs/input/tests/mocks/MockSprite.h b/libs/input/tests/mocks/MockSprite.h
new file mode 100644
index 0000000..013b79c
--- /dev/null
+++ b/libs/input/tests/mocks/MockSprite.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MOCK_SPRITE_H
+#define _MOCK_SPRITE_H
+
+#include <input/SpriteController.h>
+
+#include <gmock/gmock.h>
+
+namespace android {
+
+class MockSprite : public Sprite {
+public:
+    virtual ~MockSprite() = default;
+
+    MOCK_METHOD(void, setIcon, (const SpriteIcon& icon), (override));
+    MOCK_METHOD(void, setVisible, (bool), (override));
+    MOCK_METHOD(void, setPosition, (float, float), (override));
+    MOCK_METHOD(void, setLayer, (int32_t), (override));
+    MOCK_METHOD(void, setAlpha, (float), (override));
+    MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override));
+    MOCK_METHOD(void, setDisplayId, (int32_t), (override));
+};
+
+}  // namespace android
+
+#endif  // _MOCK_SPRITE_H
diff --git a/libs/input/tests/mocks/MockSpriteController.h b/libs/input/tests/mocks/MockSpriteController.h
new file mode 100644
index 0000000..a034f66
--- /dev/null
+++ b/libs/input/tests/mocks/MockSpriteController.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MOCK_SPRITE_CONTROLLER_H
+#define _MOCK_SPRITE_CONTROLLER_H
+
+#include "MockSprite.h"
+
+#include <input/SpriteController.h>
+
+namespace android {
+
+class MockSpriteController : public SpriteController {
+
+public:
+    MockSpriteController(sp<Looper> looper) : SpriteController(looper, 0) {}
+    ~MockSpriteController() {}
+
+    MOCK_METHOD(sp<Sprite>, createSprite, (), (override));
+    MOCK_METHOD(void, openTransaction, (), (override));
+    MOCK_METHOD(void, closeTransaction, (), (override));
+};
+
+}  // namespace android
+
+#endif  // _MOCK_SPRITE_CONTROLLER_H
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index c8cf7f50..304f81b 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -43,7 +43,7 @@
 winsonc@google.com
 
 #Android Auto
-stenning@google.com
+hseog@google.com
 
 #Android TV
 rgl@google.com
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index eb72b81..33c2b582 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -33,7 +33,7 @@
         "net-utils-framework-common",
     ],
     libs: [
-        "framework-tethering",
+        "framework-tethering.impl",
         "unsupportedappusage",
     ],
     plugins: ["java_api_finder"],
@@ -96,7 +96,7 @@
         "res",
     ],
     libs: [
-        "framework-tethering",
+        "framework-tethering.impl",
     ],
     jarjar_rules: "jarjar-rules.txt",
     optimize: {
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 79a1782..408725c 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -13,31 +13,28 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-java_library {
+java_sdk_library {
     name: "framework-tethering",
-    sdk_version: "module_current",
+    defaults: ["framework-module-defaults"],
     srcs: [
         ":framework-tethering-srcs",
     ],
+
+    // 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",
+
     jarjar_rules: "jarjar-rules.txt",
     installable: true,
 
-    libs: [
-        "framework-annotations-lib",
-    ],
-
     hostdex: true, // for hiddenapi check
     visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
+    stubs_library_visibility: ["//visibility:public"],
     apex_available: ["com.android.tethering"],
     permitted_packages: ["android.net"],
 }
 
-stubs_defaults {
-    name: "framework-tethering-stubs-defaults",
-    srcs: [":framework-tethering-srcs"],
-    dist: { dest: "framework-tethering.txt" },
-}
-
 filegroup {
     name: "framework-tethering-srcs",
     srcs: [
@@ -55,56 +52,3 @@
     ],
     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/module-lib-lint-baseline.txt b/packages/Tethering/common/TetheringLib/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..af7ad52
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/module-lib-lint-baseline.txt
@@ -0,0 +1,39 @@
+// Baseline format: 1.0
+ActionValue: android.net.TetheringConstants#EXTRA_ADD_TETHER_TYPE:
+    Inconsistent extra value; expected `android.net.extra.ADD_TETHER_TYPE`, was `extraAddTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_PROVISION_CALLBACK:
+    Inconsistent extra value; expected `android.net.extra.PROVISION_CALLBACK`, was `extraProvisionCallback`
+ActionValue: android.net.TetheringConstants#EXTRA_REM_TETHER_TYPE:
+    Inconsistent extra value; expected `android.net.extra.REM_TETHER_TYPE`, was `extraRemTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_RUN_PROVISION:
+    Inconsistent extra value; expected `android.net.extra.RUN_PROVISION`, was `extraRunProvision`
+ActionValue: android.net.TetheringConstants#EXTRA_SET_ALARM:
+    Inconsistent extra value; expected `android.net.extra.SET_ALARM`, was `extraSetAlarm`
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+    Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+    Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+    Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+    Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+
+CallbackInterface: android.net.TetheringManager.StartTetheringCallback:
+    Callbacks must be abstract class instead of interface to enable extension in future API levels: StartTetheringCallback
+CallbackInterface: android.net.TetheringManager.TetheringEventCallback:
+    Callbacks must be abstract class instead of interface to enable extension in future API levels: TetheringEventCallback
+
+
+ManagerConstructor: android.net.TetheringManager#TetheringManager(android.content.Context, java.util.function.Supplier<android.os.IBinder>):
+    Managers must always be obtained from Context; no direct constructors
+
+
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setShouldShowEntitlementUi(boolean):
+    android.net.TetheringManager.TetheringRequest does not declare a `shouldShowEntitlementUi()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setShouldShowEntitlementUi(boolean)
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setStaticIpv4Addresses(android.net.LinkAddress, android.net.LinkAddress):
+    android.net.TetheringManager.TetheringRequest does not declare a `getStaticIpv4Addresses()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setStaticIpv4Addresses(android.net.LinkAddress,android.net.LinkAddress)
+
+
+StaticFinalBuilder: android.net.TetheringManager.TetheringRequest.Builder:
+    Builder must be final: android.net.TetheringManager.TetheringRequest.Builder
diff --git a/packages/Tethering/common/TetheringLib/api/system-lint-baseline.txt b/packages/Tethering/common/TetheringLib/api/system-lint-baseline.txt
new file mode 100644
index 0000000..f8d291c
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/api/system-lint-baseline.txt
@@ -0,0 +1,25 @@
+// Baseline format: 1.0
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+    Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+    Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+    Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+    Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+
+CallbackInterface: android.net.TetheringManager.StartTetheringCallback:
+    Callbacks must be abstract class instead of interface to enable extension in future API levels: StartTetheringCallback
+CallbackInterface: android.net.TetheringManager.TetheringEventCallback:
+    Callbacks must be abstract class instead of interface to enable extension in future API levels: TetheringEventCallback
+
+
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setShouldShowEntitlementUi(boolean):
+    android.net.TetheringManager.TetheringRequest does not declare a `shouldShowEntitlementUi()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setShouldShowEntitlementUi(boolean)
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setStaticIpv4Addresses(android.net.LinkAddress, android.net.LinkAddress):
+    android.net.TetheringManager.TetheringRequest does not declare a `getStaticIpv4Addresses()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setStaticIpv4Addresses(android.net.LinkAddress,android.net.LinkAddress)
+
+
+StaticFinalBuilder: android.net.TetheringManager.TetheringRequest.Builder:
+    Builder must be final: android.net.TetheringManager.TetheringRequest.Builder
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 659d344..f08429b 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -16,14 +16,13 @@
 
 package android.net.ip;
 
-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.PrefixUtils.asIpPrefix;
 import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
 import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
@@ -66,11 +65,11 @@
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.networkstack.tethering.PrivateAddressCoordinator;
 
 import java.io.IOException;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
-import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
@@ -108,27 +107,8 @@
 
     private static final byte DOUG_ADAMS = (byte) 42;
 
-    private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
-    private static final int USB_PREFIX_LENGTH = 24;
-    private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
-    private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
-    private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1";
-    private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24;
-    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;
+    private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1/24";
 
     // TODO: have this configurable
     private static final int DHCP_LEASE_TIME_SECS = 3600;
@@ -167,6 +147,14 @@
          * Notify that the DHCP leases changed in one of the IpServers.
          */
         public void dhcpLeasesChanged() { }
+
+        /**
+         * Request Tethering change.
+         *
+         * @param tetheringType the downstream type of this IpServer.
+         * @param enabled enable or disable tethering.
+         */
+        public void requestEnableTethering(int tetheringType, boolean enabled) { }
     }
 
     /** Capture IpServer dependencies, for injection. */
@@ -196,6 +184,7 @@
                 return 0;
             }
         }
+
         /** Create a DhcpServer instance to be used by IpServer. */
         public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
                 DhcpServerCallbacks cb);
@@ -225,16 +214,20 @@
     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;
+    // request from PrivateAddressCoordinator to restart tethering.
+    public static final int CMD_NOTIFY_PREFIX_CONFLICT      = BASE_IPSERVER + 13;
 
     private final State mInitialState;
     private final State mLocalHotspotState;
     private final State mTetheredState;
     private final State mUnavailableState;
+    private final State mWaitingForRestartState;
 
     private final SharedLog mLog;
     private final INetd mNetd;
     private final Callback mCallback;
     private final InterfaceController mInterfaceCtrl;
+    private final PrivateAddressCoordinator mPrivateAddressCoordinator;
 
     private final String mIfaceName;
     private final int mInterfaceType;
@@ -261,7 +254,6 @@
     private int mDhcpServerStartIndex = 0;
     private IDhcpServer mDhcpServer;
     private RaParams mLastRaParams;
-    private LinkAddress mIpv4Address;
 
     private LinkAddress mStaticIpv4ServerAddr;
     private LinkAddress mStaticIpv4ClientAddr;
@@ -316,12 +308,14 @@
 
     private final IpNeighborMonitor mIpNeighborMonitor;
 
+    private LinkAddress mIpv4Address;
+
     // 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, boolean usingBpfOffload,
-            Dependencies deps) {
+            PrivateAddressCoordinator addressCoordinator, Dependencies deps) {
         super(ifaceName, looper);
         mLog = log.forSubComponent(ifaceName);
         mNetd = netd;
@@ -332,6 +326,7 @@
         mLinkProperties = new LinkProperties();
         mUsingLegacyDhcp = usingLegacyDhcp;
         mUsingBpfOffload = usingBpfOffload;
+        mPrivateAddressCoordinator = addressCoordinator;
         mDeps = deps;
         resetLinkProperties();
         mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -352,9 +347,11 @@
         mLocalHotspotState = new LocalHotspotState();
         mTetheredState = new TetheredState();
         mUnavailableState = new UnavailableState();
+        mWaitingForRestartState = new WaitingForRestartState();
         addState(mInitialState);
         addState(mLocalHotspotState);
         addState(mTetheredState);
+        addState(mWaitingForRestartState, mTetheredState);
         addState(mUnavailableState);
 
         setInitialState(mInitialState);
@@ -387,6 +384,11 @@
         return new LinkProperties(mLinkProperties);
     }
 
+    /** The address which IpServer is using. */
+    public LinkAddress getAddress() {
+        return mIpv4Address;
+    }
+
     /**
      * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
      * thread.
@@ -617,6 +619,7 @@
         // NOTE: All of configureIPv4() will be refactored out of existence
         // into calls to InterfaceController, shared with startIPv4().
         mInterfaceCtrl.clearIPv4Address();
+        mPrivateAddressCoordinator.releaseDownstream(this);
         mIpv4Address = null;
         mStaticIpv4ServerAddr = null;
         mStaticIpv4ClientAddr = null;
@@ -625,43 +628,24 @@
     private boolean configureIPv4(boolean enabled) {
         if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
 
-        // TODO: Replace this hard-coded information with dynamically selected
-        // config passed down to us by a higher layer IP-coordinating element.
-        final Inet4Address srvAddr;
-        int prefixLen = 0;
-        try {
-            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;
-            } else if (mInterfaceType == TetheringManager.TETHERING_WIFI) {
-                srvAddr = (Inet4Address) parseNumericAddress(getRandomWifiIPv4Address());
-                prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
-            } else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
-                srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR);
-                prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
-            } else if (mInterfaceType == TetheringManager.TETHERING_ETHERNET) {
-                // TODO: randomize address for tethering too, similarly to wifi
-                srvAddr = (Inet4Address) parseNumericAddress(ETHERNET_IFACE_ADDR);
-                prefixLen = ETHERNET_IFACE_PREFIX_LENGTH;
-            } else {
-                // BT configures the interface elsewhere: only start DHCP.
-                // TODO: make all tethering types behave the same way, and delete the bluetooth
-                // code that calls into NetworkManagementService directly.
-                srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
-                mIpv4Address = new LinkAddress(srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
-                return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
-            }
-            mIpv4Address = new LinkAddress(srvAddr, prefixLen);
-        } catch (IllegalArgumentException e) {
-            mLog.e("Error selecting ipv4 address", e);
-            if (!enabled) stopDhcp();
+        if (enabled) {
+            mIpv4Address = requestIpv4Address();
+        }
+
+        if (mIpv4Address == null) {
+            mLog.e("No available ipv4 address");
             return false;
         }
 
+        if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
+            // BT configures the interface elsewhere: only start DHCP.
+            // TODO: make all tethering types behave the same way, and delete the bluetooth
+            // code that calls into NetworkManagementService directly.
+            return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
+        }
+
+        final IpPrefix ipv4Prefix = asIpPrefix(mIpv4Address);
+
         final Boolean setIfaceUp;
         if (mInterfaceType == TetheringManager.TETHERING_WIFI
                 || mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) {
@@ -688,21 +672,14 @@
         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 LinkAddress requestIpv4Address() {
+        if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
 
-    private String getRandomWifiIPv4Address() {
-        final Inet4Address ipv4Addr =
-                getRandomIPv4Address(parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress());
-        return ipv4Addr != null ? ipv4Addr.getHostAddress() : WIFI_HOST_IFACE_ADDR;
+        if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
+            return new LinkAddress(BLUETOOTH_IFACE_ADDR);
+        }
+
+        return mPrivateAddressCoordinator.requestDownstreamAddress(this);
     }
 
     private boolean startIPv6() {
@@ -978,19 +955,6 @@
         }
     }
 
-    // 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()) {
@@ -999,12 +963,12 @@
         }
 
         final LinkAddress deprecatedLinkAddress = mIpv4Address;
-        final Inet4Address srvAddr = requestDownstreamAddress(currentPrefix);
-        if (srvAddr == null) {
+        mIpv4Address = requestIpv4Address();
+        if (mIpv4Address == null) {
             mLog.e("Fail to request a new downstream prefix");
             return;
         }
-        mIpv4Address = new LinkAddress(srvAddr, currentPrefix.getPrefixLength());
+        final Inet4Address srvAddr = (Inet4Address) mIpv4Address.getAddress();
 
         // Add new IPv4 address on the interface.
         if (!mInterfaceCtrl.addAddress(srvAddr, currentPrefix.getPrefixLength())) {
@@ -1162,7 +1126,7 @@
             }
 
             try {
-                NetdUtils.tetherInterface(mNetd, mIfaceName, PrefixUtils.asIpPrefix(mIpv4Address));
+                NetdUtils.tetherInterface(mNetd, mIfaceName, asIpPrefix(mIpv4Address));
             } catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
                 mLog.e("Error Tethering", e);
                 mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
@@ -1222,6 +1186,11 @@
                 case CMD_NEW_PREFIX_REQUEST:
                     handleNewPrefixRequest((IpPrefix) message.obj);
                     break;
+                case CMD_NOTIFY_PREFIX_CONFLICT:
+                    mLog.i("restart tethering: " + mInterfaceType);
+                    mCallback.requestEnableTethering(mInterfaceType, false /* enabled */);
+                    transitionTo(mWaitingForRestartState);
+                    break;
                 default:
                     return false;
             }
@@ -1403,6 +1372,28 @@
         }
     }
 
+    class WaitingForRestartState extends State {
+        @Override
+        public boolean processMessage(Message message) {
+            logMessage(this, message.what);
+            switch (message.what) {
+                case CMD_TETHER_UNREQUESTED:
+                    transitionTo(mInitialState);
+                    mLog.i("Untethered (unrequested) and restarting " + mIfaceName);
+                    mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
+                    break;
+                case CMD_INTERFACE_DOWN:
+                    transitionTo(mUnavailableState);
+                    mLog.i("Untethered (interface down) and restarting" + mIfaceName);
+                    mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
+                    break;
+                default:
+                    return false;
+            }
+            return true;
+        }
+    }
+
     // Accumulate routes representing "prefixes to be assigned to the local
     // interface", for subsequent modification of local_network routing.
     private static ArrayList<RouteInfo> getLocalRoutesFor(
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
new file mode 100644
index 0000000..160a166
--- /dev/null
+++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.content.Context;
+import android.net.ConnectivityManager;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.ip.IpServer;
+import android.net.util.PrefixUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * This class coordinate IP addresses conflict problem.
+ *
+ * Tethering downstream IP addresses may conflict with network assigned addresses. This
+ * coordinator is responsible for recording all of network assigned addresses and dispatched
+ * free address to downstream interfaces.
+ *
+ * This class is not thread-safe and should be accessed on the same tethering internal thread.
+ * @hide
+ */
+public class PrivateAddressCoordinator {
+    public static final int PREFIX_LENGTH = 24;
+
+    private static final int MAX_UBYTE = 256;
+    private static final int BYTE_MASK = 0xff;
+    // reserved for bluetooth tethering.
+    private static final int BLUETOOTH_RESERVED = 44;
+    private static final byte DEFAULT_ID = (byte) 42;
+
+    // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
+    // address may be requested before coordinator get current upstream notification. To ensure
+    // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
+    // when tethering is down. Instead coordinator would remove all depcreted upstreams from
+    // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprectedUpstreams().
+    private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
+    private final ArraySet<IpServer> mDownstreams;
+    // IANA has reserved the following three blocks of the IP address space for private intranets:
+    // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
+    // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
+    private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
+    private final IpPrefix mTetheringPrefix;
+    private final ConnectivityManager mConnectivityMgr;
+
+    public PrivateAddressCoordinator(Context context) {
+        mDownstreams = new ArraySet<>();
+        mUpstreamPrefixMap = new ArrayMap<>();
+        mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
+        mConnectivityMgr = (ConnectivityManager) context.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+    }
+
+    /**
+     * Record a new upstream IpPrefix which may conflict with tethering downstreams.
+     * The downstreams will be notified if a conflict is found.
+     */
+    public void updateUpstreamPrefix(final Network network, final LinkProperties lp) {
+        final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(lp.getAllLinkAddresses());
+        if (ipv4Prefixes.isEmpty()) {
+            removeUpstreamPrefix(network);
+            return;
+        }
+
+        mUpstreamPrefixMap.put(network, ipv4Prefixes);
+        handleMaybePrefixConflict(ipv4Prefixes);
+    }
+
+    private ArrayList<IpPrefix> getIpv4Prefixes(final List<LinkAddress> linkAddresses) {
+        final ArrayList<IpPrefix> list = new ArrayList<>();
+        for (LinkAddress address : linkAddresses) {
+            if (!address.isIpv4()) continue;
+
+            list.add(PrefixUtils.asIpPrefix(address));
+        }
+
+        return list;
+    }
+
+    private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
+        for (IpServer downstream : mDownstreams) {
+            final IpPrefix target = getDownstreamPrefix(downstream);
+            if (target == null) continue;
+
+            for (IpPrefix source : prefixes) {
+                if (isConflictPrefix(source, target)) {
+                    downstream.sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+                    break;
+                }
+            }
+        }
+    }
+
+    /** Remove IpPrefix records corresponding to input network. */
+    public void removeUpstreamPrefix(final Network network) {
+        mUpstreamPrefixMap.remove(network);
+    }
+
+    private void maybeRemoveDeprectedUpstreams() {
+        if (!mDownstreams.isEmpty() || mUpstreamPrefixMap.isEmpty()) return;
+
+        final ArrayList<Network> toBeRemoved = new ArrayList<>();
+        List<Network> allNetworks = Arrays.asList(mConnectivityMgr.getAllNetworks());
+        for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
+            final Network network = mUpstreamPrefixMap.keyAt(i);
+            if (!allNetworks.contains(network)) toBeRemoved.add(network);
+        }
+
+        mUpstreamPrefixMap.removeAll(toBeRemoved);
+    }
+
+    /**
+     * Pick a random available address and mark its prefix as in use for the provided IpServer,
+     * returns null if there is no available address.
+     */
+    @Nullable
+    public LinkAddress requestDownstreamAddress(final IpServer ipServer) {
+        maybeRemoveDeprectedUpstreams();
+
+        // Address would be 192.168.[subAddress]/24.
+        final byte[] bytes = mTetheringPrefix.getRawAddress();
+        final int subAddress = getRandomSubAddr();
+        final int subNet = (subAddress >> 8) & BYTE_MASK;
+        bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
+        for (int i = 0; i < MAX_UBYTE; i++) {
+            final int newSubNet = (subNet + i) & BYTE_MASK;
+            if (newSubNet == BLUETOOTH_RESERVED) continue;
+
+            bytes[2] = (byte) newSubNet;
+            final InetAddress addr;
+            try {
+                addr = InetAddress.getByAddress(bytes);
+            } catch (UnknownHostException e) {
+                throw new IllegalStateException("Invalid address, shouldn't happen.", e);
+            }
+
+            final IpPrefix prefix = new IpPrefix(addr, PREFIX_LENGTH);
+            // Check whether this prefix is in use.
+            if (isDownstreamPrefixInUse(prefix)) continue;
+            // Check whether this prefix is conflict with any current upstream network.
+            if (isConflictWithUpstream(prefix)) continue;
+
+            mDownstreams.add(ipServer);
+            return new LinkAddress(addr, PREFIX_LENGTH);
+        }
+
+        // No available address.
+        return null;
+    }
+
+    /** Get random sub address value. Return value is in 0 ~ 0xffff. */
+    @VisibleForTesting
+    public int getRandomSubAddr() {
+        return ((new Random()).nextInt()) & 0xffff; // subNet is in 0 ~ 0xffff.
+    }
+
+    private byte getSanitizedAddressSuffix(final int source, byte... excluded) {
+        final byte subId = (byte) (source & BYTE_MASK);
+        for (byte value : excluded) {
+            if (subId == value) return DEFAULT_ID;
+        }
+
+        return subId;
+    }
+
+    /** Release downstream record for IpServer. */
+    public void releaseDownstream(final IpServer ipServer) {
+        mDownstreams.remove(ipServer);
+    }
+
+    /** Clear current upstream prefixes records. */
+    public void clearUpstreamPrefixes() {
+        mUpstreamPrefixMap.clear();
+    }
+
+    private boolean isConflictWithUpstream(final IpPrefix source) {
+        for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
+            final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
+            for (IpPrefix target : list) {
+                if (isConflictPrefix(source, target)) return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
+        if (prefix2.getPrefixLength() < prefix1.getPrefixLength()) {
+            return prefix2.contains(prefix1.getAddress());
+        }
+
+        return prefix1.contains(prefix2.getAddress());
+    }
+
+    private boolean isDownstreamPrefixInUse(final IpPrefix source) {
+        // This class always generates downstream prefixes with the same prefix length, so
+        // prefixes cannot be contained in each other. They can only be equal to each other.
+        for (IpServer downstream : mDownstreams) {
+            final IpPrefix prefix = getDownstreamPrefix(downstream);
+            if (source.equals(prefix)) return true;
+        }
+        return false;
+    }
+
+    private IpPrefix getDownstreamPrefix(final IpServer downstream) {
+        final LinkAddress address = downstream.getAddress();
+        if (address == null) return null;
+
+        return PrefixUtils.asIpPrefix(address);
+    }
+
+    void dump(final IndentingPrintWriter pw) {
+        pw.decreaseIndent();
+        pw.println("mUpstreamPrefixMap:");
+        pw.increaseIndent();
+        for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
+            pw.println(mUpstreamPrefixMap.keyAt(i) + " - " + mUpstreamPrefixMap.valueAt(i));
+        }
+        pw.decreaseIndent();
+        pw.println("mDownstreams:");
+        pw.increaseIndent();
+        for (IpServer ipServer : mDownstreams) {
+            pw.println(ipServer.interfaceType() + " - " + ipServer.getAddress());
+        }
+        pw.decreaseIndent();
+    }
+}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 3ce3b45..6eb1012 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -207,6 +207,7 @@
             new SparseArray<>();
 
     // used to synchronize public access to members
+    // TODO(b/153621704): remove mPublicSync to make Tethering lock free
     private final Object mPublicSync;
     private final Context mContext;
     private final ArrayMap<String, TetherState> mTetherStates;
@@ -231,6 +232,7 @@
     private final TetheringThreadExecutor mExecutor;
     private final TetheringNotificationUpdater mNotificationUpdater;
     private final UserManager mUserManager;
+    private final PrivateAddressCoordinator mPrivateAddressCoordinator;
     private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
     // All the usage of mTetheringEventCallback should run in the same thread.
     private ITetheringEventCallback mTetheringEventCallback = null;
@@ -314,6 +316,7 @@
         mExecutor = new TetheringThreadExecutor(mHandler);
         mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
         mNetdCallback = new NetdCallback();
+        mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext);
 
         // Load tethering configuration.
         updateConfiguration();
@@ -1616,6 +1619,14 @@
             }
         }
 
+        private void addUpstreamPrefixes(final UpstreamNetworkState ns) {
+            mPrivateAddressCoordinator.updateUpstreamPrefix(ns.network, ns.linkProperties);
+        }
+
+        private void removeUpstreamPrefixes(final UpstreamNetworkState ns) {
+            mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network);
+        }
+
         @VisibleForTesting
         void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
             if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
@@ -1624,6 +1635,14 @@
             }
 
             final UpstreamNetworkState ns = (UpstreamNetworkState) o;
+            switch (arg1) {
+                case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
+                    addUpstreamPrefixes(ns);
+                    break;
+                case UpstreamNetworkMonitor.EVENT_ON_LOST:
+                    removeUpstreamPrefixes(ns);
+                    break;
+            }
 
             if (ns == null || !pertainsToCurrentUpstream(ns)) {
                 // TODO: In future, this is where upstream evaluation and selection
@@ -2190,6 +2209,11 @@
         mOffloadController.dump(pw);
         pw.decreaseIndent();
 
+        pw.println("Private address coordinator:");
+        pw.increaseIndent();
+        mPrivateAddressCoordinator.dump(pw);
+        pw.decreaseIndent();
+
         pw.println("Log:");
         pw.increaseIndent();
         if (argsContain(args, "--short")) {
@@ -2231,6 +2255,11 @@
             public void dhcpLeasesChanged() {
                 updateConnectedClients(null /* wifiClients */);
             }
+
+            @Override
+            public void requestEnableTethering(int tetheringType, boolean enabled) {
+                enableTetheringInternal(tetheringType, enabled, null);
+            }
         };
     }
 
@@ -2314,7 +2343,8 @@
         final TetherState tetherState = new TetherState(
                 new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
-                             mConfig.enableBpfOffload, mDeps.getIpServerDependencies()));
+                             mConfig.enableBpfOffload, mPrivateAddressCoordinator,
+                             mDeps.getIpServerDependencies()));
         mTetherStates.put(iface, tetherState);
         tetherState.ipServer.start();
     }
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index 3305ed0..ed69b7d 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -63,7 +63,6 @@
 // NetworkStackTests.
 android_test {
     name: "TetheringCoverageTests",
-    certificate: "platform",
     platform_apis: true,
     test_suites: ["device-tests", "mts"],
     test_config: "AndroidTest_Coverage.xml",
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 4bac9da..2fb7e60 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -76,7 +76,7 @@
 public class EthernetTetheringTest {
 
     private static final String TAG = EthernetTetheringTest.class.getSimpleName();
-    private static final int TIMEOUT_MS = 1000;
+    private static final int TIMEOUT_MS = 5000;
     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[] {
@@ -338,7 +338,8 @@
 
     private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception {
         return enableEthernetTethering(iface,
-                new TetheringRequest.Builder(TETHERING_ETHERNET).build());
+                new TetheringRequest.Builder(TETHERING_ETHERNET)
+                .setExemptFromEntitlementCheck(true).build());
     }
 
     private int getMTU(TestNetworkInterface iface) throws SocketException {
@@ -508,7 +509,8 @@
         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();
+                .setStaticIpv4Addresses(localAddr, clientAddr)
+                .setExemptFromEntitlementCheck(true).build();
     }
 
     private void assertInvalidStaticIpv4Request(String iface, String local, String client)
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 9b7d683..1572768 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -29,7 +29,7 @@
     sdk_version: "core_platform",
     libs: [
         "framework-minus-apex",
-        "framework-tethering",
+        "framework-tethering.impl",
     ],
     visibility: ["//cts/tests/tests/tethering"],
 }
@@ -59,7 +59,7 @@
         "ext",
         "framework-minus-apex",
         "framework-res",
-        "framework-tethering",
+        "framework-tethering.impl",
     ],
     jni_libs: [
         // For mockito extended
@@ -82,7 +82,7 @@
 
 android_test {
     name: "TetheringTests",
-    certificate: "platform",
+    platform_apis: true,
     test_suites: [
         "device-tests",
         "mts",
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 307ebf1..0cda29a 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -78,6 +78,7 @@
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
+import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
@@ -86,6 +87,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.networkstack.tethering.PrivateAddressCoordinator;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -109,7 +112,7 @@
     private static final String UPSTREAM_IFACE2 = "upstream1";
     private static final int UPSTREAM_IFINDEX = 101;
     private static final int UPSTREAM_IFINDEX2 = 102;
-    private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
+    private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.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;
@@ -119,6 +122,9 @@
 
     private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
 
+    private final LinkAddress mTestAddress = new LinkAddress("192.168.42.5/24");
+    private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
+
     @Mock private INetd mNetd;
     @Mock private IpServer.Callback mCallback;
     @Mock private SharedLog mSharedLog;
@@ -126,6 +132,7 @@
     @Mock private RouterAdvertisementDaemon mRaDaemon;
     @Mock private IpNeighborMonitor mIpNeighborMonitor;
     @Mock private IpServer.Dependencies mDependencies;
+    @Mock private PrivateAddressCoordinator mAddressCoordinator;
 
     @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
 
@@ -173,7 +180,7 @@
 
         mIpServer = new IpServer(
                 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
-                mCallback, usingLegacyDhcp, usingBpfOffload, mDependencies);
+                mCallback, usingLegacyDhcp, usingBpfOffload, mAddressCoordinator, mDependencies);
         mIpServer.start();
         mNeighborEventConsumer = neighborCaptor.getValue();
 
@@ -200,12 +207,14 @@
             lp.setInterfaceName(upstreamIface);
             dispatchTetherConnectionChanged(upstreamIface, lp, 0);
         }
-        reset(mNetd, mCallback);
+        reset(mNetd, mCallback, mAddressCoordinator);
+        when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
     }
 
     @Before public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
+        when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(mTestAddress);
     }
 
     @Test
@@ -214,7 +223,7 @@
                 .thenReturn(mIpNeighborMonitor);
         mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
                 mNetd, mCallback, false /* usingLegacyDhcp */, DEFAULT_USING_BPF_OFFLOAD,
-                mDependencies);
+                mAddressCoordinator, mDependencies);
         mIpServer.start();
         mLooper.dispatchAll();
         verify(mCallback).updateInterfaceState(
@@ -277,16 +286,17 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, null);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNetd, mCallback);
+        InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
         inOrder.verify(mNetd).tetherApplyDnsInterfaces();
         inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
         inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
+        inOrder.verify(mAddressCoordinator).releaseDownstream(any());
         inOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
     }
 
     @Test
@@ -294,7 +304,8 @@
         initStateMachine(TETHERING_USB);
 
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
-        InOrder inOrder = inOrder(mCallback, mNetd);
+        InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
+        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                   IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
         inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -306,7 +317,7 @@
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
-        verifyNoMoreInteractions(mNetd, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
     }
 
     @Test
@@ -314,7 +325,8 @@
         initStateMachine(TETHERING_WIFI_P2P);
 
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
-        InOrder inOrder = inOrder(mCallback, mNetd);
+        InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
+        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
                   IFACE_NAME.equals(cfg.ifName) && assertNotContainsFlag(cfg.flags, IF_STATE_UP)));
         inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
@@ -326,7 +338,7 @@
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
-        verifyNoMoreInteractions(mNetd, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
     }
 
     @Test
@@ -392,18 +404,19 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNetd, mCallback);
+        InOrder inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
         inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNetd).tetherApplyDnsInterfaces();
         inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
         inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
         inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
+        inOrder.verify(mAddressCoordinator).releaseDownstream(any());
         inOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNetd, mCallback);
+        verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
     }
 
     @Test
@@ -483,7 +496,7 @@
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE);
 
-        assertDhcpStarted(new IpPrefix("192.168.43.0/24"));
+        assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
     }
 
     @Test
@@ -491,7 +504,7 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE);
 
-        assertDhcpStarted(new IpPrefix("192.168.44.0/24"));
+        assertDhcpStarted(mBluetoothPrefix);
     }
 
     @Test
@@ -499,7 +512,7 @@
         initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE);
 
-        assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
+        assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
     }
 
     @Test
@@ -524,21 +537,27 @@
         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 inOrder = inOrder(mNetd, mCallback, mAddressCoordinator);
+        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
         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(mCallback).updateInterfaceState(
+                mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
+        inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
+        verifyNoMoreInteractions(mCallback, mAddressCoordinator);
+
+        // Simulate the DHCP server receives DHCPDECLINE on MirrorLink and then signals
+        // onNewPrefixRequest callback.
+        final LinkAddress newAddress = new LinkAddress("192.168.100.125/24");
+        when(mAddressCoordinator.requestDownstreamAddress(any())).thenReturn(newAddress);
+        eventCallbacks.onNewPrefixRequest(new IpPrefix("192.168.42.0/24"));
+        mLooper.dispatchAll();
+
+        inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any());
         inOrder.verify(mNetd).tetherApplyDnsInterfaces();
         inOrder.verify(mCallback).updateLinkProperties(eq(mIpServer), lpCaptor.capture());
         verifyNoMoreInteractions(mCallback);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
new file mode 100644
index 0000000..93efd49
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.ip.IpServer;
+import android.net.util.NetworkConstants;
+import android.net.util.PrefixUtils;
+
+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.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class PrivateAddressCoordinatorTest {
+    private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
+    private static final String TEST_WIFI_IFNAME = "test_wlan0";
+
+    @Mock private IpServer mHotspotIpServer;
+    @Mock private IpServer mUsbIpServer;
+    @Mock private IpServer mEthernetIpServer;
+    @Mock private Context mContext;
+    @Mock private ConnectivityManager mConnectivityMgr;
+
+    private PrivateAddressCoordinator mPrivateAddressCoordinator;
+    private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24");
+    private final Network mWifiNetwork = new Network(1);
+    private final Network mMobileNetwork = new Network(2);
+    private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork};
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr);
+        when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
+        mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext));
+    }
+
+    @Test
+    public void testDownstreamPrefixRequest() throws Exception {
+        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
+        assertNotEquals(hotspotPrefix, mBluetoothPrefix);
+
+        address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        final IpPrefix testDupRequest = PrefixUtils.asIpPrefix(address);
+        assertNotEquals(hotspotPrefix, testDupRequest);
+        assertNotEquals(mBluetoothPrefix, testDupRequest);
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+
+        address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mUsbIpServer);
+        final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
+        assertNotEquals(usbPrefix, mBluetoothPrefix);
+        assertNotEquals(usbPrefix, hotspotPrefix);
+        mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
+    }
+
+    @Test
+    public void testRequestDownstreamAddress() throws Exception {
+        LinkAddress expectedAddress = new LinkAddress("192.168.43.42/24");
+        int fakeSubAddr = 0x2b00;
+        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+        LinkAddress actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        assertEquals(actualAddress, expectedAddress);
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+
+        fakeSubAddr = 0x2b01;
+        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+        actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        assertEquals(actualAddress, expectedAddress);
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+
+        fakeSubAddr = 0x2bff;
+        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+        actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        assertEquals(actualAddress, expectedAddress);
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+
+        expectedAddress = new LinkAddress("192.168.43.5/24");
+        fakeSubAddr = 0x2b05;
+        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+        actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        assertEquals(actualAddress, expectedAddress);
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+    }
+
+    @Test
+    public void testReserveBluetoothPrefix() throws Exception {
+        final int fakeSubAddr = 0x2c05;
+        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
+        assertNotEquals("Should not get reserved prefix: ", mBluetoothPrefix, hotspotPrefix);
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+    }
+
+    @Test
+    public void testNoConflictDownstreamPrefix() throws Exception {
+        final int fakeHotspotSubAddr = 0x2b05;
+        final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
+        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+        LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address);
+        assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
+        when(mHotspotIpServer.getAddress()).thenReturn(address);
+
+        address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mUsbIpServer);
+        final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(address);
+        assertNotEquals(predefinedPrefix, usbPrefix);
+
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+        mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
+        address = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mUsbIpServer);
+        final IpPrefix allowUseFreePrefix = PrefixUtils.asIpPrefix(address);
+        assertEquals("Fail to reselect available perfix: ", predefinedPrefix, allowUseFreePrefix);
+    }
+
+    private LinkProperties buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6,
+            boolean isMobile) {
+        final String testIface;
+        final String testIpv4Address;
+        if (isMobile) {
+            testIface = TEST_MOBILE_IFNAME;
+            testIpv4Address = "10.0.0.1";
+        } else {
+            testIface = TEST_WIFI_IFNAME;
+            testIpv4Address = "192.168.43.5";
+        }
+
+        final LinkProperties prop = new LinkProperties();
+        prop.setInterfaceName(testIface);
+
+        if (withIPv4) {
+            prop.addLinkAddress(
+                    new LinkAddress(InetAddresses.parseNumericAddress(testIpv4Address),
+                            NetworkConstants.IPV4_ADDR_BITS));
+        }
+
+        if (withIPv6) {
+            prop.addLinkAddress(
+                    new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
+                            NetworkConstants.RFC7421_PREFIX_LENGTH));
+        }
+        return prop;
+    }
+
+    @Test
+    public void testNoConflictUpstreamPrefix() throws Exception {
+        final int fakeHotspotSubId = 43;
+        final int fakeHotspotSubAddr = 0x2b05;
+        final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
+        // Force always get subAddress "43.5" for conflict testing.
+        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+        // 1. Enable hotspot with prefix 192.168.43.0/24
+        final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr);
+        assertEquals("Wrong wifi perfix: ", predefinedPrefix, hotspotPrefix);
+        when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr);
+        // 2. Update v6 only mobile network, hotspot prefix should not be removed.
+        List<String> testConflicts;
+        final LinkProperties v6OnlyMobileProp = buildUpstreamLinkProperties(false, true, true);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v6OnlyMobileProp);
+        verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork);
+        // 3. Update v4 only mobile network, hotspot prefix should not be removed.
+        final LinkProperties v4OnlyMobileProp = buildUpstreamLinkProperties(true, false, true);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4OnlyMobileProp);
+        verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        // 4. Update v4v6 mobile network, hotspot prefix should not be removed.
+        final LinkProperties v4v6MobileProp = buildUpstreamLinkProperties(true, true, true);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4v6MobileProp);
+        verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        // 5. Update v6 only wifi network, hotspot prefix should not be removed.
+        final LinkProperties v6OnlyWifiProp = buildUpstreamLinkProperties(false, true, false);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v6OnlyWifiProp);
+        verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
+        // 6. Update v4 only wifi network, it conflict with hotspot prefix.
+        final LinkProperties v4OnlyWifiProp = buildUpstreamLinkProperties(true, false, false);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
+        verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        reset(mHotspotIpServer);
+        // 7. Restart hotspot again and its prefix is different previous.
+        mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
+        final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer);
+        final IpPrefix hotspotPrefix2 = PrefixUtils.asIpPrefix(hotspotAddr2);
+        assertNotEquals(hotspotPrefix, hotspotPrefix2);
+        when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
+        verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        // 7. Usb tethering can be enabled and its prefix is different with conflict one.
+        final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mUsbIpServer);
+        final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(usbAddr);
+        assertNotEquals(predefinedPrefix, usbPrefix);
+        assertNotEquals(hotspotPrefix2, usbPrefix);
+        when(mUsbIpServer.getAddress()).thenReturn(usbAddr);
+        // 8. Disable wifi upstream, then wifi's prefix can be selected again.
+        mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
+        final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mEthernetIpServer);
+        final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr);
+        assertEquals(predefinedPrefix, ethPrefix);
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 7734a3c..5fffaae 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -32,6 +32,7 @@
 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.TETHER_ERROR_IFACE_CFG_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
@@ -84,6 +85,7 @@
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
 import android.net.EthernetManager;
+import android.net.EthernetManager.TetheredInterfaceCallback;
 import android.net.EthernetManager.TetheredInterfaceRequest;
 import android.net.IIntResultListener;
 import android.net.INetd;
@@ -169,9 +171,11 @@
     private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
     private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
     private static final String TEST_USB_IFNAME = "test_rndis0";
-    private static final String TEST_WLAN_IFNAME = "test_wlan0";
+    private static final String TEST_WIFI_IFNAME = "test_wlan0";
+    private static final String TEST_WLAN_IFNAME = "test_wlan1";
     private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
     private static final String TEST_NCM_IFNAME = "test_ncm0";
+    private static final String TEST_ETH_IFNAME = "test_eth0";
     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";
@@ -279,10 +283,11 @@
                             || ifName.equals(TEST_WLAN_IFNAME)
                             || ifName.equals(TEST_MOBILE_IFNAME)
                             || ifName.equals(TEST_P2P_IFNAME)
-                            || ifName.equals(TEST_NCM_IFNAME));
+                            || ifName.equals(TEST_NCM_IFNAME)
+                            || ifName.equals(TEST_ETH_IFNAME));
             final String[] ifaces = new String[] {
                     TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME,
-                    TEST_NCM_IFNAME};
+                    TEST_NCM_IFNAME, TEST_ETH_IFNAME};
             return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
                     MacAddress.ALL_ZEROS_ADDRESS);
         }
@@ -490,7 +495,7 @@
         when(mNetd.interfaceGetList())
                 .thenReturn(new String[] {
                         TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
-                        TEST_NCM_IFNAME});
+                        TEST_NCM_IFNAME, TEST_ETH_IFNAME});
         when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
         mInterfaceConfiguration = new InterfaceConfigurationParcel();
         mInterfaceConfiguration.flags = new String[0];
@@ -1836,6 +1841,109 @@
         mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
         sendConfigurationChanged();
     }
+
+    private static UpstreamNetworkState buildV4WifiUpstreamState(final String ipv4Address,
+            final int prefixLength, final Network network) {
+        final LinkProperties prop = new LinkProperties();
+        prop.setInterfaceName(TEST_WIFI_IFNAME);
+
+        prop.addLinkAddress(
+                new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address),
+                        prefixLength));
+
+        final NetworkCapabilities capabilities = new NetworkCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+        return new UpstreamNetworkState(prop, capabilities, network);
+    }
+
+    @Test
+    public void testHandleIpConflict() throws Exception {
+        final Network wifiNetwork = new Network(200);
+        final Network[] allNetworks = { wifiNetwork };
+        when(mCm.getAllNetworks()).thenReturn(allNetworks);
+        UpstreamNetworkState upstreamNetwork = null;
+        runUsbTethering(upstreamNetwork);
+        final ArgumentCaptor<InterfaceConfigurationParcel> ifaceConfigCaptor =
+                ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
+        verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture());
+        final String ipv4Address = ifaceConfigCaptor.getValue().ipv4Addr;
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
+        reset(mNetd, mUsbManager);
+        upstreamNetwork = buildV4WifiUpstreamState(ipv4Address, 30, wifiNetwork);
+        mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
+                Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+                0,
+                upstreamNetwork);
+        mLooper.dispatchAll();
+        // verify trun off usb tethering
+        verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        mTethering.interfaceRemoved(TEST_USB_IFNAME);
+        mLooper.dispatchAll();
+        // verify restart usb tethering
+        verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+    }
+
+    @Test
+    public void testNoAddressAvailable() throws Exception {
+        final Network wifiNetwork = new Network(200);
+        final Network[] allNetworks = { wifiNetwork };
+        when(mCm.getAllNetworks()).thenReturn(allNetworks);
+        final String upstreamAddress = "192.168.0.100";
+        runUsbTethering(null);
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
+        reset(mUsbManager);
+        final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
+        when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
+        final ArgumentCaptor<TetheredInterfaceCallback> callbackCaptor =
+                ArgumentCaptor.forClass(TetheredInterfaceCallback.class);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
+        mLooper.dispatchAll();
+        verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
+        TetheredInterfaceCallback ethCallback = callbackCaptor.getValue();
+        ethCallback.onAvailable(TEST_ETH_IFNAME);
+        mLooper.dispatchAll();
+        reset(mUsbManager, mEm);
+
+        final UpstreamNetworkState upstreamNetwork = buildV4WifiUpstreamState(
+                upstreamAddress, 16, wifiNetwork);
+        mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
+                Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+                0,
+                upstreamNetwork);
+        mLooper.dispatchAll();
+        // verify trun off usb tethering
+        verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        // verify trun off ethernet tethering
+        verify(mockRequest).release();
+        mTethering.interfaceRemoved(TEST_USB_IFNAME);
+        ethCallback.onUnavailable();
+        mLooper.dispatchAll();
+        // verify restart usb tethering
+        verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+        // verify restart ethernet tethering
+        verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
+        ethCallback = callbackCaptor.getValue();
+        ethCallback.onAvailable(TEST_ETH_IFNAME);
+
+        reset(mUsbManager, mEm);
+        when(mNetd.interfaceGetList())
+                .thenReturn(new String[] {
+                        TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME,
+                        TEST_NCM_IFNAME, TEST_ETH_IFNAME});
+
+        mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+        sendUsbBroadcast(true, true, true, TETHERING_USB);
+        mLooper.dispatchAll();
+        assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_USB_IFNAME);
+        assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_ETH_IFNAME);
+        assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_USB_IFNAME));
+        assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME));
+    }
+
     // TODO: Test that a request for hotspot mode doesn't interfere with an
     // already operating tethering mode interface.
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6e026ab..4b66cea 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -8183,6 +8183,11 @@
                 + "creators");
         }
 
+        // Instead of passing the data stall directly to the ConnectivityDiagnostics handler, treat
+        // this as a Data Stall received directly from NetworkMonitor. This requires wrapping the
+        // Data Stall information as a DataStallReportParcelable and passing to
+        // #notifyDataStallSuspected. This ensures that unknown Data Stall detection methods are
+        // still passed to ConnectivityDiagnostics (with new detection methods masked).
         final DataStallReportParcelable p = new DataStallReportParcelable();
         p.timestampMillis = timestampMillis;
         p.detectionMethod = detectionMethod;
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 34d0bed..3091a71 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -198,6 +198,9 @@
         if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) {
             stopPrefixDiscovery();
         }
+        if (!mPrefixDiscoveryRunning) {
+            setPrefix64(mNat64PrefixInUse);
+        }
     }
 
     /**
@@ -221,6 +224,10 @@
         mIface = null;
         mBaseIface = null;
 
+        if (!mPrefixDiscoveryRunning) {
+            setPrefix64(null);
+        }
+
         if (isPrefixDiscoveryNeeded()) {
             if (!mPrefixDiscoveryRunning) {
                 startPrefixDiscovery();
@@ -308,6 +315,16 @@
         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)) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 730da28..0cda7ca 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2867,6 +2867,23 @@
         return Credentials.PLATFORM_VPN + mUserHandle + "_" + packageName;
     }
 
+    @VisibleForTesting
+    void validateRequiredFeatures(VpnProfile profile) {
+        switch (profile.type) {
+            case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+            case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+            case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+                if (!mContext.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_IPSEC_TUNNELS)) {
+                    throw new UnsupportedOperationException(
+                            "Ikev2VpnProfile(s) requires PackageManager.FEATURE_IPSEC_TUNNELS");
+                }
+                break;
+            default:
+                return;
+        }
+    }
+
     /**
      * Stores an app-provisioned VPN profile and returns whether the app is already prepared.
      *
@@ -2883,6 +2900,7 @@
 
         verifyCallingUidAndPackage(packageName);
         enforceNotRestrictedUser();
+        validateRequiredFeatures(profile);
 
         if (profile.isRestrictedToTestNetworks) {
             mContext.enforceCallingPermission(Manifest.permission.MANAGE_TEST_NETWORKS,
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 3dac106..86ad0b3 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -152,12 +152,10 @@
 
     /**
      * Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
-     * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map, boolean)
+     * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
      */
-    public void apply464xlatAdjustments(NetworkStats baseTraffic,
-            NetworkStats stackedTraffic, boolean useBpfStats) {
-        NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces,
-                useBpfStats);
+    public void apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic) {
+        NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
     }
 
     public NetworkStatsFactory() {
@@ -380,7 +378,7 @@
         // network, the overhead is their fault.
         // No locking here: apply464xlatAdjustments behaves fine with an add-only
         // ConcurrentHashMap.
-        delta.apply464xlatAdjustments(mStackedIfaces, mUseBpfStats);
+        delta.apply464xlatAdjustments(mStackedIfaces);
 
         // Migrate data usage over a VPN to the TUN network.
         for (VpnInfo info : vpnArray) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 5d1cc2a..ba538e1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1306,21 +1306,39 @@
                 }
 
                 // Traffic occurring on stacked interfaces is usually clatd.
-                // UID stats are always counted on the stacked interface and never
-                // on the base interface, because the packets on the base interface
-                // do not actually match application sockets until they are translated.
                 //
-                // Interface stats are more complicated. Packets subject to BPF offload
-                // never appear on the base interface and only appear on the stacked
-                // interface, so to ensure those packets increment interface stats, interface
-                // stats from stacked interfaces must be collected.
+                // UID stats are always counted on the stacked interface and never on the base
+                // interface, because the packets on the base interface do not actually match
+                // application sockets (they're not IPv4) and thus the app uid is not known.
+                // For receive this is obvious: packets must be translated from IPv6 to IPv4
+                // before the application socket can be found.
+                // For transmit: either they go through the clat daemon which by virtue of going
+                // through userspace strips the original socket association during the IPv4 to
+                // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
+                // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
+                // which don't trigger again post ebpf translation.
+                // (as such stats accounted to the clat uid are ignored)
+                //
+                // Interface stats are more complicated.
+                //
+                // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
+                // *all* statistics are collected by iptables on the stacked v4-* interface.
+                //
+                // Additionally for ingress all packets bound for the clat IPv6 address are dropped
+                // in ip6tables raw prerouting and thus even non-offloaded packets are only
+                // accounted for on the stacked interface.
+                //
+                // For egress, packets subject to eBPF offload never appear on the base interface
+                // and only appear on the stacked interface. Thus to ensure packets increment
+                // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
+                // (or non eBPF offloaded) TX they would appear on both, however egress interface
+                // accounting is explicitly bypassed for traffic from the clat uid.
+                //
                 final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
                 for (LinkProperties stackedLink : stackedLinks) {
                     final String stackedIface = stackedLink.getInterfaceName();
                     if (stackedIface != null) {
-                        if (mUseBpfTrafficStats) {
-                            findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
-                        }
+                        findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
                         findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
                         if (isMobile) {
                             mobileIfaces.add(stackedIface);
@@ -1859,14 +1877,13 @@
         // fold tethering stats and operations into uid snapshot
         final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
         tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
-        mStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot,
-                mUseBpfTrafficStats);
+        mStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
         uidSnapshot.combineAllValues(tetherSnapshot);
 
         // get a stale copy of uid stats snapshot provided by providers.
         final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID);
         providerStats.filter(UID_ALL, ifaces, TAG_ALL);
-        mStatsFactory.apply464xlatAdjustments(uidSnapshot, providerStats, mUseBpfTrafficStats);
+        mStatsFactory.apply464xlatAdjustments(uidSnapshot, providerStats);
         uidSnapshot.combineAllValues(providerStats);
 
         uidSnapshot.combineAllValues(mUidOperations);
diff --git a/services/core/jni/com_android_server_SerialService.cpp b/services/core/jni/com_android_server_SerialService.cpp
index aef0b25..c945999 100644
--- a/services/core/jni/com_android_server_SerialService.cpp
+++ b/services/core/jni/com_android_server_SerialService.cpp
@@ -48,6 +48,7 @@
 
     jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
     if (fileDescriptor == NULL) {
+        close(fd);
         return NULL;
     }
     return env->NewObject(gParcelFileDescriptorOffsets.mClass,
diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp
index ff1ec04..72dce4d 100644
--- a/services/core/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp
@@ -19,6 +19,7 @@
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
 #include "MtpDescriptors.h"
@@ -88,6 +89,7 @@
     }
     jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
     if (fileDescriptor == NULL) {
+        close(fd);
         return NULL;
     }
     return env->NewObject(gParcelFileDescriptorOffsets.mClass,
@@ -120,35 +122,30 @@
 }
 
 static jobject android_server_UsbDeviceManager_openControl(JNIEnv *env, jobject /* thiz */, jstring jFunction) {
-    const char *function = env->GetStringUTFChars(jFunction, NULL);
+    ScopedUtfChars function(env, jFunction);
     bool ptp = false;
     int fd = -1;
-    if (!strcmp(function, "ptp")) {
+    if (!strcmp(function.c_str(), "ptp")) {
         ptp = true;
     }
-    if (!strcmp(function, "mtp") || ptp) {
+    if (!strcmp(function.c_str(), "mtp") || ptp) {
         fd = TEMP_FAILURE_RETRY(open(ptp ? FFS_PTP_EP0 : FFS_MTP_EP0, O_RDWR));
         if (fd < 0) {
-            ALOGE("could not open control for %s %s", function, strerror(errno));
-            goto error;
+            ALOGE("could not open control for %s %s", function.c_str(), strerror(errno));
+            return NULL;
         }
         if (!writeDescriptors(fd, ptp)) {
-            goto error;
+            close(fd);
+            return NULL;
         }
     }
 
-    if (function != NULL) {
-        env->ReleaseStringUTFChars(jFunction, function);
-    }
-    return jniCreateFileDescriptor(env, fd);
-error:
-    if (fd != -1) {
+    jobject jifd = jniCreateFileDescriptor(env, fd);
+    if (jifd == NULL) {
+        // OutOfMemoryError will be pending.
         close(fd);
     }
-    if (function != NULL) {
-        env->ReleaseStringUTFChars(jFunction, function);
-    }
-    return NULL;
+    return jifd;
 }
 
 static const JNINativeMethod method_table[] = {
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index 24f2014..a40bcd0 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -134,6 +134,7 @@
 
     jobject fileDescriptor = jniCreateFileDescriptor(env, newFD);
     if (fileDescriptor == NULL) {
+        close(newFD);
         return NULL;
     }
     return env->NewObject(gParcelFileDescriptorOffsets.mClass,
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index 79d935f..8ac2c4f 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -20,6 +20,7 @@
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
 
@@ -99,24 +100,45 @@
         int fd = open(path, O_RDWR);
         if (fd < 0) {
             ALOGE("open failed on %s for index %d", path, i);
-            return NULL;
+            goto release_fds;
         }
-
-        jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
-        env->SetObjectArrayElement(fds, i, fileDescriptor);
-        env->DeleteLocalRef(fileDescriptor);
+        ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, fd));
+        if (jifd.get() == NULL) {
+            goto release_fds;
+        }
+        env->SetObjectArrayElement(fds, i, jifd.get());
     }
 
     // create a pipe to use for unblocking our input thread
-    int pipeFD[2];
-    pipe(pipeFD);
-    jobject fileDescriptor = jniCreateFileDescriptor(env, pipeFD[0]);
-    env->SetObjectArrayElement(fds, subdevice_count, fileDescriptor);
-    env->DeleteLocalRef(fileDescriptor);
-    // store our end of the pipe in mPipeFD
-    env->SetIntField(thiz, sPipeFDField, pipeFD[1]);
+    {
+        int pipeFD[2];
+        if (pipe(pipeFD) == -1) {
+            ALOGE("pipe() failed, errno = %d", errno);
+            goto release_fds;
+        }
 
+        ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, pipeFD[0]));
+        if (jifd.get() == NULL) {
+            close(pipeFD[0]);
+            close(pipeFD[1]);
+            goto release_fds;
+        }
+        env->SetObjectArrayElement(fds, subdevice_count, jifd.get());
+        // store our end of the pipe in mPipeFD
+        env->SetIntField(thiz, sPipeFDField, pipeFD[1]);
+    }
     return fds;
+
+release_fds:
+    for (int i = 0; i < subdevice_count + 1; ++i) {
+        ScopedLocalRef<jobject> jifd(env, env->GetObjectArrayElement(fds, i));
+        if (jifd.get() == NULL) {
+            break;
+        }
+        int fd = jniGetFDFromFileDescriptor(env, jifd.get());
+        close(fd);
+    }
+    return NULL;
 }
 
 static void
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index d331d1f..89bcb7f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -166,6 +166,7 @@
             outPointerIcon->bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
                     bitmapCopy->rowBytes(), 0, 0);
         }
+        outSpriteIcon->style = outPointerIcon->style;
         outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
         outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
     }
@@ -318,7 +319,6 @@
     void updateInactivityTimeoutLocked();
     void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
     void ensureSpriteControllerLocked();
-    const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
     int32_t getPointerDisplayId();
     void updatePointerDisplayLocked();
     static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
@@ -396,16 +396,6 @@
     return false;
 }
 
-const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
-        REQUIRES(mLock) {
-    for (const DisplayViewport& v : mLocked.viewports) {
-        if (v.displayId == displayId) {
-            return &v;
-        }
-    }
-    return nullptr;
-}
-
 void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
     std::vector<DisplayViewport> viewports;
 
@@ -554,6 +544,8 @@
 
         outConfig->setDisplayViewports(mLocked.viewports);
 
+        outConfig->defaultPointerDisplayId = mLocked.pointerDisplayId;
+
         outConfig->disabledDevices = mLocked.disabledInputDevices;
     } // release lock
 }
@@ -571,8 +563,6 @@
         updateInactivityTimeoutLocked();
     }
 
-    updatePointerDisplayLocked();
-
     return controller;
 }
 
@@ -587,23 +577,6 @@
     return pointerDisplayId;
 }
 
-void NativeInputManager::updatePointerDisplayLocked() REQUIRES(mLock) {
-    ATRACE_CALL();
-
-    sp<PointerController> controller = mLocked.pointerController.promote();
-    if (controller != nullptr) {
-        const DisplayViewport* viewport = findDisplayViewportLocked(mLocked.pointerDisplayId);
-        if (viewport == nullptr) {
-            ALOGW("Can't find pointer display viewport, fallback to default display.");
-            viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
-        }
-
-        if (viewport != nullptr) {
-            controller->setDisplayViewport(*viewport);
-        }
-    }
-}
-
 void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
     if (mLocked.spriteController == nullptr) {
         JNIEnv* env = jniEnv();
@@ -1252,7 +1225,8 @@
     status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(),
             displayContext.get(), &pointerIcon);
     if (!status && !pointerIcon.isNullIcon()) {
-        *icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
+        *icon = SpriteIcon(
+                pointerIcon.bitmap, pointerIcon.style, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
     } else {
         *icon = SpriteIcon();
     }
@@ -1293,10 +1267,12 @@
                     milliseconds_to_nanoseconds(pointerIcon.durationPerFrame);
             animationData.animationFrames.reserve(numFrames);
             animationData.animationFrames.push_back(SpriteIcon(
-                    pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+                    pointerIcon.bitmap, pointerIcon.style,
+                    pointerIcon.hotSpotX, pointerIcon.hotSpotY));
             for (size_t i = 0; i < numFrames - 1; ++i) {
               animationData.animationFrames.push_back(SpriteIcon(
-                      pointerIcon.bitmapFrames[i], pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+                      pointerIcon.bitmapFrames[i], pointerIcon.style,
+                      pointerIcon.hotSpotX, pointerIcon.hotSpotY));
             }
         }
     }
@@ -1732,6 +1708,7 @@
         pointerIcon.bitmap.readPixels(spriteInfo, spriteIcon.bitmap.getPixels(),
                 spriteIcon.bitmap.rowBytes(), 0, 0);
     }
+    spriteIcon.style = pointerIcon.style;
     spriteIcon.hotSpotX = pointerIcon.hotSpotX;
     spriteIcon.hotSpotY = pointerIcon.hotSpotY;
     im->setCustomPointerIcon(spriteIcon);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0167a3b..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();
     }
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 9ec3f61..6840e8e 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -430,7 +430,6 @@
     /**
      * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
      *     notifies the framework.
-     * @hide
      */
     private void setListener(IImsMmTelListener listener) {
         synchronized (mLock) {
@@ -442,6 +441,16 @@
     }
 
     /**
+     * @return the listener associated with this MmTelFeature. May be null if it has not been set
+     * by the framework yet.
+     */
+    private IImsMmTelListener getListener() {
+        synchronized (mLock) {
+            return mListener;
+        }
+    }
+
+    /**
      * The current capability status that this MmTelFeature has defined is available. This
      * configuration will be used by the platform to figure out which capabilities are CURRENTLY
      * available to be used.
@@ -489,15 +498,14 @@
             throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
                     + "null.");
         }
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Session is not available.");
-            }
-            try {
-                mListener.onIncomingCall(c.getServiceImpl(), extras);
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
-            }
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onIncomingCall(c.getServiceImpl(), extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
         }
     }
 
@@ -516,15 +524,14 @@
             throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
                     + "null.");
         }
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Session is not available.");
-            }
-            try {
-                mListener.onRejectedCall(callProfile, reason);
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
-            }
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onRejectedCall(callProfile, reason);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
         }
     }
 
@@ -533,15 +540,14 @@
      * @hide
      */
     public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Session is not available.");
-            }
-            try {
-                mListener.onIncomingCall(c, extras);
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
-            }
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onIncomingCall(c, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
         }
     }
 
@@ -552,15 +558,14 @@
      */
     @SystemApi @TestApi
     public final void notifyVoiceMessageCountUpdate(int count) {
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Session is not available.");
-            }
-            try {
-                mListener.onVoiceMessageCountUpdate(count);
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
-            }
+        IImsMmTelListener listener = getListener();
+        if (listener == null) {
+            throw new IllegalStateException("Session is not available.");
+        }
+        try {
+            listener.onVoiceMessageCountUpdate(count);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
         }
     }
 
diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java
index 442ac56..0b13800 100644
--- a/tests/net/java/android/net/IpMemoryStoreTest.java
+++ b/tests/net/java/android/net/IpMemoryStoreTest.java
@@ -105,7 +105,7 @@
 
     private static NetworkAttributes buildTestNetworkAttributes(String hint, int mtu) {
         return new NetworkAttributes.Builder()
-                .setGroupHint(hint)
+                .setCluster(hint)
                 .setMtu(mtu)
                 .build();
     }
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 98f705f..735fa7c 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -909,8 +909,8 @@
                 13805 /* txPackets */,
                 0 /* operations */);
 
-        // Traffic measured for the root uid on the base interface if eBPF is in use.
-        final NetworkStats.Entry ebpfRootUidEntry = new NetworkStats.Entry(
+        // Traffic measured for the root uid on the base interface.
+        final NetworkStats.Entry rootUidEntry = new NetworkStats.Entry(
                 baseIface, rootUid, SET_DEFAULT, TAG_NONE,
                 163577 /* rxBytes */,
                 187 /* rxPackets */,
@@ -918,17 +918,6 @@
                 97 /* txPackets */,
                 0 /* operations */);
 
-        // Traffic measured for the root uid on the base interface if xt_qtaguid is in use.
-        // Incorrectly includes appEntry's bytes and packets, plus IPv4-IPv6 translation
-        // overhead (20 bytes per packet), in rx direction.
-        final NetworkStats.Entry xtRootUidEntry = new NetworkStats.Entry(
-                baseIface, rootUid, SET_DEFAULT, TAG_NONE,
-                31113087 /* rxBytes */,
-                22588 /* rxPackets */,
-                17607 /* txBytes */,
-                97 /* txPackets */,
-                0 /* operations */);
-
         final NetworkStats.Entry otherEntry = new NetworkStats.Entry(
                 otherIface, appUid, SET_DEFAULT, TAG_NONE,
                 2600  /* rxBytes */,
@@ -937,21 +926,14 @@
                 3 /* txPackets */,
                 0 /* operations */);
 
-        final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
+        final NetworkStats stats = new NetworkStats(TEST_START, 3)
                 .insertEntry(appEntry)
-                .insertEntry(xtRootUidEntry)
+                .insertEntry(rootUidEntry)
                 .insertEntry(otherEntry);
 
-        final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
-                .insertEntry(appEntry)
-                .insertEntry(ebpfRootUidEntry)
-                .insertEntry(otherEntry);
+        stats.apply464xlatAdjustments(stackedIface);
 
-        statsXt.apply464xlatAdjustments(stackedIface, false);
-        statsEbpf.apply464xlatAdjustments(stackedIface, true);
-
-        assertEquals(3, statsXt.size());
-        assertEquals(3, statsEbpf.size());
+        assertEquals(3, stats.size());
         final NetworkStats.Entry expectedAppUid = new NetworkStats.Entry(
                 v4Iface, appUid, SET_DEFAULT, TAG_NONE,
                 30949510,
@@ -966,12 +948,9 @@
                 17607,
                 97,
                 0);
-        assertEquals(expectedAppUid, statsXt.getValues(0, null));
-        assertEquals(expectedRootUid, statsXt.getValues(1, null));
-        assertEquals(otherEntry, statsXt.getValues(2, null));
-        assertEquals(expectedAppUid, statsEbpf.getValues(0, null));
-        assertEquals(expectedRootUid, statsEbpf.getValues(1, null));
-        assertEquals(otherEntry, statsEbpf.getValues(2, null));
+        assertEquals(expectedAppUid, stats.getValues(0, null));
+        assertEquals(expectedRootUid, stats.getValues(1, null));
+        assertEquals(otherEntry, stats.getValues(2, null));
     }
 
     @Test
@@ -996,7 +975,7 @@
                 .insertEntry(secondEntry);
 
         // Empty map: no adjustment
-        stats.apply464xlatAdjustments(new ArrayMap<>(), false);
+        stats.apply464xlatAdjustments(new ArrayMap<>());
 
         assertEquals(2, stats.size());
         assertEquals(firstEntry, stats.getValues(0, null));
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
index 1a3ea609..02f52865 100644
--- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -54,7 +54,7 @@
 
         builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9"));
         builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000);
-        builder.setGroupHint("groupHint");
+        builder.setCluster("groupHint");
         builder.setDnsAddresses(Arrays.asList(
                 InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"),
                 InetAddress.getByName("6.7.8.9")));
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 065ddd3..ea4982e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -6322,6 +6322,7 @@
         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());
@@ -6331,6 +6332,7 @@
         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
@@ -6340,6 +6342,7 @@
         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.
@@ -6347,6 +6350,7 @@
         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 */,
@@ -6363,6 +6367,7 @@
         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);
@@ -6372,6 +6377,7 @@
         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);
@@ -6385,6 +6391,7 @@
                 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.
@@ -6392,7 +6399,9 @@
         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);
 
@@ -6405,11 +6414,45 @@
         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);
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f8d8a56..4ccf79a 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -199,6 +199,8 @@
         when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
                 .thenReturn(Resources.getSystem().getString(
                         R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
+                .thenReturn(true);
         when(mSystemServices.isCallerSystem()).thenReturn(true);
 
         // Used by {@link Notification.Builder}
@@ -731,6 +733,20 @@
     }
 
     @Test
+    public void testProvisionVpnProfileNoIpsecTunnels() throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS))
+                .thenReturn(false);
+        final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+        try {
+            checkProvisionVpnProfile(
+                    vpn, true /* expectedResult */, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+            fail("Expected exception due to missing feature");
+        } catch (UnsupportedOperationException expected) {
+        }
+    }
+
+    @Test
     public void testProvisionVpnProfilePreconsented() throws Exception {
         final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
 
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index 4473492..e4996d9 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -446,7 +446,7 @@
         assertStatsEntry(stats, "v4-wlan0", 1000, SET_DEFAULT, 0x0, 30812L, 2310L);
         assertStatsEntry(stats, "v4-wlan0", 10102, SET_DEFAULT, 0x0, 10022L, 3330L);
         assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 9532772L, 254112L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 15229L, 0L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L);
         assertStatsEntry(stats, "wlan0", 1000, SET_DEFAULT, 0x0, 6126L, 2013L);
         assertStatsEntry(stats, "wlan0", 10013, SET_DEFAULT, 0x0, 0L, 144L);
         assertStatsEntry(stats, "wlan0", 10018, SET_DEFAULT, 0x0, 5980263L, 167667L);
@@ -468,9 +468,7 @@
         long appRxBytesAfter = 439237478L;
         assertEquals("App traffic should be ~100MB", 110553449, appRxBytesAfter - appRxBytesBefore);
 
-        long rootRxBytesBefore = 1394011L;
-        long rootRxBytesAfter = 1398634L;
-        assertEquals("UID 0 traffic should be ~0", 4623, rootRxBytesAfter - rootRxBytesBefore);
+        long rootRxBytes = 330187296L;
 
         mFactory.noteStackedIface("v4-wlan0", "wlan0");
         NetworkStats stats;
@@ -478,12 +476,12 @@
         // Stats snapshot before the download
         stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_before);
         assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesBefore, 5199872L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesBefore, 0L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L);
 
         // Stats snapshot after the download
         stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_100mb_download_after);
         assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
-        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 0L);
+        assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytes, 0L);
     }
 
     /**
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/tests/net/res/raw/xt_qtaguid_with_clat
index 6cd7499..f04b32f 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat
+++ b/tests/net/res/raw/xt_qtaguid_with_clat
@@ -7,7 +7,7 @@
 7 v4-wlan0 0x0 10060 1 1448660 1041 31192 753 1448660 1041 0 0 0 0 31192 753 0 0 0 0
 8 v4-wlan0 0x0 10102 0 9702 16 2870 23 9702 16 0 0 0 0 2870 23 0 0 0 0
 9 v4-wlan0 0x0 10102 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 wlan0 0x0 0 0 11058671 7892 0 0 11043898 7811 13117 61 1656 20 0 0 0 0 0 0
+10 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 11 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 12 wlan0 0x0 1000 0 6126 13 2013 16 5934 11 192 2 0 0 1821 14 192 2 0 0
 13 wlan0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
@@ -41,5 +41,3 @@
 41 dummy0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 42 lo 0x0 0 0 1288 16 1288 16 0 0 532 8 756 8 0 0 532 8 756 8
 43 lo 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-44 wlan0 0x0 1029 0 0 0 312046 5113 0 0 0 0 0 0 306544 5046 3230 38 2272 29
-45 wlan0 0x0 1029 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
index 9f86153..12d98ca 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after
@@ -9,7 +9,7 @@
 9 v4-wlan0 0x0 10057 1 728 7 392 7 0 0 728 7 0 0 0 0 392 7 0 0
 10 v4-wlan0 0x0 10106 0 2232 18 2232 18 0 0 2232 18 0 0 0 0 2232 18 0 0
 11 v4-wlan0 0x0 10106 1 432952718 314238 5442288 121260 432950238 314218 2480 20 0 0 5433900 121029 8388 231 0 0
-12 wlan0 0x0 0 0 440746376 329772 0 0 439660007 315369 232001 1276 854368 13127 0 0 0 0 0 0
+12 wlan0 0x0 0 0 330187296 250652 0 0 329106990 236273 226202 1255 854104 13124 0 0 0 0 0 0
 13 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 14 wlan0 0x0 1000 0 77113 272 56151 575 77113 272 0 0 0 0 19191 190 36960 385 0 0
 15 wlan0 0x0 1000 1 20227 80 8356 72 18539 74 1688 6 0 0 7562 66 794 6 0 0
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/tests/net/res/raw/xt_qtaguid_with_clat_simple
index b37fae6..a1d6d411 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_simple
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_simple
@@ -1,5 +1,4 @@
 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 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0
 3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0
-5 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0
+4 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0