Merge "Mark old APIs as @removed"
diff --git a/Android.bp b/Android.bp
index b4ea61a..b2b3dff 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1070,53 +1070,63 @@
      "-federate SupportLib https://developer.android.com " +
      "-federationapi SupportLib $(location current/support-api.txt) "
 
-doc_defaults {
-    name: "api-stubs-default",
-    srcs: [
-        ":opt-telephony-srcs",
-        ":opt-net-voip-srcs",
-        ":openjdk_javadoc_files",
-        ":non_openjdk_javadoc_files",
-        ":android_icu4j_src_files_for_docs",
-    ],
-    srcs_lib: "framework",
-    srcs_lib_whitelist_dirs: frameworks_base_subdirs,
-    srcs_lib_whitelist_pkgs: packages_to_document,
-    libs: [
-        "core-oj",
-        "core-libart",
-        "conscrypt",
-        "bouncycastle",
-        "okhttp",
-        "ext",
-        "framework",
-        "voip-common",
-        "android.test.mock.impl",
-    ],
-    local_sourcepaths: frameworks_base_subdirs,
-    html_dirs: [
-        "docs/html",
-    ],
-    knowntags: [
-        "docs/knowntags.txt",
-        ":known-oj-tags",
-    ],
-    custom_template: "droiddoc-templates-sdk",
-    hdf: [
-        "dac true",
-        "sdk.codename O",
-        "sdk.preview.version 1",
-        "sdk.version 7.0",
-        "sdk.rel.id 1",
-        "sdk.preview 0",
-    ],
-    resourcesdir: "docs/html/reference/images",
-    resourcesoutdir: "reference/android/images",
-    installable: false,
-}
+framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " +
+     "-overview $(location core/java/overview.html) " +
+     // Federate Support Library references against local API file.
+     "-federate SupportLib https://developer.android.com " +
+     "-federationapi SupportLib $(location current/support-api.txt) "
 
-doc_defaults {
-    name: "framework-docs-default",
+framework_docs_only_libs = [
+    "conscrypt",
+    "bouncycastle",
+    "voip-common",
+    "android.test.mock",
+    "android-support-annotations",
+    "android-support-compat",
+    "android-support-core-ui",
+    "android-support-core-utils",
+    "android-support-customtabs",
+    "android-support-design",
+    "android-support-dynamic-animation",
+    "android-support-exifinterface",
+    "android-support-fragment",
+    "android-support-media-compat",
+    "android-support-percent",
+    "android-support-recommendation",
+    "android-support-transition",
+    "android-support-tv-provider",
+    "android-support-v7-cardview",
+    "android-support-v7-gridlayout",
+    "android-support-v7-mediarouter",
+    "android-support-v7-palette",
+    "android-support-v7-preference",
+    "android-support-v13",
+    "android-support-v14-preference",
+    "android-support-v17-leanback",
+    "android-support-v17-preference-leanback",
+    "android-support-wear",
+    "android-support-vectordrawable",
+    "android-support-animatedvectordrawable",
+    "android-support-v7-appcompat",
+    "android-support-v7-recyclerview",
+    "android-support-emoji",
+    "android-support-emoji-appcompat",
+    "android-support-emoji-bundled",
+    "android-support-v8-renderscript",
+    "android-support-multidex",
+    "android-support-multidex-instrumentation",
+]
+
+metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
+    "--hide-package com.android.okhttp " +
+    "--hide-package com.android.org.conscrypt --hide-package com.android.server " +
+    "--hide RequiresPermission " +
+    "--hide MissingPermission --hide BroadcastBehavior " +
+    "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
+    "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
+
+stubs_defaults {
+    name: "framework-doc-stubs-default",
     srcs: [
         "test-base/src/**/*.java",
         ":opt-telephony-srcs",
@@ -1130,47 +1140,25 @@
     srcs_lib: "framework",
     srcs_lib_whitelist_dirs: frameworks_base_subdirs,
     srcs_lib_whitelist_pkgs: packages_to_document,
-    libs: [
-        "conscrypt",
-        "bouncycastle",
-        "voip-common",
-        "android.test.mock",
-        "android-support-annotations",
-        "android-support-compat",
-        "android-support-core-ui",
-        "android-support-core-utils",
-        "android-support-customtabs",
-        "android-support-design",
-        "android-support-dynamic-animation",
-        "android-support-exifinterface",
-        "android-support-fragment",
-        "android-support-media-compat",
-        "android-support-percent",
-        "android-support-recommendation",
-        "android-support-transition",
-        "android-support-tv-provider",
-        "android-support-v7-cardview",
-        "android-support-v7-gridlayout",
-        "android-support-v7-mediarouter",
-        "android-support-v7-palette",
-        "android-support-v7-preference",
-        "android-support-v13",
-        "android-support-v14-preference",
-        "android-support-v17-leanback",
-        "android-support-v17-preference-leanback",
-        "android-support-wear",
-        "android-support-vectordrawable",
-        "android-support-animatedvectordrawable",
-        "android-support-v7-appcompat",
-        "android-support-v7-recyclerview",
-        "android-support-emoji",
-        "android-support-emoji-appcompat",
-        "android-support-emoji-bundled",
-        "android-support-v8-renderscript",
-        "android-support-multidex",
-        "android-support-multidex-instrumentation",
-    ],
+    libs: framework_docs_only_libs,
     local_sourcepaths: frameworks_base_subdirs,
+    create_doc_stubs: true,
+    annotations_enabled: true,
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
+    previous_api: ":last-released-public-api",
+    merge_annotations_dirs: [
+        "metalava-manual",
+        "ojluni-annotated-sdk-stubs",
+    ],
+}
+
+doc_defaults {
+    name: "framework-docs-default",
+    libs: framework_docs_only_libs,
     html_dirs: [
         "docs/html",
     ],
@@ -1191,22 +1179,12 @@
     ],
     arg_files: [
         "core/res/AndroidManifest.xml",
-        ":api-version-xml",
         "core/java/overview.html",
         ":current-support-api",
-        "api/current.txt",
     ],
     create_stubs: false,
 }
 
-metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
-    "--hide-package com.android.okhttp " +
-    "--hide-package com.android.org.conscrypt --hide-package com.android.server " +
-    "--hide RequiresPermission " +
-    "--hide MissingPermission --hide BroadcastBehavior " +
-    "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
-    "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
-
 stubs_defaults {
     name: "metalava-api-stubs-default",
     srcs: [
@@ -1236,25 +1214,54 @@
     previous_api: ":last-released-public-api",
     merge_annotations_dirs: [
         "metalava-manual",
-        "ojluni-annotated-stubs",
+        "ojluni-annotated-sdk-stubs",
     ],
+    api_levels_annotations_enabled: true,
+    api_levels_annotations_dirs: [
+        "sdk-dir",
+        "api-versions-jars-dir",
+    ],
+}
+
+droidstubs {
+    name: "framework-doc-stubs",
+    defaults: ["framework-doc-stubs-default"],
+    arg_files: [
+        "core/res/AndroidManifest.xml",
+    ],
+    args: metalava_framework_docs_args,
+}
+
+droidstubs {
+    name: "framework-doc-system-stubs",
+    defaults: ["framework-doc-stubs-default"],
+    arg_files: [
+        "core/res/AndroidManifest.xml",
+    ],
+    args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi ",
 }
 
 droiddoc {
     name: "doc-comment-check-docs",
     defaults: ["framework-docs-default"],
-    args: framework_docs_args + " -referenceonly -parsecomments",
+    srcs: [
+        ":framework-doc-stubs",
+    ],
+    args: framework_docs_only_args + " -referenceonly -parsecomments",
     installable: false,
 }
 
 droiddoc {
     name: "offline-sdk-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-stubs",
+    ],
     hdf: [
         "android.whichdoc offline",
     ],
     proofread_file: "offline-sdk-docs-proofrerad.txt",
-    args: framework_docs_args + " -offlinemode -title \"Android SDK\"",
+    args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"",
     write_sdk_values: true,
     static_doc_index_redirect: "docs/docs-preview-index.html",
 }
@@ -1262,11 +1269,14 @@
 droiddoc {
     name: "offline-sdk-referenceonly-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-stubs",
+    ],
     hdf: [
         "android.whichdoc offline",
     ],
     proofread_file: "offline-sdk-referenceonly-docs-proofrerad.txt",
-    args: framework_docs_args + " -offlinemode -title \"Android SDK\" -referenceonly",
+    args: framework_docs_only_args + " -offlinemode -title \"Android SDK\" -referenceonly",
     write_sdk_values: true,
     static_doc_index_redirect: "docs/docs-documentation-redirect.html",
     static_doc_properties: "docs/source.properties",
@@ -1275,13 +1285,15 @@
 droiddoc {
     name: "offline-system-sdk-referenceonly-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-system-stubs",
+    ],
     hdf: [
         "android.whichdoc offline",
     ],
     proofread_file: "offline-system-sdk-referenceonly-docs-proofrerad.txt",
-    args: framework_docs_args + " -hide 101 -hide 104 -hide 108" +
-          " -showAnnotation android.annotation.SystemApi " +
-          " -offlinemode -title \"Android System SDK\" -referenceonly",
+    args: framework_docs_only_args  + " -hide 101 -hide 104 -hide 108" +
+        " -offlinemode -title \"Android System SDK\" -referenceonly",
     write_sdk_values: true,
     static_doc_index_redirect: "docs/docs-documentation-redirect.html",
     static_doc_properties: "docs/source.properties",
@@ -1290,12 +1302,15 @@
 droiddoc {
     name: "online-sdk-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-stubs",
+    ],
     hdf: [
         "android.whichdoc online",
         "android.hasSamples true",
     ],
     proofread_file: "online-sdk-docs-proofrerad.txt",
-    args: framework_docs_args +
+    args: framework_docs_only_args  +
         " -toroot / -samplegroup Admin " +
         " -samplegroup Background " +
         " -samplegroup Connectivity " +
@@ -1316,14 +1331,16 @@
 droiddoc {
     name: "online-system-api-sdk-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-system-stubs",
+    ],
     hdf: [
         "android.whichdoc online",
         "android.hasSamples true",
     ],
     proofread_file: "online-system-api-sdk-docs-proofrerad.txt",
-    args: framework_docs_args +
+    args: framework_docs_only_args +
         " -referenceonly " +
-        " -showAnnotation android.annotation.SystemApi " +
         " -title \"Android SDK - Including system APIs.\" " +
         " -hide 101 " +
         " -hide 104 " +
@@ -1349,12 +1366,15 @@
 droiddoc {
     name: "ds-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-stubs",
+    ],
     hdf: [
         "android.whichdoc online",
         "android.hasSamples true",
     ],
     proofread_file: "ds-docs-proofrerad.txt",
-    args: framework_docs_args +
+    args: framework_docs_only_args +
         " -toroot / -samplegroup Admin " +
         " -samplegroup Background " +
         " -samplegroup Connectivity " +
@@ -1375,11 +1395,14 @@
 droiddoc {
     name: "ds-static-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-stubs",
+    ],
     hdf: [
         "android.whichdoc online",
     ],
     proofread_file: "ds-static-docs-proofrerad.txt",
-    args: framework_docs_args +
+    args: framework_docs_only_args +
           " -staticonly " +
           " -toroot / " +
           " -devsite " +
@@ -1389,11 +1412,14 @@
 droiddoc {
     name: "ds-ref-navtree-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-stubs",
+    ],
     hdf: [
         "android.whichdoc online",
     ],
     proofread_file: "ds-ref-navtree-docs-proofrerad.txt",
-    args: framework_docs_args +
+    args: framework_docs_only_args +
           " -toroot / " +
           " -atLinksNavtree " +
           " -navtreeonly ",
@@ -1402,12 +1428,15 @@
 droiddoc {
     name: "online-sdk-dev-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-stubs",
+    ],
     hdf: [
         "android.whichdoc online",
         "android.hasSamples true",
     ],
     proofread_file: "online-sdk-dev-docs-proofrerad.txt",
-    args: framework_docs_args +
+    args: framework_docs_only_args +
         " -toroot / -samplegroup Admin " +
         " -samplegroup Background " +
         " -samplegroup Connectivity " +
@@ -1428,13 +1457,16 @@
 droiddoc {
     name: "hidden-docs",
     defaults: ["framework-docs-default"],
+    srcs: [
+        ":framework-doc-stubs",
+    ],
     proofread_file: "hidden-docs-proofrerad.txt",
-    args: framework_docs_args +
+    args: framework_docs_only_args +
           " -referenceonly " +
           " -title \"Android SDK - Including hidden APIs.\"",
 }
 
-droiddoc {
+droidstubs {
     name: "hwbinder-stubs-docs",
     srcs: [
         "core/java/android/os/HidlSupport.java",
@@ -1452,10 +1484,15 @@
         "core/java/android/os/RemoteException.java",
         "core/java/android/util/AndroidException.java",
     ],
-    custom_template: "droiddoc-templates-sdk",
     installable: false,
     no_framework_libs: true,
-    args: "-showAnnotation android.annotation.SystemApi -nodocs -stubsourceonly",
+    annotations_enabled: true,
+    previous_api: ":last-released-public-api",
+    merge_annotations_dirs: [
+        "metalava-manual",
+        "ojluni-annotated-sdk-stubs",
+    ],
+    args: " --show-annotation android.annotation.SystemApi",
 }
 
 java_library_static {
@@ -1482,23 +1519,17 @@
 }
 
 
-droiddoc {
+droidstubs {
     name: "hiddenapi-mappings",
-    defaults: ["api-stubs-default"],
+    defaults: ["metalava-api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
-        ":api-version-xml",
-        "core/java/overview.html",
-        ":current-support-api",
-        "api/current.txt",
     ],
     dex_mapping_filename: "dex-mapping.txt",
-    args: framework_docs_args +
-          " -referenceonly" +
-          " -nodocs" +
-          " -showUnannotated" +
-          " -showAnnotation android.annotation.SystemApi" +
-          " -showAnnotation android.annotation.TestApi",
+    args: metalava_framework_docs_args +
+        " --show-unannotated " +
+        " --show-annotation android.annotation.SystemApi " +
+        " --show-annotation android.annotation.TestApi "
 }
 
 filegroup {
@@ -1545,6 +1576,7 @@
             removed_api_file: "api/removed.txt",
         },
     },
+    jdiff_enabled: true,
 }
 
 droidstubs {
@@ -1569,6 +1601,7 @@
             removed_api_file: "api/system-removed.txt",
         },
     },
+    jdiff_enabled: true,
 }
 
 droidstubs {
diff --git a/Android.mk b/Android.mk
index 5c4c237..d333074 100644
--- a/Android.mk
+++ b/Android.mk
@@ -53,265 +53,16 @@
 	cat $^ | sort -u > $@.tmp
 	$(call commit-change-for-toc,$@)
 
-# the documentation
-# ============================================================
-
-# TODO: deal with com/google/android/googleapps
-packages_to_document := \
-  android \
-  javax/microedition/khronos \
-  org/apache/http/conn \
-  org/apache/http/params \
-
-# include definition of libcore_to_document
-include libcore/Docs.mk
-
-non_base_dirs := \
-  ../opt/telephony/src/java/android/telephony \
-  ../opt/telephony/src/java/android/telephony/gsm \
-  ../opt/net/voip/src/java/android/net/rtp \
-  ../opt/net/voip/src/java/android/net/sip \
-
-# Find all files in specific directories (relative to frameworks/base)
-# to document and check apis
-files_to_check_apis := \
-  $(call find-other-java-files, \
-    $(non_base_dirs) \
-  )
-
-# Find all files in specific packages that were used to compile
-# framework.jar to document and check apis
-files_to_check_apis += \
-  $(addprefix ../../,\
-    $(filter \
-      $(foreach dir,$(FRAMEWORKS_BASE_JAVA_SRC_DIRS),\
-        $(foreach package,$(packages_to_document),\
-          $(dir)/$(package)/%.java)),\
-      $(SOONG_FRAMEWORK_SRCS)))
-
-# Find all generated files that were used to compile framework.jar
-files_to_check_apis_generated := \
-  $(filter $(OUT_DIR)/%,\
-    $(SOONG_FRAMEWORK_SRCS))
-
-# These are relative to frameworks/base
-# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
-files_to_document := \
-  $(files_to_check_apis) \
-  $(call find-other-java-files,\
-    test-base/src \
-    test-mock/src \
-    test-runner/src)
-
-# These are relative to frameworks/base
-html_dirs := \
-	$(FRAMEWORKS_BASE_SUBDIRS) \
-	$(non_base_dirs) \
-
-# Common sources for doc check and api check
-common_src_files := \
-	$(call find-other-html-files, $(html_dirs)) \
-	$(addprefix ../../, $(libcore_to_document)) \
-
-# These are relative to frameworks/base
-framework_docs_LOCAL_SRC_FILES := \
-  $(files_to_document) \
-  $(common_src_files) \
-
-# These are relative to frameworks/base
-framework_docs_LOCAL_API_CHECK_SRC_FILES := \
-  $(files_to_check_apis) \
-  $(common_src_files) \
-
 # This is used by ide.mk as the list of source files that are
 # always included.
 INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
 
-framework_docs_LOCAL_DROIDDOC_SOURCE_PATH := \
-	$(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
-
-framework_docs_LOCAL_SRCJARS := $(SOONG_FRAMEWORK_SRCJARS)
-
-framework_docs_LOCAL_GENERATED_SOURCES := \
-  $(libcore_to_document_generated) \
-  $(files_to_check_apis_generated) \
-
-framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
-	core-oj \
-	core-libart \
-	conscrypt \
-	bouncycastle \
-	okhttp \
-	ext \
-	framework \
-	voip-common \
-
-# Platform docs can refer to Support Library APIs, but we don't actually build
-# them as part of the docs target, so we need to include them on the classpath.
-framework_docs_LOCAL_JAVA_LIBRARIES := \
-	$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES) \
-	$(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES)
-
-framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
-# The since flag (-since N.xml API_LEVEL) is used to add API Level information
-# to the reference documentation. Must be in order of oldest to newest.
-#
-# Conscrypt (com.android.org.conscrypt) is an implementation detail and should
-# not be referenced in the documentation.
-framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-    -android \
-    -knowntags ./frameworks/base/docs/knowntags.txt \
-    -knowntags ./libcore/known_oj_tags.txt \
-    -manifest ./frameworks/base/core/res/AndroidManifest.xml \
-    -hidePackage com.android.internal \
-    -hidePackage com.android.internal.util \
-    -hidePackage com.android.okhttp \
-    -hidePackage com.android.org.conscrypt \
-    -hidePackage com.android.server
-
-# Convert an sdk level to a "since" argument.
-since-arg = -since $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/$(1)/public/api/android.$(2)) $(1)
-
-finalized_xml_sdks := $(call numerically_sort,\
-    $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.xml,%,\
-    $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.xml)))
-finalized_txt_sdks := $(call numerically_sort,\
-     $(patsubst $(HISTORICAL_SDK_VERSIONS_ROOT)/%/public/api/android.txt,%,\
-    $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/public/api/android.txt)))
-
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_xml_sdks),$(call since-arg,$(sdk),xml))
-framework_docs_LOCAL_DROIDDOC_OPTIONS += $(foreach sdk,$(finalized_txt_sdks),$(call since-arg,$(sdk),txt))
-ifneq ($(PLATFORM_VERSION_CODENAME),REL)
-  framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-      -since ./frameworks/base/api/current.txt $(PLATFORM_VERSION_CODENAME)
-endif
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-    -werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
-    -overview $(LOCAL_PATH)/core/java/overview.html
-
-framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
-
-framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
-	$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-
-framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
-    frameworks/base/docs/knowntags.txt \
-    $(libcore_to_document_generated)
-
-samples_dir := development/samples/browseable
-
-# Whitelist of valid groups, used for default TOC grouping. Each sample must
-# belong to one (and only one) group. Assign samples to groups by setting
-# a sample.group var to one of these groups in the sample's _index.jd.
-sample_groups := -samplegroup Admin \
-                 -samplegroup Background \
-                 -samplegroup Connectivity \
-                 -samplegroup Content \
-                 -samplegroup Input \
-                 -samplegroup Media \
-                 -samplegroup Notification \
-                 -samplegroup RenderScript \
-                 -samplegroup Security \
-                 -samplegroup Sensors \
-                 -samplegroup System \
-                 -samplegroup Testing \
-                 -samplegroup UI \
-                 -samplegroup Views \
-                 -samplegroup Wearable
-
-## SDK version identifiers used in the published docs
-  # major[.minor] version for current SDK. (full releases only)
-framework_docs_SDK_VERSION:=7.0
-  # release version (ie "Release x")  (full releases only)
-framework_docs_SDK_REL_ID:=1
-
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-		-hdf dac true \
-		-hdf sdk.codename O \
-		-hdf sdk.preview.version 1 \
-		-hdf sdk.version $(framework_docs_SDK_VERSION) \
-		-hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \
-		-hdf sdk.preview 0 \
-		-resourcesdir $(LOCAL_PATH)/docs/html/reference/images/ \
-		-resourcesoutdir reference/android/images/
-
-# Federate Support Library references against local API file.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-		-federate SupportLib https://developer.android.com \
-		-federationapi SupportLib prebuilts/sdk/current/support-api.txt
-
-# Federate AndroidX references against local API file.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-		-federate AndroidX https://developer.android.com \
-		-federationapi AndroidX prebuilts/sdk/current/androidx-api.txt
-
-# Get the highest numbered api txt for the given api level.
-# $(1): the api level (e.g. public, system)
-define highest_sdk_txt
-$(HISTORICAL_SDK_VERSIONS_ROOT)/$(lastword $(call numerically_sort, \
-    $(patsubst \
-        $(HISTORICAL_SDK_VERSIONS_ROOT)/%,\
-        %,\
-        $(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/$(1)/api/android.txt)\
-    ) \
-))
-endef
-
-# ====  Public API diff ===========================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
-	$(INTERNAL_PLATFORM_API_FILE)
-
-LOCAL_MODULE := offline-sdk-referenceonly
-
-# Basename, because apidiff adds .txt internally.
-LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,public))
-LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_API_FILE))
-
-include $(BUILD_APIDIFF)
-
-# Hack to get diffs included in docs output
-out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
-$(out_zip): $(full_target)
-
-# ====  System API diff ===========================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-	$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
-	$(INTERNAL_PLATFORM_SYSTEM_API_FILE)
-
-LOCAL_MODULE := offline-system-sdk-referenceonly
-
-# Basename, because apidiff adds .txt internally.
-LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,system))
-LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_SYSTEM_API_FILE))
-
-include $(BUILD_APIDIFF)
-
-# Hack to get diffs included in docs output
-out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
-$(out_zip): $(full_target)
-
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt)
 
 # sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
 # So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
diff --git a/api/current.txt b/api/current.txt
index 2a1295c..955b13b 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -7698,7 +7698,9 @@
     method public boolean isMultipleAdvertisementSupported();
     method public boolean isOffloadedFilteringSupported();
     method public boolean isOffloadedScanBatchingSupported();
+    method public android.bluetooth.BluetoothServerSocket listenUsingInsecureL2capChannel() throws java.io.IOException;
     method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
+    method public android.bluetooth.BluetoothServerSocket listenUsingL2capChannel() throws java.io.IOException;
     method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
     method public boolean setName(java.lang.String);
     method public boolean startDiscovery();
@@ -8066,7 +8068,9 @@
     method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int);
     method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int, android.os.Handler);
     method public boolean createBond();
+    method public android.bluetooth.BluetoothSocket createInsecureL2capChannel(int) throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
+    method public android.bluetooth.BluetoothSocket createL2capChannel(int) throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
     method public int describeContents();
     method public boolean fetchUuidsWithSdp();
@@ -8482,6 +8486,7 @@
     method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
     method public void close() throws java.io.IOException;
+    method public int getPsm();
   }
 
   public final class BluetoothSocket implements java.io.Closeable {
@@ -18532,6 +18537,37 @@
     field public static final int WORD_NUMBER_LIMIT = 200; // 0xc8
   }
 
+  public abstract class CaseMap {
+    method public static android.icu.text.CaseMap.Fold fold();
+    method public abstract android.icu.text.CaseMap omitUnchangedText();
+    method public static android.icu.text.CaseMap.Lower toLower();
+    method public static android.icu.text.CaseMap.Title toTitle();
+    method public static android.icu.text.CaseMap.Upper toUpper();
+  }
+
+  public static final class CaseMap.Fold extends android.icu.text.CaseMap {
+    method public <A extends java.lang.Appendable> A apply(java.lang.CharSequence, A, android.icu.text.Edits);
+    method public android.icu.text.CaseMap.Fold omitUnchangedText();
+    method public android.icu.text.CaseMap.Fold turkic();
+  }
+
+  public static final class CaseMap.Lower extends android.icu.text.CaseMap {
+    method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
+    method public android.icu.text.CaseMap.Lower omitUnchangedText();
+  }
+
+  public static final class CaseMap.Title extends android.icu.text.CaseMap {
+    method public <A extends java.lang.Appendable> A apply(java.util.Locale, android.icu.text.BreakIterator, java.lang.CharSequence, A, android.icu.text.Edits);
+    method public android.icu.text.CaseMap.Title noBreakAdjustment();
+    method public android.icu.text.CaseMap.Title noLowercase();
+    method public android.icu.text.CaseMap.Title omitUnchangedText();
+  }
+
+  public static final class CaseMap.Upper extends android.icu.text.CaseMap {
+    method public <A extends java.lang.Appendable> A apply(java.util.Locale, java.lang.CharSequence, A, android.icu.text.Edits);
+    method public android.icu.text.CaseMap.Upper omitUnchangedText();
+  }
+
   public final class CollationElementIterator {
     method public int getMaxExpansion(int);
     method public int getOffset();
@@ -19197,6 +19233,30 @@
     enum_constant public static final android.icu.text.DisplayContext.Type SUBSTITUTE_HANDLING;
   }
 
+  public final class Edits {
+    ctor public Edits();
+    method public void addReplace(int, int);
+    method public void addUnchanged(int);
+    method public android.icu.text.Edits.Iterator getCoarseChangesIterator();
+    method public android.icu.text.Edits.Iterator getCoarseIterator();
+    method public android.icu.text.Edits.Iterator getFineChangesIterator();
+    method public android.icu.text.Edits.Iterator getFineIterator();
+    method public boolean hasChanges();
+    method public int lengthDelta();
+    method public void reset();
+  }
+
+  public static final class Edits.Iterator {
+    method public int destinationIndex();
+    method public boolean findSourceIndex(int);
+    method public boolean hasChange();
+    method public int newLength();
+    method public boolean next();
+    method public int oldLength();
+    method public int replacementIndex();
+    method public int sourceIndex();
+  }
+
   public abstract class IDNA {
     method public static android.icu.text.IDNA getUTS46Instance(int);
     method public abstract java.lang.StringBuilder labelToASCII(java.lang.CharSequence, java.lang.StringBuilder, android.icu.text.IDNA.Info);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ab9c7e8..18f6a1c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -272,9 +272,9 @@
  * Logs reporting of a ble scan finding results.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
  */
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
+// TODO: Consider also tracking per-scanner-id.
 message BleScanResultReceived {
     repeated AttributionNode attribution_node = 1;
 
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 3567605..9857cd0 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -11074,6 +11074,7 @@
 HPLjava/lang/Long;->valueOf(Ljava/lang/String;I)Ljava/lang/Long;
 HPLjava/lang/Math;->addExact(JJ)J
 HPLjava/lang/Math;->scalb(FI)F
+HPLjava/lang/Object;->wait()V
 HPLjava/lang/StackTraceElement;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V
 HPLjava/lang/String;->copyValueOf([C)Ljava/lang/String;
 HPLjava/lang/String;->join(Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index d520b28..9c94f96 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2205,6 +2205,7 @@
 Lcom/android/internal/telephony/SmsHeader$ConcatRef;-><init>()V
 Lcom/android/internal/telephony/SmsHeader$PortAddrs;-><init>()V
 Lcom/android/internal/telephony/SmsMessageBase;-><init>()V
+Lcom/android/internal/telephony/uicc/IccUtils;->bytesToHexString([B)Ljava/lang/String;
 Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String;
 Lcom/android/internal/view/BaseIWindow;-><init>()V
@@ -2293,17 +2294,11 @@
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V
 Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setHostname(Ljava/lang/String;)V
 Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setUseSessionTickets(Z)V
-Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostname()Ljava/lang/String;
-Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostnameOrIP()Ljava/lang/String;
-Lcom/android/org/conscrypt/ConscryptSocketBase;->getSoWriteTimeout()I
-Lcom/android/org/conscrypt/ConscryptSocketBase;->setHandshakeTimeout(I)V
-Lcom/android/org/conscrypt/ConscryptSocketBase;->setHostname(Ljava/lang/String;)V
-Lcom/android/org/conscrypt/ConscryptSocketBase;->setSoWriteTimeout(I)V
-Lcom/android/org/conscrypt/ConscryptSocketBase;->socket:Ljava/net/Socket;
 Lcom/android/org/conscrypt/OpenSSLKey;-><init>(J)V
 Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey;
 Lcom/android/org/conscrypt/OpenSSLKey;->getNativeRef()Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;
 Lcom/android/org/conscrypt/OpenSSLKey;->getPublicKey()Ljava/security/PublicKey;
+Lcom/android/org/conscrypt/OpenSSLProvider;-><init>()V
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String;
@@ -2325,6 +2320,108 @@
 Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateChain(Ljava/security/cert/X509Certificate;)Ljava/util/List;
 Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;)V
 Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
+Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
+Lcom/google/android/mms/MmsException;-><init>()V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
+Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
+Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
+Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
+Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
+Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
+Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
+Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
+Lcom/google/android/mms/pdu/PduBody;-><init>()V
+Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
+Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
+Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
+Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
+Lcom/google/android/mms/pdu/PduComposer;->make()[B
+Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPart;-><init>()V
+Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPart;->getCharset()I
+Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
+Lcom/google/android/mms/pdu/PduPart;->getData()[B
+Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
+Lcom/google/android/mms/pdu/PduPart;->getName()[B
+Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
+Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
+Lcom/google/android/mms/pdu/PduPart;->setData([B)V
+Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
+Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
+Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
+Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
+Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
+Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
+Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
+Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
+Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;-><init>()V
+Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
+Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
+Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
+Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
+Lcom/google/android/mms/util/PduCache;->purgeAll()V
+Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
 Ldalvik/system/BaseDexClassLoader;-><init>(Ljava/lang/String;Ljava/io/File;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V
 Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V
 Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V
@@ -2628,6 +2725,7 @@
 Ljava/net/SocketException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
 Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket;
 Ljava/net/SocketImpl;->socket:Ljava/net/Socket;
+Ljava/net/SocksSocketImpl;-><init>()V
 Ljava/net/URI;->fragment:Ljava/lang/String;
 Ljava/net/URI;->host:Ljava/lang/String;
 Ljava/net/URI;->port:I
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 4c655b5..654bfaf 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -81,7 +81,8 @@
  * {@link #getBondedDevices()}; start device discovery with
  * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
  * listen for incoming RFComm connection requests with {@link
- * #listenUsingRfcommWithServiceRecord(String, UUID)}; or start a scan for
+ * #listenUsingRfcommWithServiceRecord(String, UUID)}; listen for incoming L2CAP Connection-oriented
+ * Channels (CoC) connection requests with {@link #listenUsingL2capChannel()}; or start a scan for
  * Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
  * </p>
  * <p>This class is thread safe.</p>
@@ -2967,7 +2968,7 @@
     /**
      * Create a secure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
      * assign a dynamic protocol/service multiplexer (PSM) value. This socket can be used to listen
-     * for incoming connections.
+     * for incoming connections. The supported Bluetooth transport is LE only.
      * <p>A remote device connecting to this socket will be authenticated and communication on this
      * socket will be encrypted.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
@@ -2977,21 +2978,16 @@
      * closed, Bluetooth is turned off, or the application exits unexpectedly.
      * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
      * defined and performed by the application.
-     * <p>Use {@link BluetoothDevice#createL2capCocSocket(int, int)} to connect to this server
+     * <p>Use {@link BluetoothDevice#createL2capChannel(int)} to connect to this server
      * socket from another Android device that is given the PSM value.
      *
-     * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
      * @return an L2CAP CoC BluetoothServerSocket
      * @throws IOException on error, for example Bluetooth not available, or insufficient
      * permissions, or unable to start this CoC
-     * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public BluetoothServerSocket listenUsingL2capCoc(int transport)
+    public BluetoothServerSocket listenUsingL2capChannel()
             throws IOException {
-        if (transport != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Unsupported transport: " + transport);
-        }
         BluetoothServerSocket socket =
                             new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, true, true,
                                       SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
@@ -3005,7 +3001,7 @@
             throw new IOException("Error: Unable to assign PSM value");
         }
         if (DBG) {
-            Log.d(TAG, "listenUsingL2capCoc: set assigned PSM to "
+            Log.d(TAG, "listenUsingL2capChannel: set assigned PSM to "
                     + assignedPsm);
         }
         socket.setChannel(assignedPsm);
@@ -3014,10 +3010,23 @@
     }
 
     /**
+     * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+     * API name, listenUsingL2capChannel.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothServerSocket listenUsingL2capCoc(int transport)
+            throws IOException {
+        Log.e(TAG, "listenUsingL2capCoc: PLEASE USE THE OFFICIAL API, listenUsingL2capChannel");
+        return listenUsingL2capChannel();
+    }
+
+    /**
      * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and
-     * assign a dynamic PSM value. This socket can be used to listen for incoming connections.
+     * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The
+     * supported Bluetooth transport is LE only.
      * <p>The link key is not required to be authenticated, i.e the communication may be vulnerable
-     * to man-in-the-middle attacks. Use {@link #listenUsingL2capCoc}, if an encrypted and
+     * to man-in-the-middle attacks. Use {@link #listenUsingL2capChannel}, if an encrypted and
      * authenticated communication channel is desired.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
      * {@link BluetoothServerSocket}.
@@ -3027,21 +3036,16 @@
      * unexpectedly.
      * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
      * defined and performed by the application.
-     * <p>Use {@link BluetoothDevice#createInsecureL2capCocSocket(int, int)} to connect to this
-     * server socket from another Android device that is given the PSM value.
+     * <p>Use {@link BluetoothDevice#createInsecureL2capChannel(int)} to connect to this server
+     * socket from another Android device that is given the PSM value.
      *
-     * @param transport Bluetooth transport to use, must be {@link BluetoothDevice#TRANSPORT_LE}
      * @return an L2CAP CoC BluetoothServerSocket
      * @throws IOException on error, for example Bluetooth not available, or insufficient
      * permissions, or unable to start this CoC
-     * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+    public BluetoothServerSocket listenUsingInsecureL2capChannel()
             throws IOException {
-        if (transport != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Unsupported transport: " + transport);
-        }
         BluetoothServerSocket socket =
                             new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP_LE, false, false,
                                       SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, false, false);
@@ -3055,11 +3059,24 @@
             throw new IOException("Error: Unable to assign PSM value");
         }
         if (DBG) {
-            Log.d(TAG, "listenUsingInsecureL2capOn: set assigned PSM to "
+            Log.d(TAG, "listenUsingInsecureL2capChannel: set assigned PSM to "
                     + assignedPsm);
         }
         socket.setChannel(assignedPsm);
 
         return socket;
     }
+
+    /**
+     * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+     * API name, listenUsingInsecureL2capChannel.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport)
+            throws IOException {
+        Log.e(TAG, "listenUsingInsecureL2capCoc: PLEASE USE THE OFFICIAL API, "
+                    + "listenUsingInsecureL2capChannel");
+        return listenUsingInsecureL2capChannel();
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 818a749..73e98cd 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1963,8 +1963,8 @@
     /**
      * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
      * be used to start a secure outgoing connection to the remote device with the same dynamic
-     * protocol/service multiplexer (PSM) value.
-     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capCoc(int)} for
+     * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
+     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingL2capChannel()} for
      * peer-peer Bluetooth applications.
      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
      * <p>Application using this API is responsible for obtaining PSM value from remote device.
@@ -1975,59 +1975,71 @@
      * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
      * int)}.
      *
-     * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
      * @param psm dynamic PSM value from remote device
      * @return a CoC #BluetoothSocket ready for an outgoing connection
      * @throws IOException on error, for example Bluetooth not available, or insufficient
      * permissions
-     * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+    public BluetoothSocket createL2capChannel(int psm) throws IOException {
         if (!isBluetoothEnabled()) {
-            Log.e(TAG, "createL2capCocSocket: Bluetooth is not enabled");
+            Log.e(TAG, "createL2capChannel: Bluetooth is not enabled");
             throw new IOException();
         }
-        if (transport != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Unsupported transport: " + transport);
-        }
-        if (DBG) Log.d(TAG, "createL2capCocSocket: transport=" + transport + ", psm=" + psm);
+        if (DBG) Log.d(TAG, "createL2capChannel: psm=" + psm);
         return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, true, true, this, psm,
                 null);
     }
 
     /**
+     * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+     * API name, createL2capChannel.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException {
+        Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createL2capChannel");
+        return createL2capChannel(psm);
+    }
+
+    /**
      * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can
      * be used to start a secure outgoing connection to the remote device with the same dynamic
-     * protocol/service multiplexer (PSM) value.
-     * <p>This is designed to be used with {@link BluetoothAdapter#listenUsingInsecureL2capCoc(int)}
-     * for peer-peer Bluetooth applications.
+     * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only.
+     * <p>This is designed to be used with {@link
+     * BluetoothAdapter#listenUsingInsecureL2capChannel()} for peer-peer Bluetooth applications.
      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing connection.
      * <p>Application using this API is responsible for obtaining PSM value from remote device.
      * <p> The communication channel may not have an authenticated link key, i.e. it may be subject
-     * to man-in-the-middle attacks. Use {@link #createL2capCocSocket(int, int)} if an encrypted and
+     * to man-in-the-middle attacks. Use {@link #createL2capChannel(int)} if an encrypted and
      * authenticated communication channel is possible.
      *
-     * @param transport Bluetooth transport to use, must be {@link #TRANSPORT_LE}
      * @param psm dynamic PSM value from remote device
      * @return a CoC #BluetoothSocket ready for an outgoing connection
      * @throws IOException on error, for example Bluetooth not available, or insufficient
      * permissions
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public BluetoothSocket createInsecureL2capChannel(int psm) throws IOException {
+        if (!isBluetoothEnabled()) {
+            Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled");
+            throw new IOException();
+        }
+        if (DBG) {
+            Log.d(TAG, "createInsecureL2capChannel: psm=" + psm);
+        }
+        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
+                null);
+    }
+
+    /**
+     * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new
+     * API name, createInsecureL2capChannel.
      * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException {
-        if (!isBluetoothEnabled()) {
-            Log.e(TAG, "createInsecureL2capCocSocket: Bluetooth is not enabled");
-            throw new IOException();
-        }
-        if (transport != BluetoothDevice.TRANSPORT_LE) {
-            throw new IllegalArgumentException("Unsupported transport: " + transport);
-        }
-        if (DBG) {
-            Log.d(TAG, "createInsecureL2capCocSocket: transport=" + transport + ", psm=" + psm);
-        }
-        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP_LE, -1, false, false, this, psm,
-                null);
+        Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel");
+        return createInsecureL2capChannel(psm);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index ba4b5a5..5fc344a 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -203,12 +203,11 @@
     /**
      * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
      * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
-     * {#link BluetoothAdapter.listenUsingL2capCoc(int)} or {#link
-     * BluetoothAdapter.listenUsingInsecureL2capCoc(int)}. The returned value is undefined if this
+     * {#link BluetoothAdapter.listenUsingL2capChannel()} or {#link
+     * BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this
      * method is called on non-L2CAP server sockets.
      *
      * @return the assigned PSM or LE_PSM value depending on transport
-     * @hide
      */
     public int getPsm() {
         return mChannel;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 780f896..3a1e2f5 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -667,6 +667,10 @@
      * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
      */
     public int getConnectionType() {
+        if (mType == TYPE_L2CAP_LE) {
+            // Treat the LE CoC to be the same type as L2CAP.
+            return TYPE_L2CAP;
+        }
         return mType;
     }
 
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 212e132..7aa223c 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -124,7 +124,7 @@
  *
  * <p><strong>Data Authority</strong> matches if any of the given values match
  * the Intent's data authority <em>and</em> one of the data schemes in the filter
- * has matched the Intent, <em>or</em> no authories were supplied in the filter.
+ * has matched the Intent, <em>or</em> no authorities were supplied in the filter.
  * The Intent authority is determined by calling
  * {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
  * <em>Note that authority matching here is <b>case sensitive</b>, unlike
diff --git a/core/java/android/content/pm/AndroidHidlUpdater.java b/core/java/android/content/pm/AndroidHidlUpdater.java
new file mode 100644
index 0000000..69cc94f
--- /dev/null
+++ b/core/java/android/content/pm/AndroidHidlUpdater.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+
+import android.content.pm.PackageParser.Package;
+import android.os.Build;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that if it targets <= P that the android.hidl.base-V1.0-java
+ * and android.hidl.manager-V1.0-java libraries are included by default.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidHidlUpdater extends PackageSharedLibraryUpdater {
+
+    @Override
+    public void updatePackage(Package pkg) {
+        // This was the default <= P and is maintained for backwards compatibility.
+        if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.P) {
+            prefixRequiredLibrary(pkg, ANDROID_HIDL_BASE);
+            prefixRequiredLibrary(pkg, ANDROID_HIDL_MANAGER);
+        }
+    }
+}
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
index a16f81b..03eefed 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -53,6 +53,8 @@
                 "android.content.pm.OrgApacheHttpLegacyUpdater",
                 RemoveUnnecessaryOrgApacheHttpLegacyLibrary::new);
 
+        packageUpdaters.add(new AndroidHidlUpdater());
+
         // Add this before adding AndroidTestBaseUpdater so that android.test.base comes before
         // android.test.mock.
         packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index 83e8663..387d29e8 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -22,6 +22,10 @@
  */
 public class SharedLibraryNames {
 
+    static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
+
+    static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
+
     static final String ANDROID_TEST_BASE = "android.test.base";
 
     static final String ANDROID_TEST_MOCK = "android.test.mock";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7a4ef5d..4b76ba8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11340,14 +11340,6 @@
         public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed";
 
         /**
-         * Enable faster emergency phone call feature.
-         * The value is a boolean (1 or 0).
-         * @hide
-         */
-        public static final String FASTER_EMERGENCY_PHONE_CALL_ENABLED =
-                "faster_emergency_phone_call_enabled";
-
-        /**
          * See RIL_PreferredNetworkType in ril.h
          * @hide
          */
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index c861499..cec0df0 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -33,6 +33,7 @@
 
     public static final String FFLAG_PREFIX = "sys.fflag.";
     public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
+    public static final String EMERGENCY_DIAL_SHORTCUTS = "settings_emergency_dial_shortcuts";
 
     private static final Map<String, String> DEFAULT_FLAGS;
     static {
@@ -44,6 +45,7 @@
         DEFAULT_FLAGS.put("settings_data_usage_v2", "true");
         DEFAULT_FLAGS.put("settings_audio_switcher", "true");
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
+        DEFAULT_FLAGS.put(EMERGENCY_DIAL_SHORTCUTS, "false");
     }
 
     /**
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 94c3933..10c32a3 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5800,8 +5800,6 @@
         for (int i = 0; i < N; i++) {
             int uid = mapUid(ws.get(i));
             getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
-            StatsLog.write_non_chained(StatsLog.BLE_SCAN_RESULT_RECEIVED, ws.get(i), ws.getName(i),
-                    numNewResults);
         }
 
         final List<WorkChain> workChains = ws.getWorkChains();
@@ -5810,8 +5808,6 @@
                 final WorkChain wc = workChains.get(i);
                 int uid = mapUid(wc.getAttributionUid());
                 getUidStatsLocked(uid).noteBluetoothScanResultsLocked(numNewResults);
-                StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED,
-                        wc.getUids(), wc.getTags(), numNewResults);
             }
         }
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 494a957..08c9678 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -227,7 +227,6 @@
     static_libs: [
         "libgif",
         "libseccomp_policy",
-        "libselinux",
         "libgrallocusage",
         "libscrypt_static",
     ],
diff --git a/core/res/res/values-mcc262-mnc02/strings.xml b/core/res/res/values-mcc262-mnc02/strings.xml
deleted file mode 100644
index 2b89401..0000000
--- a/core/res/res/values-mcc262-mnc02/strings.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2017, Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-     <!-- Do not translate. Template for showing mobile network operator name while WFC is active -->
-     <string-array name="wfcSpnFormats">
-         <item>%s</item>
-         <item>%s Wi-Fi Calling</item>
-     </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc370/strings.xml b/core/res/res/values-mcc302-mnc370/strings.xml
deleted file mode 100644
index f5b8496..0000000
--- a/core/res/res/values-mcc302-mnc370/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Template for showing mobile network operator name while WFC is active -->
-    <string-array name="wfcSpnFormats">
-        <item>%s</item>
-        <item>%s Wi-Fi</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values-mcc302-mnc720/strings.xml b/core/res/res/values-mcc302-mnc720/strings.xml
deleted file mode 100644
index f5b8496..0000000
--- a/core/res/res/values-mcc302-mnc720/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Template for showing mobile network operator name while WFC is active -->
-    <string-array name="wfcSpnFormats">
-        <item>%s</item>
-        <item>%s Wi-Fi</item>
-    </string-array>
-</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3c5159c..2549dd0 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -240,10 +240,31 @@
         <item>Issue registering Wi\u2011Fi calling with your carrier: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g></item>
     </string-array>
     <!-- Template for showing mobile network operator name while WFC is active -->
-    <string-array name="wfcSpnFormats">
-        <item>%s</item>
-        <item>%s Wi-Fi Calling</item>
+    <string-array name="wfcSpnFormats" translatable="false">
+        <item>@string/wfcSpnFormat_spn</item>
+        <item>@string/wfcSpnFormat_spn_wifi_calling</item>
+        <item>@string/wfcSpnFormat_wlan_call</item>
+        <item>@string/wfcSpnFormat_spn_wlan_call</item>
+        <item>@string/wfcSpnFormat_spn_wifi</item>
+        <item>@string/wfcSpnFormat_wifi_calling_bar_spn</item>
+        <item>@string/wfcSpnFormat_spn_vowifi</item>
     </string-array>
+
+    <!-- Spn during Wi-Fi Calling: "<operator>" -->
+    <string name="wfcSpnFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string>
+    <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi Calling" -->
+    <string name="wfcSpnFormat_spn_wifi_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi Calling</string>
+    <!-- Spn during Wi-Fi Calling: "WLAN Call" -->
+    <string name="wfcSpnFormat_wlan_call">WLAN Call</string>
+    <!-- Spn during Wi-Fi Calling: "<operator> WLAN Call" -->
+    <string name="wfcSpnFormat_spn_wlan_call"><xliff:g id="spn" example="Operator">%s</xliff:g> WLAN Call</string>
+    <!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi" -->
+    <string name="wfcSpnFormat_spn_wifi"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi</string>
+    <!-- Spn during Wi-Fi Calling: "WiFi Calling | <operator>" -->
+    <string name="wfcSpnFormat_wifi_calling_bar_spn">WiFi Calling | <xliff:g id="spn" example="Operator">%s</xliff:g></string>
+    <!-- Spn during Wi-Fi Calling: "<operator> VoWifi" -->
+    <string name="wfcSpnFormat_spn_vowifi"><xliff:g id="spn" example="Operator">%s</xliff:g> VoWifi</string>
+
     <!-- WFC, summary for Disabled -->
     <string name="wifi_calling_off_summary">Off</string>
     <!-- WFC, summary for Wi-Fi Preferred -->
diff --git a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
new file mode 100644
index 0000000..7218b3a2
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import static android.content.pm.PackageBuilder.builder;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+
+import android.os.Build;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link AndroidHidlUpdater}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class AndroidHidlUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+    private static final String OTHER_LIBRARY = "other.library";
+
+    @Test
+    public void targeted_at_O() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O);
+
+        // Should add both HIDL libraries
+        PackageBuilder after = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_not_empty_usesLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(OTHER_LIBRARY);
+
+        // The hidl jars should be added at the start of the list because it
+        // is not on the bootclasspath and the package targets pre-P.
+        PackageBuilder after = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE, OTHER_LIBRARY);
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.O)
+                .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
+
+        // No change is required because although the HIDL libraries has been removed from
+        // the bootclasspath the package explicitly requests it.
+        checkBackwardsCompatibility(before, before);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        PackageBuilder before = builder().requiredLibraries(ANDROID_HIDL_BASE);
+
+        // No change is required because the package explicitly requests the HIDL libraries
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, before);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        PackageBuilder before = builder().optionalLibraries(ANDROID_HIDL_BASE);
+
+        // No change is required because the package explicitly requests the HIDL libraries
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, before);
+    }
+
+    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
+        checkBackwardsCompatibility(before, after, AndroidHidlUpdater::new);
+    }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 02e0d02..cd96583 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -238,7 +238,6 @@
                     Settings.Global.EUICC_SUPPORTED_COUNTRIES,
                     Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
                     Settings.Global.FANCY_IME_ANIMATIONS,
-                    Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED,
                     Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
                     Settings.Global.FORCED_APP_STANDBY_ENABLED,
                     Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 6f52fbd..73c10d2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -188,6 +188,12 @@
     <library name="android.test.runner"
             file="/system/framework/android.test.runner.impl.jar" />
 
+    <!-- In BOOT_JARS historically, and now added to legacy applications. -->
+    <library name="android.hidl.base-V1.0-java"
+            file="/system/framework/android.hidl.base-V1.0-java.jar" />
+    <library name="android.hidl.manager-V1.0-java"
+            file="/system/framework/android.hidl.manager-V1.0-java.jar" />
+
     <!-- These are the standard packages that are white-listed to always have internet
          access while in power save mode, even if they aren't in the foreground. -->
     <allow-in-power-save package="com.android.providers.downloads" />
@@ -204,6 +210,11 @@
     <allow-in-power-save-except-idle package="com.android.providers.calendar" />
     <allow-in-power-save-except-idle package="com.android.providers.contacts" />
 
+    <!-- The PAC proxy process must have network access, otherwise no app will
+         be able to connect to the internet when such a proxy is in use, since
+         all outgoing connections originate from this app. -->
+    <allow-in-power-save-except-idle package="com.android.proxyhandler" />
+
     <!-- These are the packages that are white-listed to be able to run as system user -->
     <system-user-whitelisted-app package="com.android.settings" />
 
diff --git a/packages/CarrierDefaultApp/Android.mk b/packages/CarrierDefaultApp/Android.mk
index 5068b3b..df88afd 100644
--- a/packages/CarrierDefaultApp/Android.mk
+++ b/packages/CarrierDefaultApp/Android.mk
@@ -9,8 +9,6 @@
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_CERTIFICATE := platform
 
-LOCAL_STATIC_JAVA_LIBRARIES := services.net
-
 include $(BUILD_PACKAGE)
 
 # This finds and builds the test apk as well, so a single make does both.
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index b1933373..4f67350 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -32,7 +32,6 @@
 import android.net.Proxy;
 import android.net.TrafficStats;
 import android.net.Uri;
-import android.net.dns.ResolvUtil;
 import android.net.http.SslError;
 import android.os.Bundle;
 import android.telephony.CarrierConfigManager;
@@ -159,9 +158,9 @@
 
     private void setNetwork(Network network) {
         if (network != null) {
+            network = network.getPrivateDnsBypassingCopy();
             mCm.bindProcessToNetwork(network);
-            mCm.setProcessDefaultNetworkForHostResolution(
-                    ResolvUtil.getNetworkWithUseLocalNameserversFlag(network));
+            mCm.setProcessDefaultNetworkForHostResolution(network);
         }
         mNetwork = network;
     }
@@ -242,7 +241,6 @@
     private void testForCaptivePortal() {
         mTestingThread = new Thread(new Runnable() {
             public void run() {
-                final Network network = ResolvUtil.makeNetworkWithPrivateDnsBypass(mNetwork);
                 // Give time for captive portal to open.
                 try {
                     Thread.sleep(1000);
@@ -253,7 +251,7 @@
                 int httpResponseCode = 500;
                 int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
                 try {
-                    urlConnection = (HttpURLConnection) network.openConnection(
+                    urlConnection = (HttpURLConnection) mNetwork.openConnection(
                             new URL(mCm.getCaptivePortalServerUrl()));
                     urlConnection.setInstanceFollowRedirects(false);
                     urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index ac5f537..0e0f63c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -33,7 +33,6 @@
 
 final class A2dpSinkProfile implements LocalBluetoothProfile {
     private static final String TAG = "A2dpSinkProfile";
-    private static boolean V = true;
 
     private BluetoothA2dpSink mService;
     private boolean mIsProfileReady;
@@ -57,7 +56,7 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
+            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothA2dpSink) proxy;
             // We just bound to the service, so refresh the UI for any connected A2DP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -76,7 +75,7 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady=false;
         }
     }
@@ -109,7 +108,9 @@
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
-        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        if (mService == null) {
+            return new ArrayList<BluetoothDevice>(0);
+        }
         return mService.getDevicesMatchingConnectionStates(
               new int[] {BluetoothProfile.STATE_CONNECTED,
                          BluetoothProfile.STATE_CONNECTING,
@@ -117,24 +118,18 @@
     }
 
     public boolean connect(BluetoothDevice device) {
-        if (mService == null) return false;
-        List<BluetoothDevice> srcs = getConnectedDevices();
-        if (srcs != null) {
-            for (BluetoothDevice src : srcs) {
-                if (src.equals(device)) {
-                    // Connect to same device, Ignore it
-                    Log.d(TAG,"Ignoring Connect");
-                    return true;
-                }
-            }
+        if (mService == null) {
+            return false;
         }
         return mService.connect(device);
     }
 
     public boolean disconnect(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         // Downgrade priority as user is disconnecting the headset.
-        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
+        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
             mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
         }
         return mService.disconnect(device);
@@ -148,17 +143,23 @@
     }
 
     public boolean isPreferred(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
     }
 
     public int getPreferred(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        if (mService == null) {
+            return BluetoothProfile.PRIORITY_OFF;
+        }
         return mService.getPriority(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
-        if (mService == null) return;
+        if (mService == null) {
+            return;
+        }
         if (preferred) {
             if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
                 mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -169,7 +170,9 @@
     }
 
     boolean isA2dpPlaying() {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         List<BluetoothDevice> srcs = mService.getConnectedDevices();
         if (!srcs.isEmpty()) {
             if (mService.isA2dpPlaying(srcs.get(0))) {
@@ -211,11 +214,11 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP_SINK,
-                                                                       mService);
+                        mService);
                 mService = null;
             }catch (Throwable t) {
                 Log.w(TAG, "Error cleaning up A2DP proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 03f6afb..0857b01 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -36,7 +36,6 @@
  */
 final class HfpClientProfile implements LocalBluetoothProfile {
     private static final String TAG = "HfpClientProfile";
-    private static boolean V = false;
 
     private BluetoothHeadsetClient mService;
     private boolean mIsProfileReady;
@@ -61,7 +60,7 @@
 
         @Override
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
+            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothHeadsetClient) proxy;
             // We just bound to the service, so refresh the UI for any connected HFP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -82,7 +81,7 @@
 
         @Override
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady=false;
         }
     }
@@ -118,7 +117,9 @@
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
-        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        if (mService == null) {
+            return new ArrayList<BluetoothDevice>(0);
+        }
         return mService.getDevicesMatchingConnectionStates(
               new int[] {BluetoothProfile.STATE_CONNECTED,
                          BluetoothProfile.STATE_CONNECTING,
@@ -127,23 +128,17 @@
 
     @Override
     public boolean connect(BluetoothDevice device) {
-        if (mService == null) return false;
-        List<BluetoothDevice> srcs = getConnectedDevices();
-        if (srcs != null) {
-            for (BluetoothDevice src : srcs) {
-                if (src.equals(device)) {
-                    // Connect to same device, Ignore it
-                    Log.d(TAG,"Ignoring Connect");
-                    return true;
-                }
-            }
+        if (mService == null) {
+            return false;
         }
         return mService.connect(device);
     }
 
     @Override
     public boolean disconnect(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         // Downgrade priority as user is disconnecting the headset.
         if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
             mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -161,19 +156,25 @@
 
     @Override
     public boolean isPreferred(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
     }
 
     @Override
     public int getPreferred(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        if (mService == null) {
+            return BluetoothProfile.PRIORITY_OFF;
+        }
         return mService.getPriority(device);
     }
 
     @Override
     public void setPreferred(BluetoothDevice device, boolean preferred) {
-        if (mService == null) return;
+        if (mService == null) {
+            return;
+        }
         if (preferred) {
             if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
                 mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -219,7 +220,7 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index ca1eea5..af78c6f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -29,7 +29,7 @@
 import java.util.List;
 
 /**
- * HidProfile handles Bluetooth HID profile.
+ * HidDeviceProfile handles Bluetooth HID Device role
  */
 public class HidDeviceProfile implements LocalBluetoothProfile {
     private static final String TAG = "HidDeviceProfile";
@@ -37,7 +37,6 @@
     private static final int ORDINAL = 18;
     // HID Device Profile is always preferred.
     private static final int PREFERRED_VALUE = -1;
-    private static final boolean DEBUG = true;
 
     private final LocalBluetoothAdapter mLocalAdapter;
     private final CachedBluetoothDeviceManager mDeviceManager;
@@ -62,9 +61,7 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (DEBUG) {
-                Log.d(TAG,"Bluetooth service connected :-)");
-            }
+            Log.d(TAG, "Bluetooth service connected :-), profile:" + profile);
             mService = (BluetoothHidDevice) proxy;
             // We just bound to the service, so refresh the UI for any connected HID devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -84,9 +81,7 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (DEBUG) {
-                Log.d(TAG, "Bluetooth service disconnected");
-            }
+            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
@@ -113,6 +108,7 @@
 
     @Override
     public boolean connect(BluetoothDevice device) {
+        // Don't invoke method in service because settings is not allowed to connect this profile.
         return false;
     }
 
@@ -129,11 +125,7 @@
         if (mService == null) {
             return BluetoothProfile.STATE_DISCONNECTED;
         }
-        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-
-        return !deviceList.isEmpty() && deviceList.contains(device)
-                ? mService.getConnectionState(device)
-                : BluetoothProfile.STATE_DISCONNECTED;
+        return mService.getConnectionState(device);
     }
 
     @Override
@@ -188,9 +180,7 @@
     }
 
     protected void finalize() {
-        if (DEBUG) {
-            Log.d(TAG, "finalize()");
-        }
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_DEVICE,
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index ad0d8ba..e9fcc11 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -32,11 +32,10 @@
 import java.util.List;
 
 /**
- * MapClientProfile handles Bluetooth MAP profile.
+ * MapClientProfile handles the Bluetooth MAP MCE role.
  */
 public final class MapClientProfile implements LocalBluetoothProfile {
     private static final String TAG = "MapClientProfile";
-    private static boolean V = false;
 
     private BluetoothMapClient mService;
     private boolean mIsProfileReady;
@@ -61,7 +60,7 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) Log.d(TAG,"Bluetooth service connected");
+            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothMapClient) proxy;
             // We just bound to the service, so refresh the UI for any connected MAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -83,14 +82,14 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mProfileManager.callServiceDisconnectedListeners();
             mIsProfileReady=false;
         }
     }
 
     public boolean isProfileReady() {
-        if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+        Log.d(TAG, "isProfileReady(): "+ mIsProfileReady);
         return mIsProfileReady;
     }
 
@@ -118,18 +117,16 @@
     }
 
     public boolean connect(BluetoothDevice device) {
-        if (mService == null) return false;
-        List<BluetoothDevice> connectedDevices = getConnectedDevices();
-        if (connectedDevices != null && connectedDevices.contains(device)) {
-            // Connect to same device, Ignore it
-            Log.d(TAG,"Ignoring Connect");
-            return true;
+        if (mService == null) {
+            return false;
         }
         return mService.connect(device);
     }
 
     public boolean disconnect(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         // Downgrade priority as user is disconnecting.
         if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
             mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -138,23 +135,30 @@
     }
 
     public int getConnectionStatus(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
-
+        if (mService == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
         return mService.getConnectionState(device);
     }
 
     public boolean isPreferred(BluetoothDevice device) {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
     }
 
     public int getPreferred(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        if (mService == null) {
+            return BluetoothProfile.PRIORITY_OFF;
+        }
         return mService.getPriority(device);
     }
 
     public void setPreferred(BluetoothDevice device, boolean preferred) {
-        if (mService == null) return;
+        if (mService == null) {
+            return;
+        }
         if (preferred) {
             if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
                 mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -165,7 +169,9 @@
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
-        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        if (mService == null) {
+            return new ArrayList<BluetoothDevice>(0);
+        }
         return mService.getDevicesMatchingConnectionStates(
               new int[] {BluetoothProfile.STATE_CONNECTED,
                          BluetoothProfile.STATE_CONNECTING,
@@ -203,11 +209,11 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP_CLIENT,
-                                                                       mService);
+                        mService);
                 mService = null;
             }catch (Throwable t) {
                 Log.w(TAG, "Error cleaning up MAP Client proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index bdbfc9d..fc54775 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -34,7 +34,6 @@
 
 public final class PbapClientProfile implements LocalBluetoothProfile {
     private static final String TAG = "PbapClientProfile";
-    private static boolean V = false;
 
     private BluetoothPbapClient mService;
     private boolean mIsProfileReady;
@@ -57,9 +56,7 @@
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (V) {
-                Log.d(TAG,"Bluetooth service connected");
-            }
+            Log.d(TAG, "Bluetooth service connected, profile:" + profile);
             mService = (BluetoothPbapClient) proxy;
             // We just bound to the service, so refresh the UI for any connected PBAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -78,9 +75,7 @@
         }
 
         public void onServiceDisconnected(int profile) {
-            if (V) {
-                Log.d(TAG,"Bluetooth service disconnected");
-            }
+            Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
             mIsProfileReady = false;
         }
     }
@@ -134,31 +129,16 @@
     }
 
     public boolean connect(BluetoothDevice device) {
-        if (V) {
-            Log.d(TAG,"PBAPClientProfile got connect request");
-        }
+        Log.d(TAG,"PBAPClientProfile got connect request");
         if (mService == null) {
             return false;
         }
-        List<BluetoothDevice> srcs = getConnectedDevices();
-        if (srcs != null) {
-            for (BluetoothDevice src : srcs) {
-                if (src.equals(device)) {
-                    // Connect to same device, Ignore it
-                    Log.d(TAG,"Ignoring Connect");
-                    return true;
-                }
-            }
-        }
         Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
-
         return mService.connect(device);
     }
 
     public boolean disconnect(BluetoothDevice device) {
-        if (V) {
-            Log.d(TAG,"PBAPClientProfile got disconnect request");
-        }
+        Log.d(TAG,"PBAPClientProfile got disconnect request");
         if (mService == null) {
             return false;
         }
@@ -221,9 +201,7 @@
     }
 
     protected void finalize() {
-        if (V) {
-            Log.d(TAG, "finalize()");
-        }
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
new file mode 100644
index 0000000..d38a2d0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothA2dpSink;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class A2dpSinkProfileTest {
+
+    @Mock
+    private LocalBluetoothAdapter mAdapter;
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothA2dpSink mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private A2dpSinkProfile mProfile;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doAnswer((invocation) -> {
+            mServiceListener = (BluetoothProfile.ServiceListener) invocation.getArguments()[1];
+            return null;
+        }).when(mAdapter).getProfileProxy(any(Context.class),
+                any(BluetoothProfile.ServiceListener.class), eq(BluetoothProfile.A2DP_SINK));
+
+        mProfile = new A2dpSinkProfile(RuntimeEnvironment.application, mAdapter,
+                mDeviceManager, mProfileManager);
+        mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, mService);
+    }
+
+    @Test
+    public void connect_shouldConnectBluetoothA2dpSink() {
+        mProfile.connect(mBluetoothDevice);
+        verify(mService).connect(mBluetoothDevice);
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothA2dpSink() {
+        mProfile.disconnect(mBluetoothDevice);
+        verify(mService).disconnect(mBluetoothDevice);
+    }
+
+    @Test
+    public void getConnectionStatus_shouldReturnConnectionState() {
+        when(mService.getConnectionState(mBluetoothDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+                isEqualTo(BluetoothProfile.STATE_CONNECTED);
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
new file mode 100644
index 0000000..7b467d6
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class HfpClientProfileTest {
+
+    @Mock
+    private LocalBluetoothAdapter mAdapter;
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothHeadsetClient mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private HfpClientProfile mProfile;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doAnswer((invocation) -> {
+            mServiceListener = (BluetoothProfile.ServiceListener) invocation.getArguments()[1];
+            return null;
+        }).when(mAdapter).getProfileProxy(any(Context.class),
+                any(BluetoothProfile.ServiceListener.class), eq(BluetoothProfile.HEADSET_CLIENT));
+
+        mProfile = new HfpClientProfile(RuntimeEnvironment.application, mAdapter,
+                mDeviceManager, mProfileManager);
+        mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, mService);
+    }
+
+    @Test
+    public void connect_shouldConnectBluetoothHeadsetClient() {
+        mProfile.connect(mBluetoothDevice);
+        verify(mService).connect(mBluetoothDevice);
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothHeadsetClient() {
+        mProfile.disconnect(mBluetoothDevice);
+        verify(mService).disconnect(mBluetoothDevice);
+    }
+
+    @Test
+    public void getConnectionStatus_shouldReturnConnectionState() {
+        when(mService.getConnectionState(mBluetoothDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+                isEqualTo(BluetoothProfile.STATE_CONNECTED);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
new file mode 100644
index 0000000..354d926
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class HidDeviceProfileTest {
+
+    @Mock
+    private LocalBluetoothAdapter mAdapter;
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothHidDevice mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private HidDeviceProfile mProfile;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doAnswer((invocation) -> {
+            mServiceListener = (BluetoothProfile.ServiceListener) invocation.getArguments()[1];
+            return null;
+        }).when(mAdapter).getProfileProxy(any(Context.class),
+                any(BluetoothProfile.ServiceListener.class), eq(BluetoothProfile.HID_DEVICE));
+
+        mProfile = new HidDeviceProfile(RuntimeEnvironment.application, mAdapter,
+                mDeviceManager, mProfileManager);
+        mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, mService);
+    }
+
+    @Test
+    public void connect_shouldReturnFalse() {
+        assertThat(mProfile.connect(mBluetoothDevice)).isFalse();
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothHidDevice() {
+        mProfile.disconnect(mBluetoothDevice);
+        verify(mService).disconnect(mBluetoothDevice);
+    }
+
+    @Test
+    public void getConnectionStatus_shouldReturnConnectionState() {
+        when(mService.getConnectionState(mBluetoothDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+                isEqualTo(BluetoothProfile.STATE_CONNECTED);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
new file mode 100644
index 0000000..97c9f18
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class MapClientProfileTest {
+
+    @Mock
+    private LocalBluetoothAdapter mAdapter;
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothMapClient mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private MapClientProfile mProfile;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doAnswer((invocation) -> {
+            mServiceListener = (BluetoothProfile.ServiceListener) invocation.getArguments()[1];
+            return null;
+        }).when(mAdapter).getProfileProxy(any(Context.class),
+                any(BluetoothProfile.ServiceListener.class), eq(BluetoothProfile.MAP_CLIENT));
+
+        mProfile = new MapClientProfile(RuntimeEnvironment.application, mAdapter,
+                mDeviceManager, mProfileManager);
+        mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, mService);
+    }
+
+    @Test
+    public void connect_shouldConnectBluetoothMapClient() {
+        mProfile.connect(mBluetoothDevice);
+        verify(mService).connect(mBluetoothDevice);
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothMapClient() {
+        mProfile.disconnect(mBluetoothDevice);
+        verify(mService).disconnect(mBluetoothDevice);
+    }
+
+    @Test
+    public void getConnectionStatus_shouldReturnConnectionState() {
+        when(mService.getConnectionState(mBluetoothDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+                isEqualTo(BluetoothProfile.STATE_CONNECTED);
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
new file mode 100644
index 0000000..45b52b2
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class PbapClientProfileTest {
+
+    @Mock
+    private LocalBluetoothAdapter mAdapter;
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothPbapClient mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private PbapClientProfile mProfile;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doAnswer((invocation) -> {
+            mServiceListener = (BluetoothProfile.ServiceListener) invocation.getArguments()[1];
+            return null;
+        }).when(mAdapter).getProfileProxy(any(Context.class),
+                any(BluetoothProfile.ServiceListener.class), eq(BluetoothProfile.PBAP_CLIENT));
+
+        mProfile = new PbapClientProfile(RuntimeEnvironment.application, mAdapter,
+                mDeviceManager, mProfileManager);
+        mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, mService);
+    }
+
+    @Test
+    public void connect_shouldConnectBluetoothPbapClient() {
+        mProfile.connect(mBluetoothDevice);
+        verify(mService).connect(mBluetoothDevice);
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothPbapClient() {
+        mProfile.disconnect(mBluetoothDevice);
+        verify(mService).disconnect(mBluetoothDevice);
+    }
+
+    @Test
+    public void getConnectionStatus_shouldReturnConnectionState() {
+        when(mService.getConnectionState(mBluetoothDevice)).
+                thenReturn(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+                isEqualTo(BluetoothProfile.STATE_CONNECTED);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/operator_name.xml b/packages/SystemUI/res/layout/operator_name.xml
index c4f75e9..015e30a 100644
--- a/packages/SystemUI/res/layout/operator_name.xml
+++ b/packages/SystemUI/res/layout/operator_name.xml
@@ -27,5 +27,6 @@
         android:maxLength="20"
         android:gravity="center_vertical|start"
         android:textAppearance="?android:attr/textAppearanceSmall"
-        android:singleLine="true" />
+        android:singleLine="true"
+        android:paddingEnd="5dp" />
 </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index 9435589..f4f2ebc 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -38,6 +38,7 @@
 import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.systemui.util.EmergencyDialerConstants;
 
 /**
  * This class implements a smart emergency button that updates itself based
@@ -47,11 +48,13 @@
  */
 public class EmergencyButton extends Button {
     private static final Intent INTENT_EMERGENCY_DIAL = new Intent()
-            .setAction("com.android.phone.EmergencyDialer.DIAL")
+            .setAction(EmergencyDialerConstants.ACTION_DIAL)
             .setPackage("com.android.phone")
             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                    | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                    | Intent.FLAG_ACTIVITY_CLEAR_TOP)
+            .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+                    EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON);
 
     private static final String LOG_TAG = "EmergencyButton";
     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 8320d32..f4cdbac 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -55,6 +55,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
@@ -89,6 +90,7 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.util.EmergencyDialerConstants;
 import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
 
 import java.util.ArrayList;
@@ -317,8 +319,8 @@
         ArraySet<String> addedKeys = new ArraySet<String>();
         mHasLogoutButton = false;
         mHasLockdownButton = false;
-        mSeparatedEmergencyButtonEnabled = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0;
+        mSeparatedEmergencyButtonEnabled = FeatureFlagUtils
+                .isEnabled(mContext, FeatureFlagUtils.EMERGENCY_DIAL_SHORTCUTS);
         for (int i = 0; i < defaultActions.length; i++) {
             String actionKey = defaultActions[i];
             if (addedKeys.contains(actionKey)) {
@@ -448,9 +450,6 @@
     }
 
     private class EmergencyDialerAction extends SinglePressAction {
-        private static final String ACTION_EMERGENCY_DIALER_DIAL =
-                "com.android.phone.EmergencyDialer.DIAL";
-
         private EmergencyDialerAction() {
             super(R.drawable.ic_faster_emergency,
                     R.string.global_action_emergency);
@@ -458,8 +457,10 @@
 
         @Override
         public void onPress() {
-            Intent intent = new Intent(ACTION_EMERGENCY_DIALER_DIAL);
+            Intent intent = new Intent(EmergencyDialerConstants.ACTION_DIAL);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+                    EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
             mContext.startActivityAsUser(intent, UserHandle.CURRENT);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java b/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java
new file mode 100644
index 0000000..d101ccb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+/**
+ * Constants defined and used in emergency dialer.
+ * Please keep these constants being consistent with those in com.android.phone.EmergencyDialer.
+ */
+public class EmergencyDialerConstants {
+    // Intent action for emergency dialer activity.
+    public static final String ACTION_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+
+    /**
+     * Extra included in {@link #ACTION_DIAL} to indicate the entry type that user starts
+     * the emergency dialer.
+     */
+    public static final String EXTRA_ENTRY_TYPE =
+            "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE";
+
+    // Indicating the entrance to emergency dialer
+    public static final int ENTRY_TYPE_UNKNOWN = 0;
+    public static final int ENTRY_TYPE_LOCKSCREEN_BUTTON = 1;
+    public static final int ENTRY_TYPE_POWER_MENU = 2;
+}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index c79cf71..9c81748 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1560,10 +1560,11 @@
 
             try {
                 // TODO: support quota shared across interfaces
-                mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
+                mNetdService.bandwidthSetInterfaceQuota(iface, quotaBytes);
+
                 mActiveQuotas.put(iface, quotaBytes);
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
+            } catch (RemoteException | ServiceSpecificException e) {
+                throw new IllegalStateException(e);
             }
 
             synchronized (mTetheringStatsProviders) {
@@ -1594,9 +1595,9 @@
 
             try {
                 // TODO: support quota shared across interfaces
-                mConnector.execute("bandwidth", "removeiquota", iface);
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
+                mNetdService.bandwidthRemoveInterfaceQuota(iface);
+            } catch (RemoteException | ServiceSpecificException e) {
+                throw new IllegalStateException(e);
             }
 
             synchronized (mTetheringStatsProviders) {
@@ -1628,10 +1629,10 @@
 
             try {
                 // TODO: support alert shared across interfaces
-                mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
+                mNetdService.bandwidthSetInterfaceAlert(iface, alertBytes);
                 mActiveAlerts.put(iface, alertBytes);
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
+            } catch (RemoteException | ServiceSpecificException e) {
+                throw new IllegalStateException(e);
             }
         }
     }
@@ -1648,10 +1649,10 @@
 
             try {
                 // TODO: support alert shared across interfaces
-                mConnector.execute("bandwidth", "removeinterfacealert", iface);
+                mNetdService.bandwidthRemoveInterfaceAlert(iface);
                 mActiveAlerts.remove(iface);
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
+            } catch (RemoteException | ServiceSpecificException e) {
+                throw new IllegalStateException(e);
             }
         }
     }
@@ -1661,18 +1662,15 @@
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            mConnector.execute("bandwidth", "setglobalalert", alertBytes);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.bandwidthSetGlobalAlert(alertBytes);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
     private void setUidOnMeteredNetworkList(int uid, boolean blacklist, boolean enable) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        final String chain = blacklist ? "naughtyapps" : "niceapps";
-        final String suffix = enable ? "add" : "remove";
-
         synchronized (mQuotaLock) {
             boolean oldEnable;
             SparseBooleanArray quotaList;
@@ -1687,7 +1685,19 @@
 
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "inetd bandwidth");
             try {
-                mConnector.execute("bandwidth", suffix + chain, uid);
+                if (blacklist) {
+                    if (enable) {
+                        mNetdService.bandwidthAddNaughtyApp(uid);
+                    } else {
+                        mNetdService.bandwidthRemoveNaughtyApp(uid);
+                    }
+                } else {
+                    if (enable) {
+                        mNetdService.bandwidthAddNiceApp(uid);
+                    } else {
+                        mNetdService.bandwidthRemoveNiceApp(uid);
+                    }
+                }
                 synchronized (mRulesLock) {
                     if (enable) {
                         quotaList.put(uid, true);
@@ -1695,8 +1705,8 @@
                         quotaList.delete(uid);
                     }
                 }
-            } catch (NativeDaemonConnectorException e) {
-                throw e.rethrowAsParcelableException();
+            } catch (RemoteException | ServiceSpecificException e) {
+                throw new IllegalStateException(e);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
             }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d16c277..a8f7259 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -68,6 +68,7 @@
 import android.hardware.usb.UsbManager;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
+import android.net.ip.IpServer;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -112,10 +113,8 @@
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.LocalServices;
-import com.android.server.connectivity.tethering.IControlsTethering;
 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.OffloadController;
-import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.connectivity.tethering.TetheringConfiguration;
 import com.android.server.connectivity.tethering.TetheringDependencies;
 import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
@@ -149,7 +148,7 @@
     protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
 
     private static final Class[] messageClasses = {
-            Tethering.class, TetherMasterSM.class, TetherInterfaceStateMachine.class
+            Tethering.class, TetherMasterSM.class, IpServer.class
     };
     private static final SparseArray<String> sMagicDecoderRing =
             MessageUtils.findMessageNames(messageClasses);
@@ -159,21 +158,21 @@
             .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
 
     private static class TetherState {
-        public final TetherInterfaceStateMachine stateMachine;
+        public final IpServer ipServer;
         public int lastState;
         public int lastError;
 
-        public TetherState(TetherInterfaceStateMachine sm) {
-            stateMachine = sm;
+        public TetherState(IpServer ipServer) {
+            this.ipServer = ipServer;
             // Assume all state machines start out available and with no errors.
-            lastState = IControlsTethering.STATE_AVAILABLE;
+            lastState = IpServer.STATE_AVAILABLE;
             lastError = TETHER_ERROR_NO_ERROR;
         }
 
         public boolean isCurrentlyServing() {
             switch (lastState) {
-                case IControlsTethering.STATE_TETHERED:
-                case IControlsTethering.STATE_LOCAL_ONLY:
+                case IpServer.STATE_TETHERED:
+                case IpServer.STATE_LOCAL_ONLY:
                     return true;
                 default:
                     return false;
@@ -198,7 +197,7 @@
     private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
     // TODO: Figure out how to merge this and other downstream-tracking objects
     // into a single coherent structure.
-    private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams;
+    private final HashSet<IpServer> mForwardedDownstreams;
     private final VersionedBroadcastListener mCarrierConfigChange;
     private final TetheringDependencies mDeps;
 
@@ -604,7 +603,7 @@
     }
 
     public int tether(String iface) {
-        return tether(iface, IControlsTethering.STATE_TETHERED);
+        return tether(iface, IpServer.STATE_TETHERED);
     }
 
     private int tether(String iface, int requestedState) {
@@ -617,7 +616,7 @@
             }
             // Ignore the error status of the interface.  If the interface is available,
             // the errors are referring to past tethering attempts anyway.
-            if (tetherState.lastState != IControlsTethering.STATE_AVAILABLE) {
+            if (tetherState.lastState != IpServer.STATE_AVAILABLE) {
                 Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
                 return TETHER_ERROR_UNAVAIL_IFACE;
             }
@@ -626,8 +625,7 @@
             // return an error.
             //
             // TODO: reexamine the threading and messaging model.
-            tetherState.stateMachine.sendMessage(
-                    TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, requestedState);
+            tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState);
             return TETHER_ERROR_NO_ERROR;
         }
     }
@@ -644,8 +642,7 @@
                 Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
                 return TETHER_ERROR_UNAVAIL_IFACE;
             }
-            tetherState.stateMachine.sendMessage(
-                    TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+            tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED);
             return TETHER_ERROR_NO_ERROR;
         }
     }
@@ -689,11 +686,11 @@
                 String iface = mTetherStates.keyAt(i);
                 if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
                     erroredList.add(iface);
-                } else if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
+                } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
                     availableList.add(iface);
-                } else if (tetherState.lastState == IControlsTethering.STATE_LOCAL_ONLY) {
+                } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
                     localOnlyList.add(iface);
-                } else if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
+                } else if (tetherState.lastState == IpServer.STATE_TETHERED) {
                     if (cfg.isUsb(iface)) {
                         usbTethered = true;
                     } else if (cfg.isWifi(iface)) {
@@ -882,10 +879,10 @@
             synchronized (Tethering.this.mPublicSync) {
                 if (!usbConnected && mRndisEnabled) {
                     // Turn off tethering if it was enabled and there is a disconnect.
-                    tetherMatchingInterfaces(IControlsTethering.STATE_AVAILABLE, TETHERING_USB);
+                    tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
                 } else if (usbConfigured && rndisEnabled) {
                     // Tether if rndis is enabled and usb is configured.
-                    tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED, TETHERING_USB);
+                    tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB);
                 }
                 mRndisEnabled = usbConfigured && rndisEnabled;
             }
@@ -959,15 +956,15 @@
         if (!TextUtils.isEmpty(ifname)) {
             final TetherState ts = mTetherStates.get(ifname);
             if (ts != null) {
-                ts.stateMachine.unwanted();
+                ts.ipServer.unwanted();
                 return;
             }
         }
 
         for (int i = 0; i < mTetherStates.size(); i++) {
-            TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine;
-            if (tism.interfaceType() == TETHERING_WIFI) {
-                tism.unwanted();
+            final IpServer ipServer = mTetherStates.valueAt(i).ipServer;
+            if (ipServer.interfaceType() == TETHERING_WIFI) {
+                ipServer.unwanted();
                 return;
             }
         }
@@ -978,15 +975,15 @@
     }
 
     private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
-        // Map wifiIpMode values to IControlsTethering serving states, inferring
+        // Map wifiIpMode values to IpServer.Callback serving states, inferring
         // from mWifiTetherRequested as a final "best guess".
         final int ipServingMode;
         switch (wifiIpMode) {
             case IFACE_IP_MODE_TETHERED:
-                ipServingMode = IControlsTethering.STATE_TETHERED;
+                ipServingMode = IpServer.STATE_TETHERED;
                 break;
             case IFACE_IP_MODE_LOCAL_ONLY:
-                ipServingMode = IControlsTethering.STATE_LOCAL_ONLY;
+                ipServingMode = IpServer.STATE_LOCAL_ONLY;
                 break;
             default:
                 mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode);
@@ -1041,12 +1038,12 @@
     private void changeInterfaceState(String ifname, int requestedState) {
         final int result;
         switch (requestedState) {
-            case IControlsTethering.STATE_UNAVAILABLE:
-            case IControlsTethering.STATE_AVAILABLE:
+            case IpServer.STATE_UNAVAILABLE:
+            case IpServer.STATE_AVAILABLE:
                 result = untether(ifname);
                 break;
-            case IControlsTethering.STATE_TETHERED:
-            case IControlsTethering.STATE_LOCAL_ONLY:
+            case IpServer.STATE_TETHERED:
+            case IpServer.STATE_LOCAL_ONLY:
                 result = tether(ifname, requestedState);
                 break;
             default:
@@ -1104,7 +1101,7 @@
         synchronized (mPublicSync) {
             for (int i = 0; i < mTetherStates.size(); i++) {
                 TetherState tetherState = mTetherStates.valueAt(i);
-                if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
+                if (tetherState.lastState == IpServer.STATE_TETHERED) {
                     list.add(mTetherStates.keyAt(i));
                 }
             }
@@ -1117,7 +1114,7 @@
         synchronized (mPublicSync) {
             for (int i = 0; i < mTetherStates.size(); i++) {
                 TetherState tetherState = mTetherStates.valueAt(i);
-                if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
+                if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
                     list.add(mTetherStates.keyAt(i));
                 }
             }
@@ -1177,7 +1174,7 @@
         synchronized (mPublicSync) {
             for (int i = 0; i < mTetherStates.size(); i++) {
                 TetherState tetherState = mTetherStates.valueAt(i);
-                if (tetherState.lastState != IControlsTethering.STATE_TETHERED) {
+                if (tetherState.lastState != IpServer.STATE_TETHERED) {
                     continue;  // Skip interfaces that aren't tethered.
                 }
                 String iface = mTetherStates.keyAt(i);
@@ -1231,7 +1228,7 @@
         // Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList
         // so that the garbage collector does not clean up the state machine before it has a chance
         // to tear itself down.
-        private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+        private final ArrayList<IpServer> mNotifyList;
         private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
         private final OffloadWrapper mOffload;
 
@@ -1268,17 +1265,19 @@
             public boolean processMessage(Message message) {
                 logMessage(this, message.what);
                 switch (message.what) {
-                    case EVENT_IFACE_SERVING_STATE_ACTIVE:
-                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+                    case EVENT_IFACE_SERVING_STATE_ACTIVE: {
+                        final IpServer who = (IpServer) message.obj;
                         if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
                         handleInterfaceServingStateActive(message.arg1, who);
                         transitionTo(mTetherModeAliveState);
                         break;
-                    case EVENT_IFACE_SERVING_STATE_INACTIVE:
-                        who = (TetherInterfaceStateMachine) message.obj;
+                    }
+                    case EVENT_IFACE_SERVING_STATE_INACTIVE: {
+                        final IpServer who = (IpServer) message.obj;
                         if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
                         handleInterfaceServingStateInactive(who);
                         break;
+                    }
                     case EVENT_IFACE_UPDATE_LINKPROPERTIES:
                         // Silently ignore these for now.
                         break;
@@ -1410,8 +1409,8 @@
 
         protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
             mCurrentUpstreamIfaceSet = ifaces;
-            for (TetherInterfaceStateMachine sm : mNotifyList) {
-                sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces);
+            for (IpServer ipServer : mNotifyList) {
+                ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifaces);
             }
         }
 
@@ -1420,13 +1419,13 @@
             mOffload.updateUpstreamNetworkState(ns);
         }
 
-        private void handleInterfaceServingStateActive(int mode, TetherInterfaceStateMachine who) {
+        private void handleInterfaceServingStateActive(int mode, IpServer who) {
             if (mNotifyList.indexOf(who) < 0) {
                 mNotifyList.add(who);
                 mIPv6TetheringCoordinator.addActiveDownstream(who, mode);
             }
 
-            if (mode == IControlsTethering.STATE_TETHERED) {
+            if (mode == IpServer.STATE_TETHERED) {
                 // No need to notify OffloadController just yet as there are no
                 // "offload-able" prefixes to pass along. This will handled
                 // when the TISM informs Tethering of its LinkProperties.
@@ -1441,10 +1440,10 @@
                 final WifiManager mgr = getWifiManager();
                 final String iface = who.interfaceName();
                 switch (mode) {
-                    case IControlsTethering.STATE_TETHERED:
+                    case IpServer.STATE_TETHERED:
                         mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED);
                         break;
-                    case IControlsTethering.STATE_LOCAL_ONLY:
+                    case IpServer.STATE_LOCAL_ONLY:
                         mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY);
                         break;
                     default:
@@ -1454,7 +1453,7 @@
             }
         }
 
-        private void handleInterfaceServingStateInactive(TetherInterfaceStateMachine who) {
+        private void handleInterfaceServingStateInactive(IpServer who) {
             mNotifyList.remove(who);
             mIPv6TetheringCoordinator.removeActiveDownstream(who);
             mOffload.excludeDownstreamInterface(who.interfaceName());
@@ -1563,10 +1562,10 @@
                 boolean retValue = true;
                 switch (message.what) {
                     case EVENT_IFACE_SERVING_STATE_ACTIVE: {
-                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+                        IpServer who = (IpServer) message.obj;
                         if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
                         handleInterfaceServingStateActive(message.arg1, who);
-                        who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
+                        who.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
                                 mCurrentUpstreamIfaceSet);
                         // If there has been a change and an upstream is now
                         // desired, kick off the selection process.
@@ -1577,7 +1576,7 @@
                         break;
                     }
                     case EVENT_IFACE_SERVING_STATE_INACTIVE: {
-                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+                        IpServer who = (IpServer) message.obj;
                         if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
                         handleInterfaceServingStateInactive(who);
 
@@ -1591,7 +1590,7 @@
                         if (DBG) {
                             Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
                                     " live requests:");
-                            for (TetherInterfaceStateMachine o : mNotifyList) {
+                            for (IpServer o : mNotifyList) {
                                 Log.d(TAG, "  " + o);
                             }
                         }
@@ -1605,7 +1604,7 @@
                     }
                     case EVENT_IFACE_UPDATE_LINKPROPERTIES: {
                         final LinkProperties newLp = (LinkProperties) message.obj;
-                        if (message.arg1 == IControlsTethering.STATE_TETHERED) {
+                        if (message.arg1 == IpServer.STATE_TETHERED) {
                             mOffload.updateDownstreamLinkProperties(newLp);
                         } else {
                             mOffload.excludeDownstreamInterface(newLp.getInterfaceName());
@@ -1650,7 +1649,7 @@
                 boolean retValue = true;
                 switch (message.what) {
                     case EVENT_IFACE_SERVING_STATE_ACTIVE:
-                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
+                        IpServer who = (IpServer) message.obj;
                         who.sendMessage(mErrorNotification);
                         break;
                     case CMD_CLEAR_ERROR:
@@ -1665,8 +1664,8 @@
 
             void notify(int msgType) {
                 mErrorNotification = msgType;
-                for (TetherInterfaceStateMachine sm : mNotifyList) {
-                    sm.sendMessage(msgType);
+                for (IpServer ipServer : mNotifyList) {
+                    ipServer.sendMessage(msgType);
                 }
             }
 
@@ -1676,7 +1675,7 @@
             @Override
             public void enter() {
                 Log.e(TAG, "Error in setIpForwardingEnabled");
-                notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR);
+                notify(IpServer.CMD_IP_FORWARDING_ENABLE_ERROR);
             }
         }
 
@@ -1684,7 +1683,7 @@
             @Override
             public void enter() {
                 Log.e(TAG, "Error in setIpForwardingDisabled");
-                notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR);
+                notify(IpServer.CMD_IP_FORWARDING_DISABLE_ERROR);
             }
         }
 
@@ -1692,7 +1691,7 @@
             @Override
             public void enter() {
                 Log.e(TAG, "Error in startTethering");
-                notify(TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR);
+                notify(IpServer.CMD_START_TETHERING_ERROR);
                 try {
                     mNMService.setIpForwardingEnabled(false);
                 } catch (Exception e) {}
@@ -1703,7 +1702,7 @@
             @Override
             public void enter() {
                 Log.e(TAG, "Error in stopTethering");
-                notify(TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR);
+                notify(IpServer.CMD_STOP_TETHERING_ERROR);
                 try {
                     mNMService.setIpForwardingEnabled(false);
                 } catch (Exception e) {}
@@ -1714,7 +1713,7 @@
             @Override
             public void enter() {
                 Log.e(TAG, "Error in setDnsForwarders");
-                notify(TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR);
+                notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR);
                 try {
                     mNMService.stopTethering();
                 } catch (Exception e) {}
@@ -1771,15 +1770,15 @@
 
                 // Maybe add prefixes or addresses for downstreams, depending on
                 // the IP serving mode of each.
-                for (TetherInterfaceStateMachine tism : mNotifyList) {
-                    final LinkProperties lp = tism.linkProperties();
+                for (IpServer ipServer : mNotifyList) {
+                    final LinkProperties lp = ipServer.linkProperties();
 
-                    switch (tism.servingMode()) {
-                        case IControlsTethering.STATE_UNAVAILABLE:
-                        case IControlsTethering.STATE_AVAILABLE:
+                    switch (ipServer.servingMode()) {
+                        case IpServer.STATE_UNAVAILABLE:
+                        case IpServer.STATE_AVAILABLE:
                             // No usable LinkProperties in these states.
                             continue;
-                        case IControlsTethering.STATE_TETHERED:
+                        case IpServer.STATE_TETHERED:
                             // Only add IPv4 /32 and IPv6 /128 prefixes. The
                             // directly-connected prefixes will be sent as
                             // downstream "offload-able" prefixes.
@@ -1789,7 +1788,7 @@
                                 localPrefixes.add(PrefixUtils.ipAddressAsPrefix(ip));
                             }
                             break;
-                        case IControlsTethering.STATE_LOCAL_ONLY:
+                        case IpServer.STATE_LOCAL_ONLY:
                             // Add prefixes covering all local IPs.
                             localPrefixes.addAll(PrefixUtils.localPrefixesFrom(lp));
                             break;
@@ -1826,16 +1825,16 @@
                 pw.print(iface + " - ");
 
                 switch (tetherState.lastState) {
-                    case IControlsTethering.STATE_UNAVAILABLE:
+                    case IpServer.STATE_UNAVAILABLE:
                         pw.print("UnavailableState");
                         break;
-                    case IControlsTethering.STATE_AVAILABLE:
+                    case IpServer.STATE_AVAILABLE:
                         pw.print("AvailableState");
                         break;
-                    case IControlsTethering.STATE_TETHERED:
+                    case IpServer.STATE_TETHERED:
                         pw.print("TetheredState");
                         break;
-                    case IControlsTethering.STATE_LOCAL_ONLY:
+                    case IpServer.STATE_LOCAL_ONLY:
                         pw.print("LocalHotspotState");
                         break;
                     default:
@@ -1873,28 +1872,26 @@
         return false;
     }
 
-    private IControlsTethering makeControlCallback(String ifname) {
-        return new IControlsTethering() {
+    private IpServer.Callback makeControlCallback() {
+        return new IpServer.Callback() {
             @Override
-            public void updateInterfaceState(
-                    TetherInterfaceStateMachine who, int state, int lastError) {
-                notifyInterfaceStateChange(ifname, who, state, lastError);
+            public void updateInterfaceState(IpServer who, int state, int lastError) {
+                notifyInterfaceStateChange(who, state, lastError);
             }
 
             @Override
-            public void updateLinkProperties(
-                    TetherInterfaceStateMachine who, LinkProperties newLp) {
-                notifyLinkPropertiesChanged(ifname, who, newLp);
+            public void updateLinkProperties(IpServer who, LinkProperties newLp) {
+                notifyLinkPropertiesChanged(who, newLp);
             }
         };
     }
 
     // TODO: Move into TetherMasterSM.
-    private void notifyInterfaceStateChange(
-            String iface, TetherInterfaceStateMachine who, int state, int error) {
+    private void notifyInterfaceStateChange(IpServer who, int state, int error) {
+        final String iface = who.interfaceName();
         synchronized (mPublicSync) {
             final TetherState tetherState = mTetherStates.get(iface);
-            if (tetherState != null && tetherState.stateMachine.equals(who)) {
+            if (tetherState != null && tetherState.ipServer.equals(who)) {
                 tetherState.lastState = state;
                 tetherState.lastError = error;
             } else {
@@ -1908,7 +1905,7 @@
             // Notify that we're tethering (or not) this interface.
             // This is how data saver for instance knows if the user explicitly
             // turned on tethering (thus keeping us from being in data saver mode).
-            mPolicyManager.onTetheringChanged(iface, state == IControlsTethering.STATE_TETHERED);
+            mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED);
         } catch (RemoteException e) {
             // Not really very much we can do here.
         }
@@ -1921,12 +1918,12 @@
         }
         int which;
         switch (state) {
-            case IControlsTethering.STATE_UNAVAILABLE:
-            case IControlsTethering.STATE_AVAILABLE:
+            case IpServer.STATE_UNAVAILABLE:
+            case IpServer.STATE_AVAILABLE:
                 which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_INACTIVE;
                 break;
-            case IControlsTethering.STATE_TETHERED:
-            case IControlsTethering.STATE_LOCAL_ONLY:
+            case IpServer.STATE_TETHERED:
+            case IpServer.STATE_LOCAL_ONLY:
                 which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_ACTIVE;
                 break;
             default:
@@ -1937,12 +1934,12 @@
         sendTetherStateChangedBroadcast();
     }
 
-    private void notifyLinkPropertiesChanged(String iface, TetherInterfaceStateMachine who,
-                                             LinkProperties newLp) {
+    private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) {
+        final String iface = who.interfaceName();
         final int state;
         synchronized (mPublicSync) {
             final TetherState tetherState = mTetherStates.get(iface);
-            if (tetherState != null && tetherState.stateMachine.equals(who)) {
+            if (tetherState != null && tetherState.ipServer.equals(who)) {
                 state = tetherState.lastState;
             } else {
                 mLog.log("got notification from stale iface " + iface);
@@ -1952,7 +1949,7 @@
 
         mLog.log(String.format(
                 "OBSERVED LinkProperties update iface=%s state=%s lp=%s",
-                iface, IControlsTethering.getStateString(state), newLp));
+                iface, IpServer.getStateString(state), newLp));
         final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES;
         mTetherMasterSM.sendMessage(which, state, 0, newLp);
     }
@@ -1976,11 +1973,11 @@
 
         mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
         final TetherState tetherState = new TetherState(
-                new TetherInterfaceStateMachine(
-                    iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
-                    makeControlCallback(iface), mConfig.enableLegacyDhcpServer, mDeps));
+                new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
+                             makeControlCallback(), mConfig.enableLegacyDhcpServer,
+                             mDeps.getIpServerDependencies()));
         mTetherStates.put(iface, tetherState);
-        tetherState.stateMachine.start();
+        tetherState.ipServer.start();
     }
 
     private void stopTrackingInterfaceLocked(final String iface) {
@@ -1989,36 +1986,11 @@
             mLog.log("attempting to remove unknown iface (" + iface + "), ignoring");
             return;
         }
-        tetherState.stateMachine.stop();
+        tetherState.ipServer.stop();
         mLog.log("removing TetheringInterfaceStateMachine for: " + iface);
         mTetherStates.remove(iface);
     }
 
-    private static String getIPv4DefaultRouteInterface(NetworkState ns) {
-        if (ns == null) return null;
-        return getInterfaceForDestination(ns.linkProperties, Inet4Address.ANY);
-    }
-
-    private static String getIPv6DefaultRouteInterface(NetworkState ns) {
-        if (ns == null) return null;
-        // An upstream network's IPv6 capability is currently only useful if it
-        // can be 64share'd downstream (RFC 7278). For now, that means mobile
-        // upstream networks only.
-        if (ns.networkCapabilities == null ||
-                !ns.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
-            return null;
-        }
-
-        return getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY);
-    }
-
-    private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
-        final RouteInfo ri = (lp != null)
-                ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
-                : null;
-        return (ri != null) ? ri.getInterface() : null;
-    }
-
     private static String[] copy(String[] strarray) {
         return Arrays.copyOf(strarray, strarray.length);
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
deleted file mode 100644
index 2b81347..0000000
--- a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity.tethering;
-
-import android.net.LinkProperties;
-
-/**
- * @hide
- *
- * Interface with methods necessary to notify that a given interface is ready for tethering.
- *
- * Rename to something more representative, e.g. IpServingControlCallback.
- *
- * All methods MUST be called on the TetherMasterSM main Looper's thread.
- */
-public class IControlsTethering {
-    public static final int STATE_UNAVAILABLE = 0;
-    public static final int STATE_AVAILABLE   = 1;
-    public static final int STATE_TETHERED    = 2;
-    public static final int STATE_LOCAL_ONLY  = 3;
-
-    public static String getStateString(int state) {
-        switch (state) {
-            case STATE_UNAVAILABLE: return "UNAVAILABLE";
-            case STATE_AVAILABLE:   return "AVAILABLE";
-            case STATE_TETHERED:    return "TETHERED";
-            case STATE_LOCAL_ONLY:  return "LOCAL_ONLY";
-        }
-        return "UNKNOWN: " + state;
-    }
-
-    /**
-     * Notify that |who| has changed its tethering state.
-     *
-     * TODO: Remove the need for the |who| argument.
-     *
-     * @param who corresponding instance of a TetherInterfaceStateMachine
-     * @param state one of IControlsTethering.STATE_*
-     * @param lastError one of ConnectivityManager.TETHER_ERROR_*
-     */
-    public void updateInterfaceState(TetherInterfaceStateMachine who, int state, int lastError) {}
-
-    /**
-     * Notify that |who| has new LinkProperties.
-     *
-     * TODO: Remove the need for the |who| argument.
-     *
-     * @param who corresponding instance of a TetherInterfaceStateMachine
-     * @param newLp the new LinkProperties to report
-     */
-    public void updateLinkProperties(TetherInterfaceStateMachine who, LinkProperties newLp) {}
-}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index ba67c94..1000148 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -17,6 +17,7 @@
 package com.android.server.connectivity.tethering;
 
 import android.net.ConnectivityManager;
+import android.net.ip.IpServer;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -50,19 +51,19 @@
     private static final boolean VDBG = false;
 
     private static class Downstream {
-        public final TetherInterfaceStateMachine tism;
-        public final int mode;  // IControlsTethering.STATE_*
+        public final IpServer ipServer;
+        public final int mode;  // IpServer.STATE_*
         // Used to append to a ULA /48, constructing a ULA /64 for local use.
         public final short subnetId;
 
-        Downstream(TetherInterfaceStateMachine tism, int mode, short subnetId) {
-            this.tism = tism;
+        Downstream(IpServer ipServer, int mode, short subnetId) {
+            this.ipServer = ipServer;
             this.mode = mode;
             this.subnetId = subnetId;
         }
     }
 
-    private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+    private final ArrayList<IpServer> mNotifyList;
     private final SharedLog mLog;
     // NOTE: mActiveDownstreams is a list and not a hash data structure because
     // we keep active downstreams in arrival order.  This is done so /64s can
@@ -74,8 +75,7 @@
     private short mNextSubnetId;
     private NetworkState mUpstreamNetworkState;
 
-    public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList,
-                                    SharedLog log) {
+    public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
         mNotifyList = notifyList;
         mLog = log.forSubComponent(TAG);
         mActiveDownstreams = new LinkedList<>();
@@ -83,7 +83,7 @@
         mNextSubnetId = 0;
     }
 
-    public void addActiveDownstream(TetherInterfaceStateMachine downstream, int mode) {
+    public void addActiveDownstream(IpServer downstream, int mode) {
         if (findDownstream(downstream) == null) {
             // Adding a new downstream appends it to the list. Adding a
             // downstream a second time without first removing it has no effect.
@@ -98,7 +98,7 @@
         }
     }
 
-    public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
+    public void removeActiveDownstream(IpServer downstream) {
         stopIPv6TetheringOn(downstream);
         if (mActiveDownstreams.remove(findDownstream(downstream))) {
             updateIPv6TetheringInterfaces();
@@ -133,8 +133,8 @@
     }
 
     private void stopIPv6TetheringOnAllInterfaces() {
-        for (TetherInterfaceStateMachine sm : mNotifyList) {
-            stopIPv6TetheringOn(sm);
+        for (IpServer ipServer : mNotifyList) {
+            stopIPv6TetheringOn(ipServer);
         }
     }
 
@@ -156,28 +156,28 @@
     }
 
     private void updateIPv6TetheringInterfaces() {
-        for (TetherInterfaceStateMachine sm : mNotifyList) {
-            final LinkProperties lp = getInterfaceIPv6LinkProperties(sm);
-            sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
+        for (IpServer ipServer : mNotifyList) {
+            final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
+            ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
             break;
         }
     }
 
-    private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) {
-        if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
+    private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
+        if (ipServer.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
             // TODO: Figure out IPv6 support on PAN interfaces.
             return null;
         }
 
-        final Downstream ds = findDownstream(sm);
+        final Downstream ds = findDownstream(ipServer);
         if (ds == null) return null;
 
-        if (ds.mode == IControlsTethering.STATE_LOCAL_ONLY) {
+        if (ds.mode == IpServer.STATE_LOCAL_ONLY) {
             // Build a Unique Locally-assigned Prefix configuration.
             return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
         }
 
-        // This downstream is in IControlsTethering.STATE_TETHERED mode.
+        // This downstream is in IpServer.STATE_TETHERED mode.
         if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) {
             return null;
         }
@@ -188,7 +188,7 @@
         // IPv6 toward the oldest (first requested) active downstream.
 
         final Downstream currentActive = mActiveDownstreams.peek();
-        if (currentActive != null && currentActive.tism == sm) {
+        if (currentActive != null && currentActive.ipServer == ipServer) {
             final LinkProperties lp = getIPv6OnlyLinkProperties(
                     mUpstreamNetworkState.linkProperties);
             if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
@@ -199,9 +199,9 @@
         return null;
     }
 
-    Downstream findDownstream(TetherInterfaceStateMachine tism) {
+    Downstream findDownstream(IpServer ipServer) {
         for (Downstream ds : mActiveDownstreams) {
-            if (ds.tism == tism) return ds;
+            if (ds.ipServer == ipServer) return ds;
         }
         return null;
     }
@@ -304,7 +304,7 @@
                 ns.linkProperties);
     }
 
-    private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
-        sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+    private static void stopIPv6TetheringOn(IpServer ipServer) {
+        ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index caa867c..8b40069 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -21,6 +21,7 @@
 import android.net.NetworkRequest;
 import android.net.dhcp.DhcpServer;
 import android.net.dhcp.DhcpServingParams;
+import android.net.ip.IpServer;
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.util.InterfaceParams;
 import android.net.util.NetdService;
@@ -49,20 +50,12 @@
     }
 
     public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
-            ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+            ArrayList<IpServer> notifyList, SharedLog log) {
         return new IPv6TetheringCoordinator(notifyList, log);
     }
 
-    public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
-        return new RouterAdvertisementDaemon(ifParams);
-    }
-
-    public InterfaceParams getInterfaceParams(String ifName) {
-        return InterfaceParams.getByName(ifName);
-    }
-
-    public INetd getNetdService() {
-        return NetdService.getInstance();
+    public IpServer.Dependencies getIpServerDependencies() {
+        return new IpServer.Dependencies();
     }
 
     public boolean isTetheringSupported() {
@@ -72,9 +65,4 @@
     public NetworkRequest getDefaultNetworkRequest() {
         return null;
     }
-
-    public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, DhcpServingParams params,
-            SharedLog log) {
-        return new DhcpServer(looper, iface, params, log);
-    }
 }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 748cf08..dea7863 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -53,16 +53,13 @@
     private final static String TAG = "OTADexopt";
     private final static boolean DEBUG_DEXOPT = true;
 
-    // The synthetic library dependencies denoting "no checks."
-    private final static String[] NO_LIBRARIES =
-            new String[] { PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK };
-
     // The amount of "available" (free - low threshold) space necessary at the start of an OTA to
     // not bulk-delete unused apps' odex files.
     private final static long BULK_DELETE_THRESHOLD = 1024 * 1024 * 1024;  // 1GB.
 
     private final Context mContext;
     private final PackageManagerService mPackageManagerService;
+    private final MetricsLogger metricsLogger;
 
     // TODO: Evaluate the need for WeakReferences here.
 
@@ -95,6 +92,7 @@
     public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
         this.mContext = context;
         this.mPackageManagerService = packageManagerService;
+        metricsLogger = new MetricsLogger();
     }
 
     public static OtaDexoptService main(Context context,
@@ -286,8 +284,8 @@
                     throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
-                // The current version.
-                builder.append("9 ");
+                // The current version. For v10, see b/115993344.
+                builder.append("10 ");
 
                 builder.append("dexopt");
 
@@ -336,11 +334,6 @@
                 collectingInstaller, mPackageManagerService.mInstallLock, mContext);
 
         String[] libraryDependencies = pkg.usesLibraryFiles;
-        if (pkg.isSystem()) {
-            // For system apps, we want to avoid classpaths checks.
-            libraryDependencies = NO_LIBRARIES;
-        }
-
 
         optimizer.performDexOpt(pkg, libraryDependencies,
                 null /* ISAs */,
@@ -443,24 +436,22 @@
     private void performMetricsLogging() {
         long finalTime = System.nanoTime();
 
-        MetricsLogger.histogram(mContext, "ota_dexopt_available_space_before_mb",
+        metricsLogger.histogram("ota_dexopt_available_space_before_mb",
                 inMegabytes(availableSpaceBefore));
-        MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_bulk_delete_mb",
+        metricsLogger.histogram("ota_dexopt_available_space_after_bulk_delete_mb",
                 inMegabytes(availableSpaceAfterBulkDelete));
-        MetricsLogger.histogram(mContext, "ota_dexopt_available_space_after_dexopt_mb",
+        metricsLogger.histogram("ota_dexopt_available_space_after_dexopt_mb",
                 inMegabytes(availableSpaceAfterDexopt));
 
-        MetricsLogger.histogram(mContext, "ota_dexopt_num_important_packages",
-                importantPackageCount);
-        MetricsLogger.histogram(mContext, "ota_dexopt_num_other_packages", otherPackageCount);
+        metricsLogger.histogram("ota_dexopt_num_important_packages", importantPackageCount);
+        metricsLogger.histogram("ota_dexopt_num_other_packages", otherPackageCount);
 
-        MetricsLogger.histogram(mContext, "ota_dexopt_num_commands", dexoptCommandCountTotal);
-        MetricsLogger.histogram(mContext, "ota_dexopt_num_commands_executed",
-                dexoptCommandCountExecuted);
+        metricsLogger.histogram("ota_dexopt_num_commands", dexoptCommandCountTotal);
+        metricsLogger.histogram("ota_dexopt_num_commands_executed", dexoptCommandCountExecuted);
 
         final int elapsedTimeSeconds =
                 (int) TimeUnit.NANOSECONDS.toSeconds(finalTime - otaDexoptTimeStart);
-        MetricsLogger.histogram(mContext, "ota_dexopt_time_s", elapsedTimeSeconds);
+        metricsLogger.histogram("ota_dexopt_time_s", elapsedTimeSeconds);
     }
 
     private static class OTADexoptPackageDexOptimizer extends
diff --git a/services/net/java/android/net/dns/ResolvUtil.java b/services/net/java/android/net/dns/ResolvUtil.java
deleted file mode 100644
index d9d4b96..0000000
--- a/services/net/java/android/net/dns/ResolvUtil.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dns;
-
-import static android.system.OsConstants.AI_ADDRCONFIG;
-
-import android.net.Network;
-import android.net.NetworkUtils;
-import android.system.GaiException;
-import android.system.OsConstants;
-import android.system.StructAddrinfo;
-
-import libcore.io.Libcore;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-
-/**
- * DNS resolution utility class.
- *
- * @hide
- */
-public class ResolvUtil {
-    // Non-portable DNS resolution flag.
-    private static final long NETID_USE_LOCAL_NAMESERVERS = 0x80000000L;
-
-    private ResolvUtil() {}
-
-    public static InetAddress[] blockingResolveAllLocally(Network network, String name)
-            throws UnknownHostException {
-        // Use AI_ADDRCONFIG by default
-        return blockingResolveAllLocally(network, name, AI_ADDRCONFIG);
-    }
-
-    public static InetAddress[] blockingResolveAllLocally(
-            Network network, String name, int aiFlags) throws UnknownHostException  {
-        final StructAddrinfo hints = new StructAddrinfo();
-        hints.ai_flags = aiFlags;
-        // Other hints identical to the default Inet6AddressImpl implementation
-        hints.ai_family = OsConstants.AF_UNSPEC;
-        hints.ai_socktype = OsConstants.SOCK_STREAM;
-
-        final Network networkForResolv = getNetworkWithUseLocalNameserversFlag(network);
-
-        try {
-            return Libcore.os.android_getaddrinfo(name, hints, (int) networkForResolv.netId);
-        } catch (GaiException gai) {
-            gai.rethrowAsUnknownHostException(name + ": TLS-bypass resolution failed");
-            return null;  // keep compiler quiet
-        }
-    }
-
-    public static Network getNetworkWithUseLocalNameserversFlag(Network network) {
-        final long netidForResolv = NETID_USE_LOCAL_NAMESERVERS | (long) network.netId;
-        return new Network((int) netidForResolv);
-    }
-
-    public static Network makeNetworkWithPrivateDnsBypass(Network network) {
-        return new Network(network) {
-            @Override
-            public InetAddress[] getAllByName(String host) throws UnknownHostException {
-                return blockingResolveAllLocally(network, host);
-            }
-        };
-    }
-}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/net/java/android/net/ip/IpServer.java
similarity index 89%
rename from services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
rename to services/net/java/android/net/ip/IpServer.java
index 5accb45..823c0a1 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package android.net.ip;
 
 import static android.net.NetworkUtils.numericToInetAddress;
 import static android.net.util.NetworkConstants.asByte;
@@ -31,11 +31,10 @@
 import android.net.RouteInfo;
 import android.net.dhcp.DhcpServer;
 import android.net.dhcp.DhcpServingParams;
-import android.net.ip.InterfaceController;
-import android.net.ip.RouterAdvertisementDaemon;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
+import android.net.util.NetdService;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.Looper;
@@ -67,7 +66,22 @@
  *
  * @hide
  */
-public class TetherInterfaceStateMachine extends StateMachine {
+public class IpServer extends StateMachine {
+    public static final int STATE_UNAVAILABLE = 0;
+    public static final int STATE_AVAILABLE   = 1;
+    public static final int STATE_TETHERED    = 2;
+    public static final int STATE_LOCAL_ONLY  = 3;
+
+    public static String getStateString(int state) {
+        switch (state) {
+            case STATE_UNAVAILABLE: return "UNAVAILABLE";
+            case STATE_AVAILABLE:   return "AVAILABLE";
+            case STATE_TETHERED:    return "TETHERED";
+            case STATE_LOCAL_ONLY:  return "LOCAL_ONLY";
+        }
+        return "UNKNOWN: " + state;
+    }
+
     private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
     private static final byte DOUG_ADAMS = (byte) 42;
 
@@ -83,15 +97,53 @@
     // TODO: have this configurable
     private static final int DHCP_LEASE_TIME_SECS = 3600;
 
-    private final static String TAG = "TetherInterfaceSM";
+    private final static String TAG = "IpServer";
     private final static boolean DBG = false;
     private final static boolean VDBG = false;
     private static final Class[] messageClasses = {
-            TetherInterfaceStateMachine.class
+            IpServer.class
     };
     private static final SparseArray<String> sMagicDecoderRing =
             MessageUtils.findMessageNames(messageClasses);
 
+    public static class Callback {
+        /**
+         * Notify that |who| has changed its tethering state.
+         *
+         * @param who the calling instance of IpServer
+         * @param state one of STATE_*
+         * @param lastError one of ConnectivityManager.TETHER_ERROR_*
+         */
+        public void updateInterfaceState(IpServer who, int state, int lastError) {}
+
+        /**
+         * Notify that |who| has new LinkProperties.
+         *
+         * @param who the calling instance of IpServer
+         * @param newLp the new LinkProperties to report
+         */
+        public void updateLinkProperties(IpServer who, LinkProperties newLp) {}
+    }
+
+    public static class Dependencies {
+        public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+            return new RouterAdvertisementDaemon(ifParams);
+        }
+
+        public InterfaceParams getInterfaceParams(String ifName) {
+            return InterfaceParams.getByName(ifName);
+        }
+
+        public INetd getNetdService() {
+            return NetdService.getInstance();
+        }
+
+        public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+                DhcpServingParams params, SharedLog log) {
+            return new DhcpServer(looper, iface, params, log);
+        }
+    }
+
     private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
     // request from the user that it wants to tether
     public static final int CMD_TETHER_REQUESTED            = BASE_IFACE + 2;
@@ -123,7 +175,7 @@
     private final INetworkManagementService mNMService;
     private final INetd mNetd;
     private final INetworkStatsService mStatsService;
-    private final IControlsTethering mTetherController;
+    private final Callback mCallback;
     private final InterfaceController mInterfaceCtrl;
 
     private final String mIfaceName;
@@ -131,7 +183,7 @@
     private final LinkProperties mLinkProperties;
     private final boolean mUsingLegacyDhcp;
 
-    private final TetheringDependencies mDeps;
+    private final Dependencies mDeps;
 
     private int mLastError;
     private int mServingMode;
@@ -148,17 +200,16 @@
     private DhcpServer mDhcpServer;
     private RaParams mLastRaParams;
 
-    public TetherInterfaceStateMachine(
+    public IpServer(
             String ifaceName, Looper looper, int interfaceType, SharedLog log,
             INetworkManagementService nMService, INetworkStatsService statsService,
-            IControlsTethering tetherController, boolean usingLegacyDhcp,
-            TetheringDependencies deps) {
+            Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
         super(ifaceName, looper);
         mLog = log.forSubComponent(ifaceName);
         mNMService = nMService;
         mNetd = deps.getNetdService();
         mStatsService = statsService;
-        mTetherController = tetherController;
+        mCallback = callback;
         mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
         mIfaceName = ifaceName;
         mInterfaceType = interfaceType;
@@ -167,7 +218,7 @@
         mDeps = deps;
         resetLinkProperties();
         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
-        mServingMode = IControlsTethering.STATE_AVAILABLE;
+        mServingMode = STATE_AVAILABLE;
 
         mInitialState = new InitialState();
         mLocalHotspotState = new LocalHotspotState();
@@ -379,6 +430,8 @@
             params.mtu = v6only.getMtu();
             params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
 
+            if (params.hasDefaultRoute) params.hopLimit = getHopLimit(v6only.getInterfaceName());
+
             for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
                 if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
 
@@ -498,6 +551,20 @@
         }
     }
 
+    private byte getHopLimit(String upstreamIface) {
+        try {
+            int upstreamHopLimit = Integer.parseUnsignedInt(
+                    mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit"));
+            // Add one hop to account for this forwarding device
+            upstreamHopLimit++;
+            // Cap the hop limit to 255.
+            return (byte) Integer.min(upstreamHopLimit, 255);
+        } catch (Exception e) {
+            mLog.e("Failed to find upstream interface hop limit", e);
+        }
+        return RaParams.DEFAULT_HOPLIMIT;
+    }
+
     private void setRaParams(RaParams newParams) {
         if (mRaDaemon != null) {
             final RaParams deprecatedParams =
@@ -521,14 +588,12 @@
 
     private void sendInterfaceState(int newInterfaceState) {
         mServingMode = newInterfaceState;
-        mTetherController.updateInterfaceState(
-                TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
+        mCallback.updateInterfaceState(this, newInterfaceState, mLastError);
         sendLinkProperties();
     }
 
     private void sendLinkProperties() {
-        mTetherController.updateLinkProperties(
-                TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
+        mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties));
     }
 
     private void resetLinkProperties() {
@@ -539,7 +604,7 @@
     class InitialState extends State {
         @Override
         public void enter() {
-            sendInterfaceState(IControlsTethering.STATE_AVAILABLE);
+            sendInterfaceState(STATE_AVAILABLE);
         }
 
         @Override
@@ -549,10 +614,10 @@
                 case CMD_TETHER_REQUESTED:
                     mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
                     switch (message.arg1) {
-                        case IControlsTethering.STATE_LOCAL_ONLY:
+                        case STATE_LOCAL_ONLY:
                             transitionTo(mLocalHotspotState);
                             break;
-                        case IControlsTethering.STATE_TETHERED:
+                        case STATE_TETHERED:
                             transitionTo(mTetheredState);
                             break;
                         default:
@@ -649,7 +714,7 @@
     // problematic because transitioning during a multi-state jump yields
     // a Log.wtf(). Ultimately, there should be only one ServingState,
     // and forwarding and NAT rules should be handled by a coordinating
-    // functional element outside of TetherInterfaceStateMachine.
+    // functional element outside of IpServer.
     class LocalHotspotState extends BaseServingState {
         @Override
         public void enter() {
@@ -659,7 +724,7 @@
             }
 
             if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
-            sendInterfaceState(IControlsTethering.STATE_LOCAL_ONLY);
+            sendInterfaceState(STATE_LOCAL_ONLY);
         }
 
         @Override
@@ -685,7 +750,7 @@
     // problematic because transitioning during a multi-state jump yields
     // a Log.wtf(). Ultimately, there should be only one ServingState,
     // and forwarding and NAT rules should be handled by a coordinating
-    // functional element outside of TetherInterfaceStateMachine.
+    // functional element outside of IpServer.
     class TetheredState extends BaseServingState {
         @Override
         public void enter() {
@@ -695,7 +760,7 @@
             }
 
             if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
-            sendInterfaceState(IControlsTethering.STATE_TETHERED);
+            sendInterfaceState(STATE_TETHERED);
         }
 
         @Override
@@ -817,7 +882,7 @@
         @Override
         public void enter() {
             mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
-            sendInterfaceState(IControlsTethering.STATE_UNAVAILABLE);
+            sendInterfaceState(STATE_UNAVAILABLE);
         }
     }
 
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index a85f21c..bf7836d 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -9,7 +9,9 @@
 LOCAL_MODULE_TAGS := tests
 
 # Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-java-files-under, utils) \
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     frameworks-base-testutils \
diff --git a/services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java b/services/tests/servicestests/utils/com/android/server/testutils/OffsettableClock.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java
rename to services/tests/servicestests/utils/com/android/server/testutils/OffsettableClock.java
diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
rename to services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestUtils.java b/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/testutils/TestUtils.java
rename to services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
new file mode 100644
index 0000000..0f8b18a
--- /dev/null
+++ b/services/tests/wmtests/Android.mk
@@ -0,0 +1,41 @@
+#########################################################################
+# Build WmTests package
+#########################################################################
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-java-files-under, ../servicestests/utils)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    androidx-test \
+    mockito-target-minus-junit4 \
+    platform-test-annotations \
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.mock \
+    android.test.base \
+    android.test.runner \
+
+LOCAL_PACKAGE_NAME := WmTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
new file mode 100644
index 0000000..1fb9473
--- /dev/null
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.frameworks.wmtests">
+
+    <!-- Uses API introduced in P (28) -->
+    <uses-sdk
+        android:minSdkVersion="1"
+        android:targetSdkVersion="28" />
+
+    <application android:testOnly="true" />
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="Window Manager Tests"
+        android:targetPackage="com.android.frameworks.wmtests" />
+</manifest>
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
new file mode 100644
index 0000000..2717ef90
--- /dev/null
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<configuration description="Runs Window Manager Tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="install-arg" value="-t" />
+        <option name="test-file-name" value="WmTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="WmTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.frameworks.wmtests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false" />
+    </test>
+</configuration>
diff --git a/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java b/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java
new file mode 100644
index 0000000..023e4ab
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import androidx.test.filters.FlakyTest;
+
+/**
+ * Dummy test for com.android.server.am.
+ * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
+ */
+public class DummyAmTests {
+
+    @Presubmit
+    @Test
+    public void preSubmitTest() {}
+
+    @FlakyTest
+    @Presubmit
+    @Test
+    public void flakyPreSubmitTest() {}
+
+    @Test
+    public void postSubmitTest() {}
+
+    @FlakyTest
+    @Test
+    public void flakyPostSubmitTest() {}
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java b/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java
new file mode 100644
index 0000000..aecb278
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import androidx.test.filters.FlakyTest;
+
+/**
+ * Dummy test for com.android.server.wm
+ * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
+ */
+public class DummyWmTests {
+
+    @Presubmit
+    @Test
+    public void preSubmitTest() {}
+
+    @FlakyTest
+    @Presubmit
+    @Test
+    public void flakyPreSubmitTest() {}
+
+    @Test
+    public void postSubmitTest() {}
+
+    @FlakyTest
+    @Test
+    public void flakyPostSubmitTest() {}
+}
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index 383d10b..bb066ad 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -195,6 +195,8 @@
         public static final int BLOCK_CHECK_FINISHED_TIMING = 9;
         public static final int FILTERING_COMPLETED_TIMING = 10;
         public static final int FILTERING_TIMED_OUT_TIMING = 11;
+        /** {@hide} */
+        public static final int START_CONNECTION_TO_REQUEST_DISCONNECT_TIMING = 12;
 
         public static final int INVALID = 999999;
 
@@ -256,6 +258,27 @@
     public static final int SIP_PHONE = 0x8;
     public static final int THIRD_PARTY_PHONE = 0x10;
 
+    /**
+     * Indicating the call source is not specified.
+     *
+     * @hide
+     */
+    public static final int CALL_SOURCE_UNSPECIFIED = 0;
+
+    /**
+     * Indicating the call is initiated via emergency dialer's dialpad.
+     *
+     * @hide
+     */
+    public static final int CALL_SOURCE_EMERGENCY_DIALPAD = 1;
+
+    /**
+     * Indicating the call is initiated via emergency dialer's shortcut button.
+     *
+     * @hide
+     */
+    public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2;
+
     public static final long MILLIS_IN_5_MINUTES = 1000 * 60 * 5;
     public static final long MILLIS_IN_1_SECOND = 1000;
 
@@ -319,6 +342,9 @@
     // A list of video events that have occurred.
     private List<VideoEvent> videoEvents;
 
+    // The source where user initiated this call. ONE OF the CALL_SOURCE_* constants.
+    private int callSource = CALL_SOURCE_UNSPECIFIED;
+
     public ParcelableCallAnalytics(long startTimeMillis, long callDurationMillis, int callType,
             boolean isAdditionalCall, boolean isInterrupted, int callTechnologies,
             int callTerminationCode, boolean isEmergencyCall, String connectionService,
@@ -356,6 +382,7 @@
         isVideoCall = readByteAsBoolean(in);
         videoEvents = new LinkedList<>();
         in.readTypedList(videoEvents, VideoEvent.CREATOR);
+        callSource = in.readInt();
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -373,6 +400,7 @@
         out.writeTypedList(eventTimings);
         writeBooleanAsByte(out, isVideoCall);
         out.writeTypedList(videoEvents);
+        out.writeInt(callSource);
     }
 
     /** {@hide} */
@@ -385,6 +413,11 @@
         this.videoEvents = videoEvents;
     }
 
+    /** {@hide} */
+    public void setCallSource(int callSource) {
+        this.callSource = callSource;
+    }
+
     public long getStartTimeMillis() {
         return startTimeMillis;
     }
@@ -443,6 +476,11 @@
         return videoEvents;
     }
 
+    /** {@hide} */
+    public int getCallSource() {
+        return callSource;
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 48c1e24..4e22823 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -622,6 +622,18 @@
             "android.telecom.extra.USE_ASSISTED_DIALING";
 
     /**
+     * Optional extra for {@link #placeCall(Uri, Bundle)} containing an integer that specifies
+     * the source where user initiated this call. This data is used in metrics.
+     * Valid source are:
+     * {@link ParcelableCallAnalytics#CALL_SOURCE_UNSPECIFIED},
+     * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_DIALPAD},
+     * {@link ParcelableCallAnalytics#CALL_SOURCE_EMERGENCY_SHORTCUT}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
+
+    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9319750..ee0512a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1070,6 +1070,16 @@
 
     /**
      * Indexes of SPN format strings in wfcSpnFormats and wfcDataSpnFormats.
+     *
+     * <p>Available options are:
+     * <ul>
+     * <li> 0: %s</li>
+     * <li> 1: %s Wi-Fi Calling</li>
+     * <li> 2: WLAN Call</li>
+     * <li> 3: %s WLAN Call</li>
+     * <li> 4: %s Wi-Fi</li>
+     * <li> 5: WiFi Calling | %s</li>
+     * <li> 6: %s VoWifi</li>
      * @hide
      */
     public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int";
@@ -1077,6 +1087,15 @@
     public static final String KEY_WFC_DATA_SPN_FORMAT_IDX_INT = "wfc_data_spn_format_idx_int";
 
     /**
+     * Use root locale when reading wfcSpnFormats.
+     *
+     * If true, then the root locale will always be used when reading wfcSpnFormats. This means the
+     * non localized version of wfcSpnFormats will be used.
+     * @hide
+     */
+    public static final String KEY_WFC_SPN_USE_ROOT_LOCALE = "wfc_spn_use_root_locale";
+
+    /**
      * The Component Name of the activity that can setup the emergency addrees for WiFi Calling
      * as per carrier requirement.
      * @hide
@@ -2264,6 +2283,7 @@
         sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null);
         sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0);
         sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0);
+        sDefaults.putBoolean(KEY_WFC_SPN_USE_ROOT_LOCALE, false);
         sDefaults.putString(KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING, "");
         sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
         sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
@@ -2273,7 +2293,7 @@
         sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
-        sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, true);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 0c8280b..8f7993c 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -59,6 +59,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
@@ -1810,6 +1811,19 @@
      */
     @UnsupportedAppUsage
     public static Resources getResourcesForSubId(Context context, int subId) {
+        return getResourcesForSubId(context, subId, false);
+    }
+
+    /**
+     * Returns the resources associated with Subscription.
+     * @param context Context object
+     * @param subId Subscription Id of Subscription who's resources are required
+     * @param useRootLocale if root locale should be used. Localized locale is used if false.
+     * @return Resources associated with Subscription.
+     * @hide
+     */
+    public static Resources getResourcesForSubId(Context context, int subId,
+            boolean useRootLocale) {
         final SubscriptionInfo subInfo =
                 SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
 
@@ -1821,6 +1835,11 @@
             newConfig.mnc = subInfo.getMnc();
             if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
         }
+
+        if (useRootLocale) {
+            newConfig.setLocale(Locale.ROOT);
+        }
+
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         DisplayMetrics newMetrics = new DisplayMetrics();
         newMetrics.setTo(metrics);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c86cc6f..7236571 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1498,23 +1498,24 @@
                 Rlog.d(TAG, "getCellLocation returning null because telephony is null");
                 return null;
             }
+
             Bundle bundle = telephony.getCellLocation(mContext.getOpPackageName());
-            if (bundle.isEmpty()) {
-                Rlog.d(TAG, "getCellLocation returning null because bundle is empty");
+            if (bundle == null || bundle.isEmpty()) {
+                Rlog.d(TAG, "getCellLocation returning null because CellLocation is unavailable");
                 return null;
             }
+
             CellLocation cl = CellLocation.newFromBundle(bundle);
-            if (cl.isEmpty()) {
-                Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty");
+            if (cl == null || cl.isEmpty()) {
+                Rlog.d(TAG, "getCellLocation returning null because CellLocation is empty or"
+                        + " phone type doesn't match CellLocation type");
                 return null;
             }
+
             return cl;
         } catch (RemoteException ex) {
             Rlog.d(TAG, "getCellLocation returning null due to RemoteException " + ex);
             return null;
-        } catch (NullPointerException ex) {
-            Rlog.d(TAG, "getCellLocation returning null due to NullPointerException " + ex);
-            return null;
         }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 4790b75..6b3df94 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -93,11 +93,23 @@
      *     converted. If the array size is more than needed, the rest of array remains unchanged.
      */
     public static void bcdToBytes(String bcd, byte[] bytes) {
+        bcdToBytes(bcd, bytes, 0);
+    }
+
+    /**
+     * Converts BCD string to bytes and put it into the given byte array.
+     *
+     * @param bcd This should have an even length. If not, an "0" will be appended to the string.
+     * @param bytes If the array size is less than needed, the rest of the BCD string isn't be
+     *     converted. If the array size is more than needed, the rest of array remains unchanged.
+     * @param offset the offset into the bytes[] to fill the data
+     */
+    public static void bcdToBytes(String bcd, byte[] bytes, int offset) {
         if (bcd.length() % 2 != 0) {
             bcd += "0";
         }
-        int size = Math.min(bytes.length * 2, bcd.length());
-        for (int i = 0, j = 0; i + 1 < size; i += 2, j++) {
+        int size = Math.min((bytes.length - offset) * 2, bcd.length());
+        for (int i = 0, j = offset; i + 1 < size; i += 2, j++) {
             bytes[j] = (byte) (charToByte(bcd.charAt(i + 1)) << 4 | charToByte(bcd.charAt(i)));
         }
     }
@@ -125,6 +137,20 @@
     }
 
     /**
+     * Convert a 5 or 6 - digit PLMN string to a nibble-swizzled encoding as per 24.008 10.5.1.3
+     *
+     * @param plmn the PLMN to convert
+     * @param data a byte array for the output
+     * @param offset the offset into data to start writing
+     */
+    public static void stringToBcdPlmn(final String plmn, byte[] data, int offset) {
+        char digit6 = (plmn.length() > 5) ? plmn.charAt(5) : 'F';
+        data[offset] = (byte) (charToByte(plmn.charAt(1)) << 4 | charToByte(plmn.charAt(0)));
+        data[offset + 1] = (byte) (charToByte(digit6) << 4 | charToByte(plmn.charAt(2)));
+        data[offset + 2] = (byte) (charToByte(plmn.charAt(4)) << 4 | charToByte(plmn.charAt(3)));
+    }
+
+    /**
      * Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH
      */
     public static String
@@ -844,7 +870,7 @@
     }
 
     /**
-     * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
+     * Converts a character of [0-9a-fA-F] to its hex value in a byte. If the character is not a
      * hex number, 0 will be returned.
      */
     private static byte charToByte(char c) {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/android/net/ip/IpServerTest.java
similarity index 71%
rename from tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
rename to tests/net/java/android/net/ip/IpServerTest.java
index 5934653..cff0b54 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity.tethering;
+package android.net.ip;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -37,9 +37,9 @@
 import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
 import static android.net.ConnectivityManager.TETHERING_USB;
 import static android.net.ConnectivityManager.TETHERING_WIFI;
-import static com.android.server.connectivity.tethering.IControlsTethering.STATE_AVAILABLE;
-import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
-import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
+import static android.net.ip.IpServer.STATE_AVAILABLE;
+import static android.net.ip.IpServer.STATE_TETHERED;
+import static android.net.ip.IpServer.STATE_UNAVAILABLE;
 
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
@@ -50,7 +50,6 @@
 import android.net.RouteInfo;
 import android.net.dhcp.DhcpServer;
 import android.net.dhcp.DhcpServingParams;
-import android.net.ip.RouterAdvertisementDaemon;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
@@ -74,7 +73,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class TetherInterfaceStateMachineTest {
+public class IpServerTest {
     private static final String IFACE_NAME = "testnet1";
     private static final String UPSTREAM_IFACE = "upstream0";
     private static final String UPSTREAM_IFACE2 = "upstream1";
@@ -85,39 +84,38 @@
 
     @Mock private INetworkManagementService mNMService;
     @Mock private INetworkStatsService mStatsService;
-    @Mock private IControlsTethering mTetherHelper;
+    @Mock private IpServer.Callback mCallback;
     @Mock private InterfaceConfiguration mInterfaceConfiguration;
     @Mock private SharedLog mSharedLog;
     @Mock private DhcpServer mDhcpServer;
     @Mock private RouterAdvertisementDaemon mRaDaemon;
-    @Mock private TetheringDependencies mTetheringDependencies;
+    @Mock private IpServer.Dependencies mDependencies;
 
     @Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor;
 
     private final TestLooper mLooper = new TestLooper();
     private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
             ArgumentCaptor.forClass(LinkProperties.class);
-    private TetherInterfaceStateMachine mTestedSm;
+    private IpServer mIpServer;
 
     private void initStateMachine(int interfaceType) throws Exception {
         initStateMachine(interfaceType, false /* usingLegacyDhcp */);
     }
 
     private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
-        mTestedSm = new TetherInterfaceStateMachine(
+        mIpServer = new IpServer(
                 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
-                mNMService, mStatsService, mTetherHelper, usingLegacyDhcp,
-                mTetheringDependencies);
-        mTestedSm.start();
+                mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
+        mIpServer.start();
         // Starting the state machine always puts us in a consistent state and notifies
         // the rest of the world that we've changed from an unknown to available state.
         mLooper.dispatchAll();
-        reset(mNMService, mStatsService, mTetherHelper);
+        reset(mNMService, mStatsService, mCallback);
         when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
-        when(mTetheringDependencies.makeDhcpServer(
+        when(mDependencies.makeDhcpServer(
                 any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer);
-        when(mTetheringDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
-        when(mTetheringDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+        when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
+        when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
 
         when(mRaDaemon.start()).thenReturn(true);
     }
@@ -130,11 +128,11 @@
     private void initTetheredStateMachine(int interfaceType, String upstreamIface,
             boolean usingLegacyDhcp) throws Exception {
         initStateMachine(interfaceType, usingLegacyDhcp);
-        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
+        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
         if (upstreamIface != null) {
             dispatchTetherConnectionChanged(upstreamIface);
         }
-        reset(mNMService, mStatsService, mTetherHelper);
+        reset(mNMService, mStatsService, mCallback);
         when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
     }
 
@@ -145,34 +143,34 @@
 
     @Test
     public void startsOutAvailable() {
-        mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
-                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
-                false /* usingLegacyDhcp */, mTetheringDependencies);
-        mTestedSm.start();
+        mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(),
+                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback,
+                false /* usingLegacyDhcp */, mDependencies);
+        mIpServer.start();
         mLooper.dispatchAll();
-        verify(mTetherHelper).updateInterfaceState(
-                mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
-        verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class));
-        verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService);
+        verify(mCallback).updateInterfaceState(
+                mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+        verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
+        verifyNoMoreInteractions(mCallback, mNMService, mStatsService);
     }
 
     @Test
     public void shouldDoNothingUntilRequested() throws Exception {
         initStateMachine(TETHERING_BLUETOOTH);
         final int [] NOOP_COMMANDS = {
-            TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED,
-            TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR,
-            TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR,
-            TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR,
-            TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR,
-            TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR,
-            TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED
+            IpServer.CMD_TETHER_UNREQUESTED,
+            IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
+            IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
+            IpServer.CMD_START_TETHERING_ERROR,
+            IpServer.CMD_STOP_TETHERING_ERROR,
+            IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
+            IpServer.CMD_TETHER_CONNECTION_CHANGED
         };
         for (int command : NOOP_COMMANDS) {
             // None of these commands should trigger us to request action from
             // the rest of the system.
             dispatchCommand(command);
-            verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+            verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
         }
     }
 
@@ -180,57 +178,57 @@
     public void handlesImmediateInterfaceDown() throws Exception {
         initStateMachine(TETHERING_BLUETOOTH);
 
-        dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
-        verify(mTetherHelper).updateInterfaceState(
-                mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
-        verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+        dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
+        verify(mCallback).updateInterfaceState(
+                mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
+        verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
+        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
     }
 
     @Test
     public void canBeTethered() throws Exception {
         initStateMachine(TETHERING_BLUETOOTH);
 
-        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
-        InOrder inOrder = inOrder(mTetherHelper, mNMService);
+        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+        InOrder inOrder = inOrder(mCallback, mNMService);
         inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
-        inOrder.verify(mTetherHelper).updateInterfaceState(
-                mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
-        inOrder.verify(mTetherHelper).updateLinkProperties(
-                eq(mTestedSm), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+        inOrder.verify(mCallback).updateInterfaceState(
+                mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
+        inOrder.verify(mCallback).updateLinkProperties(
+                eq(mIpServer), any(LinkProperties.class));
+        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
     }
 
     @Test
     public void canUnrequestTethering() throws Exception {
         initTetheredStateMachine(TETHERING_BLUETOOTH, null);
 
-        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
+        dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
+        InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
         inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
         inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
-        inOrder.verify(mTetherHelper).updateInterfaceState(
-                mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
-        inOrder.verify(mTetherHelper).updateLinkProperties(
-                eq(mTestedSm), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+        inOrder.verify(mCallback).updateInterfaceState(
+                mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+        inOrder.verify(mCallback).updateLinkProperties(
+                eq(mIpServer), any(LinkProperties.class));
+        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
     }
 
     @Test
     public void canBeTetheredAsUsb() throws Exception {
         initStateMachine(TETHERING_USB);
 
-        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
-        InOrder inOrder = inOrder(mTetherHelper, mNMService);
+        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+        InOrder inOrder = inOrder(mCallback, mNMService);
         inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
         inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
         inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
-        inOrder.verify(mTetherHelper).updateInterfaceState(
-                mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
-        inOrder.verify(mTetherHelper).updateLinkProperties(
-                eq(mTestedSm), mLinkPropertiesCaptor.capture());
+        inOrder.verify(mCallback).updateInterfaceState(
+                mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
+        inOrder.verify(mCallback).updateLinkProperties(
+                eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
-        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
     }
 
     @Test
@@ -243,7 +241,7 @@
         InOrder inOrder = inOrder(mNMService);
         inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
-        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
     }
 
     @Test
@@ -257,7 +255,7 @@
         inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2);
         inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2);
-        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
     }
 
     @Test
@@ -300,18 +298,18 @@
     public void canUnrequestTetheringWithUpstream() throws Exception {
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
 
-        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper);
+        dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
+        InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
         inOrder.verify(mStatsService).forceUpdate();
         inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
         inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
-        inOrder.verify(mTetherHelper).updateInterfaceState(
-                mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
-        inOrder.verify(mTetherHelper).updateLinkProperties(
-                eq(mTestedSm), any(LinkProperties.class));
-        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+        inOrder.verify(mCallback).updateInterfaceState(
+                mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
+        inOrder.verify(mCallback).updateLinkProperties(
+                eq(mIpServer), any(LinkProperties.class));
+        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
     }
 
     @Test
@@ -322,15 +320,15 @@
             if (shouldThrow) {
                 doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME);
             }
-            dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
-            InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+            dispatchCommand(IpServer.CMD_INTERFACE_DOWN);
+            InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
             usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
             usbTeardownOrder.verify(mNMService).setInterfaceConfig(
                     IFACE_NAME, mInterfaceConfiguration);
-            usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
-                    mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
-            usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
-                    eq(mTestedSm), mLinkPropertiesCaptor.capture());
+            usbTeardownOrder.verify(mCallback).updateInterfaceState(
+                    mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
+            usbTeardownOrder.verify(mCallback).updateLinkProperties(
+                    eq(mIpServer), mLinkPropertiesCaptor.capture());
             assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
         }
     }
@@ -340,15 +338,15 @@
         initStateMachine(TETHERING_USB);
 
         doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME);
-        dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED);
-        InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+        dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
+        InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
         usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
         usbTeardownOrder.verify(mNMService).setInterfaceConfig(
                 IFACE_NAME, mInterfaceConfiguration);
-        usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
-                mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
-        usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
-                eq(mTestedSm), mLinkPropertiesCaptor.capture());
+        usbTeardownOrder.verify(mCallback).updateInterfaceState(
+                mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
+        usbTeardownOrder.verify(mCallback).updateLinkProperties(
+                eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
     }
 
@@ -358,13 +356,13 @@
 
         doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString());
         dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-        InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper);
+        InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback);
         usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown();
         usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
-        usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
-                mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
-        usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
-                eq(mTestedSm), mLinkPropertiesCaptor.capture());
+        usbTeardownOrder.verify(mCallback).updateInterfaceState(
+                mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
+        usbTeardownOrder.verify(mCallback).updateLinkProperties(
+                eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
     }
 
@@ -372,11 +370,11 @@
     public void ignoresDuplicateUpstreamNotifications() throws Exception {
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
 
-        verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+        verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
 
         for (int i = 0; i < 5; i++) {
             dispatchTetherConnectionChanged(UPSTREAM_IFACE);
-            verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
+            verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
         }
     }
 
@@ -401,11 +399,11 @@
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE);
 
-        verify(mTetheringDependencies, never()).makeDhcpServer(any(), any(), any(), any());
+        verify(mDependencies, never()).makeDhcpServer(any(), any(), any(), any());
     }
 
     private void assertDhcpStarted(IpPrefix expectedPrefix) {
-        verify(mTetheringDependencies, times(1)).makeDhcpServer(
+        verify(mDependencies, times(1)).makeDhcpServer(
                 eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog));
         verify(mDhcpServer, times(1)).start();
         final DhcpServingParams params = mDhcpParamsCaptor.getValue();
@@ -422,21 +420,21 @@
     /**
      * Send a command to the state machine under test, and run the event loop to idle.
      *
-     * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
+     * @param command One of the IpServer.CMD_* constants.
      * @param arg1 An additional argument to pass.
      */
     private void dispatchCommand(int command, int arg1) {
-        mTestedSm.sendMessage(command, arg1);
+        mIpServer.sendMessage(command, arg1);
         mLooper.dispatchAll();
     }
 
     /**
      * Send a command to the state machine under test, and run the event loop to idle.
      *
-     * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
+     * @param command One of the IpServer.CMD_* constants.
      */
     private void dispatchCommand(int command) {
-        mTestedSm.sendMessage(command);
+        mIpServer.sendMessage(command);
         mLooper.dispatchAll();
     }
 
@@ -447,7 +445,7 @@
      * @param upstreamIface String name of upstream interface (or null)
      */
     private void dispatchTetherConnectionChanged(String upstreamIface) {
-        mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
+        mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
                 new InterfaceSet(upstreamIface));
         mLooper.dispatchAll();
     }
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 0d3b8e4..40d5544 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -75,6 +75,7 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.ip.IpServer;
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.util.InterfaceParams;
 import android.net.util.NetworkConstants;
@@ -99,10 +100,8 @@
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.connectivity.tethering.IControlsTethering;
 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.OffloadHardwareInterface;
-import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.connectivity.tethering.TetheringDependencies;
 import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
 
@@ -190,7 +189,7 @@
 
     public class MockTetheringDependencies extends TetheringDependencies {
         StateMachine upstreamNetworkMonitorMasterSM;
-        ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+        ArrayList<IpServer> ipv6CoordinatorNotifyList;
         int isTetheringSupportedCalls;
 
         public void reset() {
@@ -213,29 +212,35 @@
 
         @Override
         public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
-                ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+                ArrayList<IpServer> notifyList, SharedLog log) {
             ipv6CoordinatorNotifyList = notifyList;
             return mIPv6TetheringCoordinator;
         }
 
         @Override
-        public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
-            return mRouterAdvertisementDaemon;
-        }
+        public IpServer.Dependencies getIpServerDependencies() {
+            return new IpServer.Dependencies() {
+                @Override
+                public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
+                        InterfaceParams ifParams) {
+                    return mRouterAdvertisementDaemon;
+                }
 
-        @Override
-        public INetd getNetdService() {
-            return mNetd;
-        }
+                @Override
+                public InterfaceParams getInterfaceParams(String ifName) {
+                    final String[] ifaces = new String[] {
+                            TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
+                    final int index = ArrayUtils.indexOf(ifaces, ifName);
+                    assertTrue("Non-mocked interface: " + ifName, index >= 0);
+                    return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
+                            MacAddress.ALL_ZEROS_ADDRESS);
+                }
 
-        @Override
-        public InterfaceParams getInterfaceParams(String ifName) {
-            final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME,
-                    TEST_MOBILE_IFNAME };
-            final int index = ArrayUtils.indexOf(ifaces, ifName);
-            assertTrue("Non-mocked interface: " + ifName, index >= 0);
-            return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
-                    MacAddress.ALL_ZEROS_ADDRESS);
+                @Override
+                public INetd getNetdService() {
+                    return mNetd;
+                }
+            };
         }
 
         @Override
@@ -458,9 +463,9 @@
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
         mLooper.dispatchAll();
 
-        // If, and only if, Tethering received an interface status changed
-        // then it creates a TetherInterfaceStateMachine and sends out a
-        // broadcast indicating that the interface is "available".
+        // If, and only if, Tethering received an interface status changed then
+        // it creates a IpServer and sends out a broadcast indicating that the
+        // interface is "available".
         if (emulateInterfaceStatusChanged) {
             assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
             verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
@@ -557,18 +562,18 @@
     }
 
     /**
-     * Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator.
+     * Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator.
      */
     private void sendIPv6TetherUpdates(NetworkState upstreamState) {
         // IPv6TetheringCoordinator must have been notified of downstream
         verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
                 argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
-                eq(IControlsTethering.STATE_TETHERED));
+                eq(IpServer.STATE_TETHERED));
 
-        for (TetherInterfaceStateMachine tism :
+        for (IpServer ipSrv :
                 mTetheringDependencies.ipv6CoordinatorNotifyList) {
             NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
-            tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
+            ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
                     upstreamState.linkProperties.isIPv6Provisioned()
                             ? ipv6OnlyState.linkProperties
                             : null);
@@ -812,7 +817,7 @@
 
         // We verify get/set called thrice here: once for setup and twice during
         // teardown because all events happen over the course of the single
-        // dispatchAll() above. Note that once the TISM IPv4 address config
+        // dispatchAll() above. Note that once the IpServer IPv4 address config
         // code is refactored the two calls during shutdown will revert to one.
         verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, times(3))
diff --git a/tools/hiddenapi/sort_api.sh b/tools/hiddenapi/sort_api.sh
index bdcc807..76a2f2d 100755
--- a/tools/hiddenapi/sort_api.sh
+++ b/tools/hiddenapi/sort_api.sh
@@ -12,8 +12,8 @@
 # Sort
 IFS=$'\n'
 # Stash away comments
-C=( $(grep -E '^#' <<< "${A[*]}") )
-A=( $(grep -v -E '^#' <<< "${A[*]}") )
+C=( $(grep -E '^#' <<< "${A[*]}" || :) )
+A=( $(grep -v -E '^#' <<< "${A[*]}" || :) )
 # Sort entries
 A=( $(LC_COLLATE=C sort -f <<< "${A[*]}") )
 A=( $(uniq <<< "${A[*]}") )