Merge "Make isUsableSubIdValue public"
diff --git a/Android.bp b/Android.bp
index b4ea61a..ee593aa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -506,11 +506,14 @@
         "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
         "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
+        "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
         "telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl",
         "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
+        "telephony/java/android/telephony/mbms/IGroupCallCallback.aidl",
         "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
         "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
+        "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",
         "telephony/java/android/telephony/INetworkService.aidl",
         "telephony/java/android/telephony/INetworkServiceCallback.aidl",
         "telephony/java/com/android/ims/internal/IImsCallSession.aidl",
@@ -686,6 +689,7 @@
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.wifi-V1.0-java-constants",
         "android.hardware.radio-V1.0-java",
+        "android.hardware.radio-V1.3-java",
         "android.hardware.usb.gadget-V1.0-java",
     ],
 
@@ -795,7 +799,7 @@
 java_library {
     name: "ext",
     installable: true,
-    no_framework_libs: true,
+    sdk_version: "core_current",
     static_libs: [
         "libphonenumber-platform",
         "nist-sip",
@@ -1070,53 +1074,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 +1144,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 +1183,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,52 +1218,92 @@
     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",
 }
 
 droiddoc {
+    // Please sync with android-api-council@ before making any changes for the name property below.
+    // Since there's cron jobs that fetch offline-sdk-referenceonly-docs-docs.zip periodically.
+    // See b/116221385 for reference.
     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",
 }
 
 droiddoc {
+    // Please sync with android-api-council@ before making any changes for the name property below.
+    // Since there's cron jobs that fetch offline-system-sdk-referenceonly-docs-docs.zip periodically.
+    // See b/116221385 for reference.
     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 +1312,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 +1341,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 +1376,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 +1405,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 +1422,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 +1438,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 +1467,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 +1494,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 +1529,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 +1586,7 @@
             removed_api_file: "api/removed.txt",
         },
     },
+    jdiff_enabled: true,
 }
 
 droidstubs {
@@ -1569,6 +1611,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 bff46b1..bafd471 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -21,6 +21,7 @@
     field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
     field public static final java.lang.String BIND_AUTOFILL_SERVICE = "android.permission.BIND_AUTOFILL_SERVICE";
+    field public static final java.lang.String BIND_CALL_REDIRECTION_SERVICE = "android.permission.BIND_CALL_REDIRECTION_SERVICE";
     field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
     field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
     field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
@@ -7698,7 +7699,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 +8069,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 +8487,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 {
@@ -18481,6 +18487,114 @@
     method public java.lang.CharSequence getName();
   }
 
+  public class Bidi {
+    ctor public Bidi();
+    ctor public Bidi(int, int);
+    ctor public Bidi(java.lang.String, int);
+    ctor public Bidi(java.text.AttributedCharacterIterator);
+    ctor public Bidi(char[], int, byte[], int, int, int);
+    method public boolean baseIsLeftToRight();
+    method public int countParagraphs();
+    method public int countRuns();
+    method public android.icu.text.Bidi createLineBidi(int, int);
+    method public static byte getBaseDirection(java.lang.CharSequence);
+    method public int getBaseLevel();
+    method public android.icu.text.BidiClassifier getCustomClassifier();
+    method public int getCustomizedClass(int);
+    method public byte getDirection();
+    method public int getLength();
+    method public byte getLevelAt(int);
+    method public byte[] getLevels();
+    method public int getLogicalIndex(int);
+    method public int[] getLogicalMap();
+    method public android.icu.text.BidiRun getLogicalRun(int);
+    method public byte getParaLevel();
+    method public android.icu.text.BidiRun getParagraph(int);
+    method public android.icu.text.BidiRun getParagraphByIndex(int);
+    method public int getParagraphIndex(int);
+    method public int getProcessedLength();
+    method public int getReorderingMode();
+    method public int getReorderingOptions();
+    method public int getResultLength();
+    method public int getRunCount();
+    method public int getRunLevel(int);
+    method public int getRunLimit(int);
+    method public int getRunStart(int);
+    method public char[] getText();
+    method public java.lang.String getTextAsString();
+    method public int getVisualIndex(int);
+    method public int[] getVisualMap();
+    method public android.icu.text.BidiRun getVisualRun(int);
+    method public static int[] invertMap(int[]);
+    method public boolean isInverse();
+    method public boolean isLeftToRight();
+    method public boolean isMixed();
+    method public boolean isOrderParagraphsLTR();
+    method public boolean isRightToLeft();
+    method public void orderParagraphsLTR(boolean);
+    method public static int[] reorderLogical(byte[]);
+    method public static int[] reorderVisual(byte[]);
+    method public static void reorderVisually(byte[], int, java.lang.Object[], int, int);
+    method public static boolean requiresBidi(char[], int, int);
+    method public void setContext(java.lang.String, java.lang.String);
+    method public void setCustomClassifier(android.icu.text.BidiClassifier);
+    method public void setInverse(boolean);
+    method public android.icu.text.Bidi setLine(int, int);
+    method public void setPara(java.lang.String, byte, byte[]);
+    method public void setPara(char[], byte, byte[]);
+    method public void setPara(java.text.AttributedCharacterIterator);
+    method public void setReorderingMode(int);
+    method public void setReorderingOptions(int);
+    method public java.lang.String writeReordered(int);
+    method public static java.lang.String writeReverse(java.lang.String, int);
+    field public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = 126; // 0x7e
+    field public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = 127; // 0x7f
+    field public static final int DIRECTION_LEFT_TO_RIGHT = 0; // 0x0
+    field public static final int DIRECTION_RIGHT_TO_LEFT = 1; // 0x1
+    field public static final short DO_MIRRORING = 2; // 0x2
+    field public static final short INSERT_LRM_FOR_NUMERIC = 4; // 0x4
+    field public static final short KEEP_BASE_COMBINING = 1; // 0x1
+    field public static final byte LEVEL_DEFAULT_LTR = 126; // 0x7e
+    field public static final byte LEVEL_DEFAULT_RTL = 127; // 0x7f
+    field public static final byte LEVEL_OVERRIDE = -128; // 0xffffff80
+    field public static final byte LTR = 0; // 0x0
+    field public static final int MAP_NOWHERE = -1; // 0xffffffff
+    field public static final byte MAX_EXPLICIT_LEVEL = 125; // 0x7d
+    field public static final byte MIXED = 2; // 0x2
+    field public static final byte NEUTRAL = 3; // 0x3
+    field public static final int OPTION_DEFAULT = 0; // 0x0
+    field public static final int OPTION_INSERT_MARKS = 1; // 0x1
+    field public static final int OPTION_REMOVE_CONTROLS = 2; // 0x2
+    field public static final int OPTION_STREAMING = 4; // 0x4
+    field public static final short OUTPUT_REVERSE = 16; // 0x10
+    field public static final short REMOVE_BIDI_CONTROLS = 8; // 0x8
+    field public static final short REORDER_DEFAULT = 0; // 0x0
+    field public static final short REORDER_GROUP_NUMBERS_WITH_R = 2; // 0x2
+    field public static final short REORDER_INVERSE_FOR_NUMBERS_SPECIAL = 6; // 0x6
+    field public static final short REORDER_INVERSE_LIKE_DIRECT = 5; // 0x5
+    field public static final short REORDER_INVERSE_NUMBERS_AS_L = 4; // 0x4
+    field public static final short REORDER_NUMBERS_SPECIAL = 1; // 0x1
+    field public static final short REORDER_RUNS_ONLY = 3; // 0x3
+    field public static final byte RTL = 1; // 0x1
+  }
+
+  public class BidiClassifier {
+    ctor public BidiClassifier(java.lang.Object);
+    method public int classify(int);
+    method public java.lang.Object getContext();
+    method public void setContext(java.lang.Object);
+  }
+
+  public class BidiRun {
+    method public byte getDirection();
+    method public byte getEmbeddingLevel();
+    method public int getLength();
+    method public int getLimit();
+    method public int getStart();
+    method public boolean isEvenRun();
+    method public boolean isOddRun();
+  }
+
   public abstract class BreakIterator implements java.lang.Cloneable {
     ctor protected BreakIterator();
     method public java.lang.Object clone();
@@ -18532,6 +18646,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 +19342,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);
@@ -26927,6 +27096,7 @@
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
     method public android.net.Network getBoundNetworkForProcess();
+    method public int getConnectionOwnerUid(int, java.net.InetSocketAddress, java.net.InetSocketAddress);
     method public android.net.ProxyInfo getDefaultProxy();
     method public android.net.LinkProperties getLinkProperties(android.net.Network);
     method public int getMultipathPreference(android.net.Network);
@@ -33053,6 +33223,7 @@
     method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
     method public static final deprecated boolean supportsProcesses();
     field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
+    field public static final int INVALID_UID = -1; // 0xffffffff
     field public static final int LAST_APPLICATION_UID = 19999; // 0x4e1f
     field public static final int PHONE_UID = 1001; // 0x3e9
     field public static final int SIGNAL_KILL = 9; // 0x9
@@ -40865,6 +41036,7 @@
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+    field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
     field public static final int PROPERTY_RTT = 1024; // 0x400
     field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
     field public static final int PROPERTY_WIFI = 8; // 0x8
@@ -40902,8 +41074,9 @@
   public abstract class CallRedirectionService extends android.app.Service {
     ctor public CallRedirectionService();
     method public final void cancelCall();
-    method public android.os.IBinder onBind(android.content.Intent);
+    method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+    method public final boolean onUnbind(android.content.Intent);
     method public final void placeCallUnmodified();
     method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
@@ -41519,6 +41692,7 @@
     field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
+    field public static final java.lang.String EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE";
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
@@ -41816,6 +41990,7 @@
     field public static final deprecated java.lang.String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
     field public static final java.lang.String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
     field public static final java.lang.String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
+    field public static final java.lang.String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool";
     field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
     field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
     field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
@@ -42077,6 +42252,13 @@
     field public static final int STATUS_UNKNOWN = 0; // 0x0
   }
 
+  public class MbmsGroupCallSession implements java.lang.AutoCloseable {
+    method public void close();
+    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsGroupCallSessionCallback);
+    method public static android.telephony.MbmsGroupCallSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback);
+    method public android.telephony.mbms.GroupCall startGroupCall(java.util.concurrent.Executor, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+  }
+
   public class MbmsStreamingSession implements java.lang.AutoCloseable {
     method public void close();
     method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback);
@@ -42085,7 +42267,7 @@
     method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
   }
 
-  public class NeighboringCellInfo implements android.os.Parcelable {
+  public deprecated class NeighboringCellInfo implements android.os.Parcelable {
     ctor public deprecated NeighboringCellInfo();
     ctor public deprecated NeighboringCellInfo(int, int);
     ctor public NeighboringCellInfo(int, java.lang.String, int);
@@ -42456,6 +42638,7 @@
     method public static int getDefaultVoiceSubscriptionId();
     method public static int[] getSubscriptionIds(int);
     method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
+    method public boolean isActiveSubscriptionId(int);
     method public boolean isNetworkRoaming(int);
     method public static boolean isUsableSubscriptionId(int);
     method public static boolean isValidSubscriptionId(int);
@@ -42543,7 +42726,6 @@
     method public java.lang.String getMmsUAProfUrl();
     method public java.lang.String getMmsUserAgent();
     method public java.lang.String getNai();
-    method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
     method public java.lang.String getNetworkCountryIso();
     method public java.lang.String getNetworkOperator();
     method public java.lang.String getNetworkOperatorName();
@@ -42845,6 +43027,36 @@
 
 }
 
+package android.telephony.emergency {
+
+  public final class EmergencyNumber implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getCountryIso();
+    method public int getEmergencyNumberSourceBitmask();
+    method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
+    method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
+    method public int getEmergencyServiceCategoryBitmask();
+    method public java.lang.String getNumber();
+    method public boolean isFromSources(int);
+    method public boolean isInEmergencyServiceCategories(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+    field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
+    field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
+    field public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 1; // 0x1
+    field public static final int EMERGENCY_NUMBER_SOURCE_SIM = 2; // 0x2
+    field public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = 64; // 0x40
+    field public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2; // 0x2
+    field public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 4; // 0x4
+    field public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 8; // 0x8
+    field public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = 32; // 0x20
+    field public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 16; // 0x10
+    field public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = 1; // 0x1
+    field public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0; // 0x0
+  }
+
+}
+
 package android.telephony.euicc {
 
   public final class DownloadableSubscription implements android.os.Parcelable {
@@ -43024,6 +43236,29 @@
     field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
   }
 
+  public class GroupCall implements java.lang.AutoCloseable {
+    method public void close();
+    method public long getTmgi();
+    method public void updateGroupCall(int[], int[]);
+    field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
+    field public static final int REASON_FREQUENCY_CONFLICT = 3; // 0x3
+    field public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6; // 0x6
+    field public static final int REASON_NONE = 0; // 0x0
+    field public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5; // 0x5
+    field public static final int REASON_OUT_OF_MEMORY = 4; // 0x4
+    field public static final int STATE_STALLED = 3; // 0x3
+    field public static final int STATE_STARTED = 2; // 0x2
+    field public static final int STATE_STOPPED = 1; // 0x1
+  }
+
+  public class GroupCallCallback {
+    ctor public GroupCallCallback();
+    method public void onBroadcastSignalStrengthUpdated(int);
+    method public void onError(int, java.lang.String);
+    method public void onGroupCallStateChanged(int, int);
+    field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
+  }
+
   public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
     ctor public MbmsDownloadReceiver();
     method public void onReceive(android.content.Context, android.content.Intent);
@@ -43072,6 +43307,14 @@
     field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
   }
 
+  public class MbmsGroupCallSessionCallback {
+    ctor public MbmsGroupCallSessionCallback();
+    method public void onAvailableSaisUpdated(java.util.List<java.lang.Integer>, java.util.List<java.util.List<java.lang.Integer>>);
+    method public void onError(int, java.lang.String);
+    method public void onMiddlewareReady();
+    method public void onServiceInterfaceAvailable(java.lang.String, int);
+  }
+
   public class MbmsStreamingSessionCallback {
     ctor public MbmsStreamingSessionCallback();
     method public void onError(int, java.lang.String);
@@ -54663,6 +54906,7 @@
   public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader {
     ctor public DelegateLastClassLoader(java.lang.String, java.lang.ClassLoader);
     ctor public DelegateLastClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader);
+    ctor public DelegateLastClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader, boolean);
   }
 
   public class DexClassLoader extends dalvik.system.BaseDexClassLoader {
diff --git a/api/removed.txt b/api/removed.txt
index f35348e..fba5187 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -540,6 +540,7 @@
   }
 
   public class TelephonyManager {
+    method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
     method public deprecated android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 1df9a46..89ac721 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5048,6 +5048,7 @@
     method public deprecated android.content.ComponentName getDefaultPhoneApp();
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
+    method public boolean isInEmergencyCall();
     method public boolean isRinging();
     method public boolean isTtySupported();
     field public static final java.lang.String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
@@ -5074,6 +5075,10 @@
     field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
   }
 
+  public class MbmsGroupCallSession implements java.lang.AutoCloseable {
+    field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_ACTION = "android.telephony.action.EmbmsGroupCall";
+  }
+
   public class MbmsStreamingSession implements java.lang.AutoCloseable {
     field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
   }
@@ -5133,9 +5138,12 @@
   }
 
   public class ServiceState implements android.os.Parcelable {
+    method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
     method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
-    method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
-    method public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+    method public deprecated java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
+    method public deprecated android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+    method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForDomain(int);
+    method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(int);
   }
 
   public final class SmsManager {
@@ -5230,7 +5238,6 @@
   }
 
   public class TelephonyManager {
-    method public deprecated void answerRingingCall();
     method public deprecated void call(java.lang.String, java.lang.String);
     method public int checkCarrierPrivilegesForPackage(java.lang.String);
     method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String);
@@ -5238,7 +5245,7 @@
     method public boolean disableDataConnectivity();
     method public boolean enableDataConnectivity();
     method public void enableVideoCalling(boolean);
-    method public deprecated boolean endCall();
+    method public java.lang.String getAidForAppType(int);
     method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -5253,6 +5260,8 @@
     method public deprecated boolean getDataEnabled();
     method public deprecated boolean getDataEnabled(int);
     method public boolean getEmergencyCallbackMode();
+    method public java.lang.String getIsimDomain();
+    method public int getPreferredNetworkType(int);
     method public int getSimApplicationState();
     method public int getSimCardState();
     method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
@@ -5262,10 +5271,7 @@
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
     method public boolean isDataConnectivityPossible();
-    method public deprecated boolean isIdle();
-    method public deprecated boolean isOffhook();
     method public deprecated boolean isRadioOn();
-    method public deprecated boolean isRinging();
     method public boolean isVideoCallingEnabled();
     method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
@@ -5279,7 +5285,6 @@
     method public void setSimPowerStateForSlot(int, int);
     method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
     method public void setVoiceActivationState(int);
-    method public deprecated void silenceRinger();
     method public boolean supplyPin(java.lang.String);
     method public int[] supplyPinReportResult(java.lang.String);
     method public boolean supplyPuk(java.lang.String, java.lang.String);
@@ -5297,6 +5302,29 @@
     field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
     field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
     field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+    field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4
+    field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5
+    field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6
+    field public static final int NETWORK_MODE_GLOBAL = 7; // 0x7
+    field public static final int NETWORK_MODE_GSM_ONLY = 1; // 0x1
+    field public static final int NETWORK_MODE_GSM_UMTS = 3; // 0x3
+    field public static final int NETWORK_MODE_LTE_CDMA_EVDO = 8; // 0x8
+    field public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; // 0xa
+    field public static final int NETWORK_MODE_LTE_GSM_WCDMA = 9; // 0x9
+    field public static final int NETWORK_MODE_LTE_ONLY = 11; // 0xb
+    field public static final int NETWORK_MODE_LTE_TDSCDMA = 15; // 0xf
+    field public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; // 0x16
+    field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; // 0x11
+    field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; // 0x14
+    field public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; // 0x13
+    field public static final int NETWORK_MODE_LTE_WCDMA = 12; // 0xc
+    field public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; // 0x15
+    field public static final int NETWORK_MODE_TDSCDMA_GSM = 16; // 0x10
+    field public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; // 0x12
+    field public static final int NETWORK_MODE_TDSCDMA_ONLY = 13; // 0xd
+    field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe
+    field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2
+    field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0
     field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2
     field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1
     field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3
@@ -5632,6 +5660,7 @@
     field public static final java.lang.String EXTRA_CODEC = "Codec";
     field public static final java.lang.String EXTRA_DIALSTRING = "dialstring";
     field public static final java.lang.String EXTRA_DISPLAY_TEXT = "DisplayText";
+    field public static final java.lang.String EXTRA_EMERGENCY_CALL = "e_call";
     field public static final java.lang.String EXTRA_IS_CALL_PULL = "CallPull";
     field public static final java.lang.String EXTRA_OI = "oi";
     field public static final java.lang.String EXTRA_OIR = "oir";
@@ -6375,6 +6404,17 @@
     method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
   }
 
+  public class MbmsGroupCallServiceBase extends android.app.Service {
+    ctor public MbmsGroupCallServiceBase();
+    method public void dispose(int) throws android.os.RemoteException;
+    method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
+    method public void onAppCallbackDied(int, int);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public void stopGroupCall(int, long);
+    method public void updateGroupCall(int, long, int[], int[]);
+  }
+
   public class MbmsStreamingServiceBase extends android.os.Binder {
     ctor public MbmsStreamingServiceBase();
     method public void dispose(int) throws android.os.RemoteException;
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 961026b..72b60e2 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -143,3 +143,16 @@
 
 }
 
+package android.telephony {
+
+  public class TelephonyManager {
+    method public deprecated void answerRingingCall();
+    method public deprecated boolean endCall();
+    method public deprecated boolean isIdle();
+    method public deprecated boolean isOffhook();
+    method public deprecated boolean isRinging();
+    method public deprecated void silenceRinger();
+  }
+
+}
+
diff --git a/api/test-current.txt b/api/test-current.txt
index 634293a..a7bb467 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -966,6 +966,10 @@
     field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override";
   }
 
+  public class MbmsGroupCallSession implements java.lang.AutoCloseable {
+    field public static final java.lang.String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA = "mbms-group-call-service-override";
+  }
+
   public class MbmsStreamingSession implements java.lang.AutoCloseable {
     field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
   }
@@ -1035,6 +1039,17 @@
     method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
   }
 
+  public class MbmsGroupCallServiceBase extends android.app.Service {
+    ctor public MbmsGroupCallServiceBase();
+    method public void dispose(int) throws android.os.RemoteException;
+    method public int initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException;
+    method public void onAppCallbackDied(int, int);
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public int startGroupCall(int, long, int[], int[], android.telephony.mbms.GroupCallCallback);
+    method public void stopGroupCall(int, long);
+    method public void updateGroupCall(int, long, int[], int[]);
+  }
+
   public class MbmsStreamingServiceBase extends android.os.Binder {
     ctor public MbmsStreamingServiceBase();
     method public void dispose(int) throws android.os.RemoteException;
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 4bbe042..72a4103 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -425,7 +425,19 @@
 
 WorkerThreadSection::~WorkerThreadSection() {}
 
+void sigpipe_handler(int signum) {
+    if (signum == SIGPIPE) {
+        ALOGE("Wrote to a broken pipe\n");
+    } else {
+        ALOGE("Received unexpected signal: %d\n", signum);
+    }
+}
+
 static void* worker_thread_func(void* cookie) {
+    // Don't crash the service if we write to a closed pipe (which can happen if
+    // dumping times out).
+    signal(SIGPIPE, sigpipe_handler);
+
     WorkerThreadData* data = (WorkerThreadData*)cookie;
     status_t err = data->section->BlockingCall(data->pipe.writeFd().get());
 
@@ -506,6 +518,7 @@
             }
         }
     }
+
     write_section_stats(requests->sectionStats(this->id), buffer);
     if (timedOut || buffer.timedOut()) {
         ALOGW("WorkerThreadSection '%s' timed out", this->name.string());
@@ -813,7 +826,10 @@
         }
     }
     gLastLogsRetrieved[mLogID] = lastTimestamp;
-    proto.flush(pipeWriteFd);
+    if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+        ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+        return EPIPE;
+    }
     return NO_ERROR;
 }
 
@@ -915,7 +931,7 @@
             break;
         }
         if (cStatus != NO_ERROR) {
-            ALOGE("TombstoneSection '%s' child had an issue: %s\n", this->name.string(), strerror(-cStatus));
+            ALOGE("[%s] child had an issue: %s\n", this->name.string(), strerror(-cStatus));
         }
 
         auto dump = std::make_unique<char[]>(buffer.size());
@@ -934,7 +950,13 @@
         dumpPipe.readFd().reset();
     }
 
-    proto.flush(pipeWriteFd);
+    if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
+        ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+        if (err != NO_ERROR) {
+            return EPIPE;
+        }
+    }
+
     return err;
 }
 
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index a031a15..2f89370 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -173,7 +173,7 @@
  */
 class TombstoneSection : public WorkerThreadSection {
 public:
-    TombstoneSection(int id, const char* type, int64_t timeoutMs = 30000 /* 30 seconds */);
+    TombstoneSection(int id, const char* type, int64_t timeoutMs = 120000 /* 2 minutes */);
     virtual ~TombstoneSection();
 
     virtual status_t BlockingCall(int pipeWriteFd) const;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ab9c7e8..c4824c0 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -152,8 +152,9 @@
         Temperature temperature = 10021;
     }
 
-    // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
-    // 100,000 are reserved for non-AOSP (e.g. OEMs) to use.
+    // DO NOT USE field numbers above 100,000 in AOSP.
+    // Field numbers 100,000 - 199,999 are reserved for non-AOSP (e.g. OEMs) to use.
+    // Field numbers 200,000 and above are reserved for future use; do not use them at all.
 }
 
 /**
@@ -272,9 +273,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/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index fff909c..73ac968 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -489,6 +489,10 @@
                                                              {"AID_RESERVED_DISK", 1065},
                                                              {"AID_STATSD", 1066},
                                                              {"AID_INCIDENTD", 1067},
+                                                             {"AID_SECURE_ELEMENT", 1068},
+                                                             {"AID_LMKD", 1069},
+                                                             {"AID_LLKD", 1070},
+                                                             {"AID_IORAPD", 1071},
                                                              {"AID_SHELL", 2000},
                                                              {"AID_CACHE", 2001},
                                                              {"AID_DIAG", 2002}};
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..fbf5b52 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2147,27 +2147,20 @@
 Lcom/android/internal/telephony/ISub;->getDefaultSubId()I
 Lcom/android/internal/telephony/ISub;->setDefaultDataSubId(I)V
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCall()Z
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCallForSubscriber(I)Z
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->isRadioOn(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
 Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
-Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_answerRingingCall:I
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
-Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_endCall:I
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I
-Lcom/android/internal/telephony/ITelephony;->answerRingingCall()V
 Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V
 Lcom/android/internal/telephony/ITelephony;->dial(Ljava/lang/String;)V
 Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z
 Lcom/android/internal/telephony/ITelephony;->disableLocationUpdates()V
 Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z
 Lcom/android/internal/telephony/ITelephony;->enableLocationUpdates()V
-Lcom/android/internal/telephony/ITelephony;->endCall()Z
-Lcom/android/internal/telephony/ITelephony;->endCallForSubscriber(I)Z
 Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I
 Lcom/android/internal/telephony/ITelephony;->getCallState()I
 Lcom/android/internal/telephony/ITelephony;->getDataActivity()I
@@ -2179,12 +2172,8 @@
 Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z
 Lcom/android/internal/telephony/ITelephony;->iccCloseLogicalChannel(II)Z
 Lcom/android/internal/telephony/ITelephony;->iccTransmitApduLogicalChannel(IIIIIIILjava/lang/String;)Ljava/lang/String;
-Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/ITelephony;->isIdleForSubscriber(ILjava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony;->isRadioOnForSubscriber(ILjava/lang/String;)Z
-Lcom/android/internal/telephony/ITelephony;->isRinging(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony;->setRadio(Z)Z
-Lcom/android/internal/telephony/ITelephony;->silenceRinger()V
 Lcom/android/internal/telephony/ITelephony;->supplyPin(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony;->toggleRadioOnOff()V
 Lcom/android/internal/telephony/ITelephony;->updateServiceLocation()V
@@ -2205,6 +2194,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 +2283,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 +2309,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 +2714,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/config/preloaded-classes b/config/preloaded-classes
index 63c583f..d93befd 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2381,7 +2381,6 @@
 android.os.-$$Lambda$StrictMode$yZJXPvy2veRNA-xL_SWdXzX_OLg
 android.os.-$$Lambda$Trace$2zLZ-Lc2kAXsVjw_nLYeNhqmGq0
 android.os.AsyncResult
-android.os.AsyncTask
 android.os.AsyncTask$1
 android.os.AsyncTask$2
 android.os.AsyncTask$3
diff --git a/config/preloaded-classes-blacklist b/config/preloaded-classes-blacklist
index 8b8d640..eca3bf3 100644
--- a/config/preloaded-classes-blacklist
+++ b/config/preloaded-classes-blacklist
@@ -1,4 +1,5 @@
 android.net.ConnectivityThread$Singleton
+android.os.AsyncTask
 android.os.FileObserver
 android.widget.Magnifier
 sun.nio.fs.UnixChannelFactory
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/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 183be5f..559a59b 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -73,6 +73,8 @@
     /** Connection canceled before completion. */
     public static final int RESULT_CANCELED = 2;
 
+    private static final int UPLOADING_FEATURE_BITMASK = 0x08;
+
     private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
             new IBluetoothStateChangeCallback.Stub() {
                 public void onBluetoothStateChange(boolean up) {
@@ -395,6 +397,23 @@
         return false;
     }
 
+    /**
+     * Returns the "Uploading" feature bit value from the SDP record's
+     * MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114).
+     * @param device The Bluetooth device to get this value for.
+     * @return Returns true if the Uploading bit value in SDP record's
+     *         MapSupportedFeatures field is set. False is returned otherwise.
+     */
+    public boolean isUploadingSupported(BluetoothDevice device) {
+        try {
+            return (mService != null && isEnabled() && isValidDevice(device))
+                && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0);
+        } catch (RemoteException e) {
+            Log.e(TAG, e.getMessage());
+        }
+        return false;
+    }
+
     private final ServiceConnection mConnection = new ServiceConnection() {
         public void onServiceConnected(ComponentName className, IBinder service) {
             if (DBG) Log.d(TAG, "Proxy object connected");
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/net/ConnectionInfo.aidl b/core/java/android/net/ConnectionInfo.aidl
new file mode 100644
index 0000000..07faf8b
--- /dev/null
+++ b/core/java/android/net/ConnectionInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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;
+
+parcelable ConnectionInfo;
diff --git a/core/java/android/net/ConnectionInfo.java b/core/java/android/net/ConnectionInfo.java
new file mode 100644
index 0000000..58d0e05
--- /dev/null
+++ b/core/java/android/net/ConnectionInfo.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Describe a network connection including local and remote address/port of a connection and the
+ * transport protocol.
+ *
+ * @hide
+ */
+public final class ConnectionInfo implements Parcelable {
+    public final int protocol;
+    public final InetSocketAddress local;
+    public final InetSocketAddress remote;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public ConnectionInfo(int protocol, InetSocketAddress local, InetSocketAddress remote) {
+        this.protocol = protocol;
+        this.local = local;
+        this.remote = remote;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(protocol);
+        out.writeByteArray(local.getAddress().getAddress());
+        out.writeInt(local.getPort());
+        out.writeByteArray(remote.getAddress().getAddress());
+        out.writeInt(remote.getPort());
+    }
+
+    public static final Creator<ConnectionInfo> CREATOR = new Creator<ConnectionInfo>() {
+        public ConnectionInfo createFromParcel(Parcel in) {
+            int protocol = in.readInt();
+            InetAddress localAddress;
+            try {
+                localAddress = InetAddress.getByAddress(in.createByteArray());
+            } catch (UnknownHostException e) {
+                throw new IllegalArgumentException("Invalid InetAddress");
+            }
+            int localPort = in.readInt();
+            InetAddress remoteAddress;
+            try {
+                remoteAddress = InetAddress.getByAddress(in.createByteArray());
+            } catch (UnknownHostException e) {
+                throw new IllegalArgumentException("Invalid InetAddress");
+            }
+            int remotePort = in.readInt();
+            InetSocketAddress local = new InetSocketAddress(localAddress, localPort);
+            InetSocketAddress remote = new InetSocketAddress(remoteAddress, remotePort);
+            return new ConnectionInfo(protocol, local, remote);
+        }
+
+        public ConnectionInfo[] newArray(int size) {
+            return new ConnectionInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ce18796..8333b81 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -26,7 +26,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
@@ -59,6 +58,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -3800,8 +3800,9 @@
 
     private void unsupportedStartingFrom(int version) {
         if (Process.myUid() == Process.SYSTEM_UID) {
-            // The getApplicationInfo() call we make below is not supported in system context, and
-            // we want to allow the system to use these APIs anyway.
+            // The getApplicationInfo() call we make below is not supported in system context. Let
+            // the call through here, and rely on the fact that ConnectivityService will refuse to
+            // allow the system to use these APIs anyway.
             return;
         }
 
@@ -3818,11 +3819,6 @@
     // functions by accessing ConnectivityService directly. However, it should be clear that doing
     // so is unsupported and may break in the future. http://b/22728205
     private void checkLegacyRoutingApiAccess() {
-        if (mContext.checkCallingOrSelfPermission("com.android.permission.INJECT_OMADM_SETTINGS")
-                == PackageManager.PERMISSION_GRANTED) {
-            return;
-        }
-
         unsupportedStartingFrom(VERSION_CODES.M);
     }
 
@@ -3930,4 +3926,26 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Returns the {@code uid} of the owner of a network connection.
+     *
+     * @param protocol The protocol of the connection. Only {@code IPPROTO_TCP} and
+     * {@code IPPROTO_UDP} currently supported.
+     * @param local The local {@link InetSocketAddress} of a connection.
+     * @param remote The remote {@link InetSocketAddress} of a connection.
+     *
+     * @return {@code uid} if the connection is found and the app has permission to observe it
+     * (e.g., if it is associated with the calling VPN app's tunnel) or
+     * {@link android.os.Process#INVALID_UID} if the connection is not found.
+     */
+    public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+                                     InetSocketAddress remote) {
+        ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
+        try {
+            return mService.getConnectionOwnerUid(connectionInfo);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index ce95b60..e7d441d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.app.PendingIntent;
+import android.net.ConnectionInfo;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
@@ -182,4 +183,6 @@
     String getCaptivePortalServerUrl();
 
     byte[] getNetworkWatchlistConfigHash();
+
+    int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 7ce7c92..d0cdf6e 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -40,6 +40,11 @@
     public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
 
     /**
+     * An invalid UID value.
+     */
+    public static final int INVALID_UID = -1;
+
+    /**
      * Defines the root UID.
      * @hide
      */
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 3e8e885..8492b0c 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -36,16 +36,12 @@
 import android.view.Display;
 import android.view.WindowManager;
 
-import com.android.internal.logging.MetricsLogger;
-
 import libcore.io.Streams;
 
-import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
@@ -97,7 +93,7 @@
     /** Used to communicate with recovery.  See bootable/recovery/recovery.cpp. */
     private static final File RECOVERY_DIR = new File("/cache/recovery");
     private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
-    private static final File LAST_INSTALL_FILE = new File(RECOVERY_DIR, "last_install");
+    private static final String LAST_INSTALL_PATH = "last_install";
     private static final String LAST_PREFIX = "last_";
     private static final String ACTION_EUICC_FACTORY_RESET =
             "com.android.internal.action.EUICC_FACTORY_RESET";
@@ -935,116 +931,6 @@
         throw new IOException("Reboot failed (no permissions?)");
     }
 
-    // Read last_install; then report time (in seconds) and I/O (in MiB) for
-    // this update to tron.
-    // Only report on the reboots immediately after an OTA update.
-    private static void parseLastInstallLog(Context context) {
-        try (BufferedReader in = new BufferedReader(new FileReader(LAST_INSTALL_FILE))) {
-            String line = null;
-            int bytesWrittenInMiB = -1, bytesStashedInMiB = -1;
-            int timeTotal = -1;
-            int uncryptTime = -1;
-            int sourceVersion = -1;
-            int temperatureStart = -1;
-            int temperatureEnd = -1;
-            int temperatureMax = -1;
-            int errorCode = -1;
-            int causeCode = -1;
-
-            while ((line = in.readLine()) != null) {
-                // Here is an example of lines in last_install:
-                // ...
-                // time_total: 101
-                // bytes_written_vendor: 51074
-                // bytes_stashed_vendor: 200
-                int numIndex = line.indexOf(':');
-                if (numIndex == -1 || numIndex + 1 >= line.length()) {
-                    continue;
-                }
-                String numString = line.substring(numIndex + 1).trim();
-                long parsedNum;
-                try {
-                    parsedNum = Long.parseLong(numString);
-                } catch (NumberFormatException ignored) {
-                    Log.e(TAG, "Failed to parse numbers in " + line);
-                    continue;
-                }
-
-                final int MiB = 1024 * 1024;
-                int scaled;
-                try {
-                    if (line.startsWith("bytes")) {
-                        scaled = Math.toIntExact(parsedNum / MiB);
-                    } else {
-                        scaled = Math.toIntExact(parsedNum);
-                    }
-                } catch (ArithmeticException ignored) {
-                    Log.e(TAG, "Number overflows in " + line);
-                    continue;
-                }
-
-                if (line.startsWith("time")) {
-                    timeTotal = scaled;
-                } else if (line.startsWith("uncrypt_time")) {
-                    uncryptTime = scaled;
-                } else if (line.startsWith("source_build")) {
-                    sourceVersion = scaled;
-                } else if (line.startsWith("bytes_written")) {
-                    bytesWrittenInMiB = (bytesWrittenInMiB == -1) ? scaled :
-                            bytesWrittenInMiB + scaled;
-                } else if (line.startsWith("bytes_stashed")) {
-                    bytesStashedInMiB = (bytesStashedInMiB == -1) ? scaled :
-                            bytesStashedInMiB + scaled;
-                } else if (line.startsWith("temperature_start")) {
-                    temperatureStart = scaled;
-                } else if (line.startsWith("temperature_end")) {
-                    temperatureEnd = scaled;
-                } else if (line.startsWith("temperature_max")) {
-                    temperatureMax = scaled;
-                } else if (line.startsWith("error")) {
-                    errorCode = scaled;
-                } else if (line.startsWith("cause")) {
-                    causeCode = scaled;
-                }
-            }
-
-            // Don't report data to tron if corresponding entry isn't found in last_install.
-            if (timeTotal != -1) {
-                MetricsLogger.histogram(context, "ota_time_total", timeTotal);
-            }
-            if (uncryptTime != -1) {
-                MetricsLogger.histogram(context, "ota_uncrypt_time", uncryptTime);
-            }
-            if (sourceVersion != -1) {
-                MetricsLogger.histogram(context, "ota_source_version", sourceVersion);
-            }
-            if (bytesWrittenInMiB != -1) {
-                MetricsLogger.histogram(context, "ota_written_in_MiBs", bytesWrittenInMiB);
-            }
-            if (bytesStashedInMiB != -1) {
-                MetricsLogger.histogram(context, "ota_stashed_in_MiBs", bytesStashedInMiB);
-            }
-            if (temperatureStart != -1) {
-                MetricsLogger.histogram(context, "ota_temperature_start", temperatureStart);
-            }
-            if (temperatureEnd != -1) {
-                MetricsLogger.histogram(context, "ota_temperature_end", temperatureEnd);
-            }
-            if (temperatureMax != -1) {
-                MetricsLogger.histogram(context, "ota_temperature_max", temperatureMax);
-            }
-            if (errorCode != -1) {
-                MetricsLogger.histogram(context, "ota_non_ab_error_code", errorCode);
-            }
-            if (causeCode != -1) {
-                MetricsLogger.histogram(context, "ota_non_ab_cause_code", causeCode);
-            }
-
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to read lines in last_install", e);
-        }
-    }
-
     /**
      * Called after booting to process and remove recovery-related files.
      * @return the log file from recovery, or null if none was found.
@@ -1062,9 +948,6 @@
             Log.e(TAG, "Error reading recovery log", e);
         }
 
-        if (log != null) {
-            parseLastInstallLog(context);
-        }
 
         // Only remove the OTA package if it's partially processed (uncrypt'd).
         boolean reservePackage = BLOCK_MAP_FILE.exists();
@@ -1095,7 +978,8 @@
         // GmsCore to avoid re-downloading everything again.
         String[] names = RECOVERY_DIR.list();
         for (int i = 0; names != null && i < names.length; i++) {
-            if (names[i].startsWith(LAST_PREFIX)) continue;
+            // Do not remove the last_install file since the recovery-persist takes care of it.
+            if (names[i].startsWith(LAST_PREFIX) || names[i].equals(LAST_INSTALL_PATH)) continue;
             if (reservePackage && names[i].equals(BLOCK_MAP_FILE.getName())) continue;
             if (reservePackage && names[i].equals(UNCRYPT_PACKAGE_FILE.getName())) continue;
 
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index 534ef8d..4b5a7b4 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -43,7 +43,7 @@
  * A base class for {@link Preference} objects that are
  * dialog-based. These preferences will, when clicked, open a dialog showing the
  * actual preference controls.
- * 
+ *
  * @attr ref android.R.styleable#DialogPreference_dialogTitle
  * @attr ref android.R.styleable#DialogPreference_dialogMessage
  * @attr ref android.R.styleable#DialogPreference_dialogIcon
@@ -56,7 +56,7 @@
         PreferenceManager.OnActivityDestroyListener {
     @UnsupportedAppUsage
     private AlertDialog.Builder mBuilder;
-    
+
     @UnsupportedAppUsage
     private CharSequence mDialogTitle;
     @UnsupportedAppUsage
@@ -77,6 +77,14 @@
     @UnsupportedAppUsage
     private int mWhichButtonClicked;
 
+    /** Dismiss the dialog on the UI thread, but not inline with handlers */
+    private final Runnable mDismissRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mDialog.dismiss();
+        }
+    };
+
     public DialogPreference(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
@@ -112,7 +120,7 @@
 
     /**
      * Sets the title of the dialog. This will be shown on subsequent dialogs.
-     * 
+     *
      * @param dialogTitle The title.
      */
     public void setDialogTitle(CharSequence dialogTitle) {
@@ -126,7 +134,7 @@
     public void setDialogTitle(int dialogTitleResId) {
         setDialogTitle(getContext().getString(dialogTitleResId));
     }
-    
+
     /**
      * Returns the title to be shown on subsequent dialogs.
      * @return The title.
@@ -134,7 +142,7 @@
     public CharSequence getDialogTitle() {
         return mDialogTitle;
     }
-    
+
     /**
      * Sets the message of the dialog. This will be shown on subsequent dialogs.
      * <p>
@@ -142,7 +150,7 @@
      * list-based dialogs, for example. If setting a custom View on a dialog via
      * {@link #setDialogLayoutResource(int)}, include a text View with ID
      * {@link android.R.id#message} and it will be populated with this message.
-     * 
+     *
      * @param dialogMessage The message.
      */
     public void setDialogMessage(CharSequence dialogMessage) {
@@ -156,7 +164,7 @@
     public void setDialogMessage(int dialogMessageResId) {
         setDialogMessage(getContext().getString(dialogMessageResId));
     }
-    
+
     /**
      * Returns the message to be shown on subsequent dialogs.
      * @return The message.
@@ -164,26 +172,26 @@
     public CharSequence getDialogMessage() {
         return mDialogMessage;
     }
-    
+
     /**
      * Sets the icon of the dialog. This will be shown on subsequent dialogs.
-     * 
+     *
      * @param dialogIcon The icon, as a {@link Drawable}.
      */
     public void setDialogIcon(Drawable dialogIcon) {
         mDialogIcon = dialogIcon;
     }
-    
+
     /**
      * Sets the icon (resource ID) of the dialog. This will be shown on
      * subsequent dialogs.
-     * 
+     *
      * @param dialogIconRes The icon, as a resource ID.
      */
     public void setDialogIcon(@DrawableRes int dialogIconRes) {
         mDialogIcon = getContext().getDrawable(dialogIconRes);
     }
-    
+
     /**
      * Returns the icon to be shown on subsequent dialogs.
      * @return The icon, as a {@link Drawable}.
@@ -191,11 +199,11 @@
     public Drawable getDialogIcon() {
         return mDialogIcon;
     }
-    
+
     /**
      * Sets the text of the positive button of the dialog. This will be shown on
      * subsequent dialogs.
-     * 
+     *
      * @param positiveButtonText The text of the positive button.
      */
     public void setPositiveButtonText(CharSequence positiveButtonText) {
@@ -209,27 +217,27 @@
     public void setPositiveButtonText(@StringRes int positiveButtonTextResId) {
         setPositiveButtonText(getContext().getString(positiveButtonTextResId));
     }
-    
+
     /**
      * Returns the text of the positive button to be shown on subsequent
      * dialogs.
-     * 
+     *
      * @return The text of the positive button.
      */
     public CharSequence getPositiveButtonText() {
         return mPositiveButtonText;
     }
-    
+
     /**
      * Sets the text of the negative button of the dialog. This will be shown on
      * subsequent dialogs.
-     * 
+     *
      * @param negativeButtonText The text of the negative button.
      */
     public void setNegativeButtonText(CharSequence negativeButtonText) {
         mNegativeButtonText = negativeButtonText;
     }
-    
+
     /**
      * @see #setNegativeButtonText(CharSequence)
      * @param negativeButtonTextResId The negative button text as a resource.
@@ -237,38 +245,38 @@
     public void setNegativeButtonText(@StringRes int negativeButtonTextResId) {
         setNegativeButtonText(getContext().getString(negativeButtonTextResId));
     }
-    
+
     /**
      * Returns the text of the negative button to be shown on subsequent
      * dialogs.
-     * 
+     *
      * @return The text of the negative button.
      */
     public CharSequence getNegativeButtonText() {
         return mNegativeButtonText;
     }
-    
+
     /**
      * Sets the layout resource that is inflated as the {@link View} to be shown
      * as the content View of subsequent dialogs.
-     * 
+     *
      * @param dialogLayoutResId The layout resource ID to be inflated.
      * @see #setDialogMessage(CharSequence)
      */
     public void setDialogLayoutResource(int dialogLayoutResId) {
         mDialogLayoutResId = dialogLayoutResId;
     }
-    
+
     /**
      * Returns the layout resource that is used as the content View for
      * subsequent dialogs.
-     * 
+     *
      * @return The layout resource.
      */
     public int getDialogLayoutResource() {
         return mDialogLayoutResId;
     }
-    
+
     /**
      * Prepares the dialog builder to be shown when the preference is clicked.
      * Use this to set custom properties on the dialog.
@@ -278,7 +286,7 @@
      */
     protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
     }
-    
+
     @Override
     protected void onClick() {
         if (mDialog != null && mDialog.isShowing()) return;
@@ -290,14 +298,14 @@
      * Shows the dialog associated with this Preference. This is normally initiated
      * automatically on clicking on the preference. Call this method if you need to
      * show the dialog on some other event.
-     * 
+     *
      * @param state Optional instance state to restore on the dialog
      */
     protected void showDialog(Bundle state) {
         Context context = getContext();
 
         mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
-        
+
         mBuilder = new AlertDialog.Builder(context)
             .setTitle(mDialogTitle)
             .setIcon(mDialogIcon)
@@ -311,11 +319,11 @@
         } else {
             mBuilder.setMessage(mDialogMessage);
         }
-        
+
         onPrepareDialogBuilder(mBuilder);
-        
+
         getPreferenceManager().registerOnActivityDestroyListener(this);
-        
+
         // Create the dialog
         final Dialog dialog = mDialog = mBuilder.create();
         if (state != null) {
@@ -324,10 +332,29 @@
         if (needInputMethod()) {
             requestInputMethod(dialog);
         }
+        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
+            @Override
+            public void onShow(DialogInterface dialog) {
+                removeDismissCallbacks();
+            }
+        });
         dialog.setOnDismissListener(this);
         dialog.show();
     }
 
+    void postDismiss() {
+        removeDismissCallbacks();
+        View decorView = mDialog.getWindow().getDecorView();
+        decorView.post(mDismissRunnable);
+    }
+
+    private void removeDismissCallbacks() {
+        if (mDialog != null && mDialog.getWindow() != null
+                && mDialog.getWindow().getDecorView() != null) {
+            mDialog.getWindow().getDecorView().removeCallbacks(mDismissRunnable);
+        }
+    }
+
     /**
      * Returns whether the preference needs to display a soft input method when the dialog
      * is displayed. Default is false. Subclasses should override this method if they need
@@ -350,7 +377,7 @@
      * Creates the content view for the dialog (if a custom content view is
      * required). By default, it inflates the dialog layout resource if it is
      * set.
-     * 
+     *
      * @return The content View for the dialog.
      * @see #setLayoutResource(int)
      */
@@ -358,48 +385,49 @@
         if (mDialogLayoutResId == 0) {
             return null;
         }
-        
+
         LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext());
         return inflater.inflate(mDialogLayoutResId, null);
     }
-    
+
     /**
      * Binds views in the content View of the dialog to data.
      * <p>
      * Make sure to call through to the superclass implementation.
-     * 
+     *
      * @param view The content View of the dialog, if it is custom.
      */
     @CallSuper
     protected void onBindDialogView(View view) {
         View dialogMessageView = view.findViewById(com.android.internal.R.id.message);
-        
+
         if (dialogMessageView != null) {
             final CharSequence message = getDialogMessage();
             int newVisibility = View.GONE;
-            
+
             if (!TextUtils.isEmpty(message)) {
                 if (dialogMessageView instanceof TextView) {
                     ((TextView) dialogMessageView).setText(message);
                 }
-                
+
                 newVisibility = View.VISIBLE;
             }
-            
+
             if (dialogMessageView.getVisibility() != newVisibility) {
                 dialogMessageView.setVisibility(newVisibility);
             }
         }
     }
-    
+
     public void onClick(DialogInterface dialog, int which) {
         mWhichButtonClicked = which;
     }
-    
+
+    @Override
     public void onDismiss(DialogInterface dialog) {
-        
+        removeDismissCallbacks();
         getPreferenceManager().unregisterOnActivityDestroyListener(this);
-        
+
         mDialog = null;
         onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
     }
@@ -407,7 +435,7 @@
     /**
      * Called when the dialog is dismissed and should be used to save data to
      * the {@link SharedPreferences}.
-     * 
+     *
      * @param positiveResult Whether the positive button was clicked (true), or
      *            the negative button was clicked or the dialog was canceled (false).
      */
@@ -416,7 +444,7 @@
 
     /**
      * Gets the dialog that is shown by this preference.
-     * 
+     *
      * @return The dialog, or null if a dialog is not being shown.
      */
     public Dialog getDialog() {
@@ -427,11 +455,11 @@
      * {@inheritDoc}
      */
     public void onActivityDestroy() {
-        
+
         if (mDialog == null || !mDialog.isShowing()) {
             return;
         }
-        
+
         mDialog.dismiss();
     }
 
@@ -466,7 +494,7 @@
     private static class SavedState extends BaseSavedState {
         boolean isDialogShowing;
         Bundle dialogBundle;
-        
+
         public SavedState(Parcel source) {
             super(source);
             isDialogShowing = source.readInt() == 1;
@@ -495,5 +523,5 @@
             }
         };
     }
-    
+
 }
diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java
index e7dec0e..c0c71af 100644
--- a/core/java/android/preference/ListPreference.java
+++ b/core/java/android/preference/ListPreference.java
@@ -33,7 +33,7 @@
  * <p>
  * This preference will store a string into the SharedPreferences. This string will be the value
  * from the {@link #setEntryValues(CharSequence[])} array.
- * 
+ *
  * @attr ref android.R.styleable#ListPreference_entries
  * @attr ref android.R.styleable#ListPreference_entryValues
  */
@@ -193,7 +193,7 @@
 
     /**
      * Sets the value to the given index from the entry values.
-     * 
+     *
      * @param index The index of the value to set.
      */
     public void setValueIndex(int index) {
@@ -201,30 +201,30 @@
             setValue(mEntryValues[index].toString());
         }
     }
-    
+
     /**
      * Returns the value of the key. This should be one of the entries in
      * {@link #getEntryValues()}.
-     * 
+     *
      * @return The value of the key.
      */
     public String getValue() {
-        return mValue; 
+        return mValue;
     }
-    
+
     /**
      * Returns the entry corresponding to the current value.
-     * 
+     *
      * @return The entry corresponding to the current value, or null.
      */
     public CharSequence getEntry() {
         int index = getValueIndex();
         return index >= 0 && mEntries != null ? mEntries[index] : null;
     }
-    
+
     /**
      * Returns the index of the given value (in the entry values array).
-     * 
+     *
      * @param value The value whose index should be returned.
      * @return The index of the value, or -1 if not found.
      */
@@ -238,22 +238,22 @@
         }
         return -1;
     }
-    
+
     private int getValueIndex() {
         return findIndexOfValue(mValue);
     }
-    
+
     @Override
     protected void onPrepareDialogBuilder(Builder builder) {
         super.onPrepareDialogBuilder(builder);
-        
+
         if (mEntries == null || mEntryValues == null) {
             throw new IllegalStateException(
                     "ListPreference requires an entries array and an entryValues array.");
         }
 
         mClickedDialogEntryIndex = getValueIndex();
-        builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex, 
+        builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
                 new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int which) {
                         mClickedDialogEntryIndex = which;
@@ -263,10 +263,10 @@
                          * click, and dismisses the dialog.
                          */
                         ListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
-                        dialog.dismiss();
+                        postDismiss();
                     }
         });
-        
+
         /*
          * The typical interaction for list-based dialogs is to have
          * click-on-an-item dismiss the dialog instead of the user having to
@@ -278,7 +278,7 @@
     @Override
     protected void onDialogClosed(boolean positiveResult) {
         super.onDialogClosed(positiveResult);
-        
+
         if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) {
             String value = mEntryValues[mClickedDialogEntryIndex].toString();
             if (callChangeListener(value)) {
@@ -296,7 +296,7 @@
     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
         setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue);
     }
-    
+
     @Override
     protected Parcelable onSaveInstanceState() {
         final Parcelable superState = super.onSaveInstanceState();
@@ -304,7 +304,7 @@
             // No need to save instance state since it's persistent
             return superState;
         }
-        
+
         final SavedState myState = new SavedState(superState);
         myState.value = getValue();
         return myState;
@@ -317,15 +317,15 @@
             super.onRestoreInstanceState(state);
             return;
         }
-         
+
         SavedState myState = (SavedState) state;
         super.onRestoreInstanceState(myState.getSuperState());
         setValue(myState.value);
     }
-    
+
     private static class SavedState extends BaseSavedState {
         String value;
-        
+
         public SavedState(Parcel source) {
             super(source);
             value = source.readString();
@@ -352,5 +352,5 @@
             }
         };
     }
-    
+
 }
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 149f2b4..dd011a0 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1444,18 +1444,6 @@
         }
 
         public static final class Media implements AudioColumns {
-
-            private static final String[] EXTERNAL_PATHS;
-
-            static {
-                String secondary_storage = System.getenv("SECONDARY_STORAGE");
-                if (secondary_storage != null) {
-                    EXTERNAL_PATHS = secondary_storage.split(":");
-                } else {
-                    EXTERNAL_PATHS = new String[0];
-                }
-            }
-
             /**
              * Get the content:// style URI for the audio media table on the
              * given volume.
@@ -1469,14 +1457,9 @@
             }
 
             public static Uri getContentUriForPath(String path) {
-                for (String ep : EXTERNAL_PATHS) {
-                    if (path.startsWith(ep)) {
-                        return EXTERNAL_CONTENT_URI;
-                    }
-                }
-
-                return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ?
-                        EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
+                return (path.startsWith(
+                        Environment.getStorageDirectory().getAbsolutePath() + "/")
+                        ? EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
             }
 
             /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7a4ef5d..29dbb83 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9131,6 +9131,19 @@
        public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
 
         /**
+         * Whether the wifi data connection should remain active even when higher
+         * priority networks like Ethernet are active, to keep both networks.
+         * In the case where higher priority networks are connected, wifi will be
+         * unused unless an application explicitly requests to use it.
+         *
+         * See ConnectivityService for more info.
+         *
+         * (0 = disabled, 1 = enabled)
+         * @hide
+         */
+        public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested";
+
+        /**
          * Size of the event buffer for IP connectivity metrics.
          * @hide
          */
@@ -9469,8 +9482,7 @@
          * Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
          *
          * Integer values are interpreted as boolean, and the absence of an explicit setting
-         * is interpreted as |true|.
-         * TODO: make the default |false|
+         * is interpreted as |false|.
          * @hide
          */
         public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
@@ -11340,14 +11352,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/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java
index 119f5d0..4f4d62a 100644
--- a/core/java/android/security/net/config/DirectoryCertificateSource.java
+++ b/core/java/android/security/net/config/DirectoryCertificateSource.java
@@ -16,26 +16,23 @@
 
 package android.security.net.config;
 
-import android.os.Environment;
-import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.Pair;
+
+import libcore.io.IoUtils;
+
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.InputStream;
 import java.io.IOException;
-import java.security.cert.Certificate;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.Collections;
 import java.util.Set;
-import libcore.io.IoUtils;
-
-import com.android.org.conscrypt.Hex;
-import com.android.org.conscrypt.NativeCrypto;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -192,8 +189,36 @@
     }
 
     private String getHash(X500Principal name) {
-        int hash = NativeCrypto.X509_NAME_hash_old(name);
-        return Hex.intToHexString(hash, 8);
+        int hash = hashName(name);
+        return intToHexString(hash, 8);
+    }
+
+    private static final char[] DIGITS = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+    private static String intToHexString(int i, int minWidth) {
+        int bufLen = 8;  // Max number of hex digits in an int
+        char[] buf = new char[bufLen];
+        int cursor = bufLen;
+
+        do {
+            buf[--cursor] = DIGITS[i & 0xf];
+        } while ((i >>>= 4) != 0 || (bufLen - cursor < minWidth));
+
+        return new String(buf, cursor, bufLen - cursor);
+    }
+
+    // This code matches the code in X509_NAME_hash_old() in Conscrypt, which in turn matches
+    // the names of certificate files.  It must be kept in sync.
+    private static int hashName(X500Principal principal) {
+        try {
+            byte[] digest = MessageDigest.getInstance("MD5").digest(principal.getEncoded());
+            int offset = 0;
+            return (((digest[offset++] & 0xff) << 0) | ((digest[offset++] & 0xff) << 8)
+                    | ((digest[offset++] & 0xff) << 16) | ((digest[offset] & 0xff) << 24));
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError(e);
+        }
     }
 
     private X509Certificate readCertificate(String file) {
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/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index b93afdf..bca2e11 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1421,6 +1421,10 @@
                     mServedView.getWindowToken() == appWindowToken) {
                 finishInputLocked();
             }
+            if (mCurRootView != null &&
+                    mCurRootView.getWindowToken() == appWindowToken) {
+                mCurRootView = null;
+            }
         }
     }
 
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index b591163..f90eab1 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -236,6 +236,7 @@
         displayName = FileUtils.buildValidFatFilename(displayName);
 
         final File before = getFileForDocId(docId);
+        final File beforeVisibleFile = getFileForDocId(docId, true);
         final File after = FileUtils.buildUniqueFile(before.getParentFile(), displayName);
         if (!before.renameTo(after)) {
             throw new IllegalStateException("Failed to rename to " + after);
@@ -245,7 +246,6 @@
         onDocIdChanged(docId);
         onDocIdChanged(afterDocId);
 
-        final File beforeVisibleFile = getFileForDocId(docId, true);
         final File afterVisibleFile = getFileForDocId(afterDocId, true);
         moveInMediaStore(beforeVisibleFile, afterVisibleFile);
 
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index c388148..fee8345 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -171,8 +171,7 @@
             boolean debuggable);
 
     private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
-            String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge,
-            boolean debuggable);
+            String abiToCopy, boolean extractNativeLibs, boolean debuggable);
 
     private static long sumNativeBinaries(Handle handle, String abi) {
         long sum = 0;
@@ -193,7 +192,7 @@
     public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
         for (long apkHandle : handle.apkHandles) {
             int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
-                    handle.extractNativeLibs, HAS_NATIVE_BRIDGE, handle.debuggable);
+                    handle.extractNativeLibs, handle.debuggable);
             if (res != INSTALL_SUCCEEDED) {
                 return res;
             }
@@ -448,9 +447,6 @@
     // We don't care about the other return values for now.
     private static final int BITCODE_PRESENT = 1;
 
-    private static final boolean HAS_NATIVE_BRIDGE =
-            !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0"));
-
     private static native int hasRenderscriptBitcode(long apkHandle);
 
     public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
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/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index 7fd83bc..f6d80a5 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -34,9 +34,18 @@
 /**
  * Helper functions for dumping the state of system services.
  * Test:
- atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
+ atest FrameworksCoreTests:DumpUtilsTest
  */
 public final class DumpUtils {
+
+    /**
+     * List of component names that should be dumped in the bug report critical section.
+     *
+     * @hide
+     */
+    public static final ComponentName[] CRITICAL_SECTION_COMPONENTS = {
+            new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService")
+    };
     private static final String TAG = "DumpUtils";
     private static final boolean DEBUG = false;
 
@@ -213,6 +222,45 @@
     }
 
     /**
+     * Return whether a package should be dumped in the critical section.
+     */
+    private static boolean isCriticalPackage(@Nullable ComponentName cname) {
+        if (cname == null) {
+            return false;
+        }
+
+        for (int i = 0; i < CRITICAL_SECTION_COMPONENTS.length; i++) {
+            if (cname.equals(CRITICAL_SECTION_COMPONENTS[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return whether a package name is considered to be part of the platform and in the critical
+     * section.
+     *
+     * @hide
+     */
+    public static boolean isPlatformCriticalPackage(@Nullable ComponentName.WithComponentName wcn) {
+        return (wcn != null) && isPlatformPackage(wcn.getComponentName()) &&
+                isCriticalPackage(wcn.getComponentName());
+    }
+
+    /**
+     * Return whether a package name is considered to be part of the platform but not in the the
+     * critical section.
+     *
+     * @hide
+     */
+    public static boolean isPlatformNonCriticalPackage(
+            @Nullable ComponentName.WithComponentName wcn) {
+        return (wcn != null) && isPlatformPackage(wcn.getComponentName()) &&
+                !isCriticalPackage(wcn.getComponentName());
+    }
+
+    /**
      * Used for dumping providers and services. Return a predicate for a given filter string.
      * @hide
      */
@@ -238,6 +286,16 @@
             return DumpUtils::isNonPlatformPackage;
         }
 
+        // Dump all platform-critical?
+        if ("all-platform-critical".equals(filterString)) {
+            return DumpUtils::isPlatformCriticalPackage;
+        }
+
+        // Dump all platform-non-critical?
+        if ("all-platform-non-critical".equals(filterString)) {
+            return DumpUtils::isPlatformNonCriticalPackage;
+        }
+
         // Is the filter a component name? If so, do an exact match.
         final ComponentName filterCname = ComponentName.unflattenFromString(filterString);
         if (filterCname != null) {
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/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index 6df23f7..a1f2377 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -58,6 +58,11 @@
     int ret;
     int fd;
 
+    if (name == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+
     fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
     if (env->ExceptionCheck()) {
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index dc04269..5eefc81 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -176,7 +176,6 @@
     void** args = reinterpret_cast<void**>(arg);
     jstring* javaNativeLibPath = (jstring*) args[0];
     jboolean extractNativeLibs = *(jboolean*) args[1];
-    jboolean hasNativeBridge = *(jboolean*) args[2];
 
     ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
 
@@ -206,9 +205,7 @@
             return INSTALL_FAILED_INVALID_APK;
         }
 
-        if (!hasNativeBridge) {
-          return INSTALL_SUCCEEDED;
-        }
+        return INSTALL_SUCCEEDED;
     }
 
     // Build local file path
@@ -489,9 +486,9 @@
 static jint
 com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
         jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
-        jboolean extractNativeLibs, jboolean hasNativeBridge, jboolean debuggable)
+        jboolean extractNativeLibs, jboolean debuggable)
 {
-    void* args[] = { &javaNativeLibPath, &extractNativeLibs, &hasNativeBridge };
+    void* args[] = { &javaNativeLibPath, &extractNativeLibs };
     return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable,
             copyFileIfChanged, reinterpret_cast<void*>(args));
 }
@@ -597,7 +594,7 @@
             "(J)V",
             (void *)com_android_internal_content_NativeLibraryHelper_close},
     {"nativeCopyNativeBinaries",
-            "(JLjava/lang/String;Ljava/lang/String;ZZZ)I",
+            "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
             (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
     {"nativeSumNativeBinaries",
             "(JLjava/lang/String;Z)J",
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index c15b7ee..109e65c 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -285,10 +285,6 @@
 static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
                                   jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
                                   jboolean useBpfStats) {
-    ScopedUtfChars path8(env, path);
-    if (path8.c_str() == NULL) {
-        return -1;
-    }
 
     std::vector<std::string> limitIfaces;
     if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
@@ -308,6 +304,11 @@
         if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
             return -1;
     } else {
+        ScopedUtfChars path8(env, path);
+        if (path8.c_str() == NULL) {
+            ALOGE("the qtaguid legacy path is invalid: %s", path8.c_str());
+            return -1;
+        }
         if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
                                          limitUid, path8.c_str()) < 0)
             return -1;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ac9617c..73bb1fc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1827,6 +1827,13 @@
     <permission android:name="android.permission.BIND_SCREENING_SERVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by a {@link android.telecom.CallRedirectionService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature|privileged
+    -->
+    <permission android:name="android.permission.BIND_CALL_REDIRECTION_SERVICE"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by a {@link android.telecom.ConnectionService},
          to ensure that only the system can bind to it.
          @deprecated {@link android.telecom.ConnectionService}s should require
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/config.xml b/core/res/res/values/config.xml
index 632e439..477b17e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3463,4 +3463,12 @@
     <!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
     <bool name="config_swipe_up_gesture_setting_available">false</bool>
 
+    <!-- Pre-scale volume at volume step 1 for Absolute Volume -->
+    <fraction name="config_prescaleAbsoluteVolume_index1">50%</fraction>
+
+    <!-- Pre-scale volume at volume step 2 for Absolute Volume -->
+    <fraction name="config_prescaleAbsoluteVolume_index2">70%</fraction>
+
+    <!-- Pre-scale volume at volume step 3 for Absolute Volume -->
+    <fraction name="config_prescaleAbsoluteVolume_index3">85%</fraction>
 </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/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index de5dc43..e9a54f1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3393,4 +3393,9 @@
 
   <java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" />
   <java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
+
+  <!-- For Bluetooth AbsoluteVolume -->
+  <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index1" />
+  <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index2" />
+  <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index3" />
 </resources>
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/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java
index 1349844..1286b13 100644
--- a/core/tests/coretests/src/android/net/LocalSocketTest.java
+++ b/core/tests/coretests/src/android/net/LocalSocketTest.java
@@ -22,6 +22,7 @@
 import android.net.LocalSocketAddress;
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
+
 import junit.framework.TestCase;
 
 import java.io.FileDescriptor;
@@ -39,6 +40,20 @@
 
         ls = new LocalSocket();
 
+        try {
+            ls.connect(new LocalSocketAddress(null));
+            fail("Expected NullPointerException");
+        } catch (NullPointerException e) {
+            // pass
+        }
+
+        try {
+            ls.bind(new LocalSocketAddress(null));
+            fail("Expected NullPointerException");
+        } catch (NullPointerException e) {
+            // pass
+        }
+
         ls.connect(new LocalSocketAddress("android.net.LocalSocketTest"));
 
         ls1 = ss.accept();
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 02e0d02..23be634 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,
@@ -457,6 +456,7 @@
                     Settings.Global.WFC_IMS_MODE,
                     Settings.Global.WFC_IMS_ROAMING_ENABLED,
                     Settings.Global.WFC_IMS_ROAMING_MODE,
+                    Settings.Global.WIFI_ALWAYS_REQUESTED,
                     Settings.Global.WIFI_BADGING_THRESHOLDS,
                     Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
                     Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
index 45b19bc..a44b860 100644
--- a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java
@@ -15,8 +15,11 @@
  */
 package com.android.internal.util;
 
+import static com.android.internal.util.DumpUtils.CRITICAL_SECTION_COMPONENTS;
 import static com.android.internal.util.DumpUtils.filterRecord;
 import static com.android.internal.util.DumpUtils.isNonPlatformPackage;
+import static com.android.internal.util.DumpUtils.isPlatformCriticalPackage;
+import static com.android.internal.util.DumpUtils.isPlatformNonCriticalPackage;
 import static com.android.internal.util.DumpUtils.isPlatformPackage;
 
 import android.content.ComponentName;
@@ -25,7 +28,7 @@
 
 /**
  * Run with:
- atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpTest.java
+ atest FrameworksCoreTests:DumpUtilsTest
  */
 public class DumpUtilsTest extends TestCase {
 
@@ -89,6 +92,32 @@
         assertTrue(isNonPlatformPackage(wcn("com.google.def/abc")));
     }
 
+    public void testIsPlatformCriticalPackage() {
+        for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) {
+            assertTrue(isPlatformCriticalPackage(() -> componentName));
+            assertTrue(isPlatformPackage(componentName));
+        }
+        assertFalse(isPlatformCriticalPackage(wcn("com.google.p/abc")));
+        assertFalse(isPlatformCriticalPackage(wcn("com.android.def/abc")));
+        assertFalse(isPlatformCriticalPackage(wcn("com.android.abc")));
+        assertFalse(isPlatformCriticalPackage(wcn("com.android")));
+        assertFalse(isPlatformCriticalPackage(wcn(null)));
+        assertFalse(isPlatformCriticalPackage(null));
+    }
+
+    public void testIsPlatformNonCriticalPackage() {
+        for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) {
+            assertFalse(isPlatformNonCriticalPackage(() -> componentName));
+        }
+        assertTrue(isPlatformNonCriticalPackage(wcn("android/abc")));
+        assertTrue(isPlatformNonCriticalPackage(wcn("android.abc/abc")));
+        assertTrue(isPlatformNonCriticalPackage(wcn("com.android.def/abc")));
+
+        assertFalse(isPlatformNonCriticalPackage(wcn("com.google.def/abc")));
+        assertFalse(isPlatformNonCriticalPackage(wcn(null)));
+        assertFalse(isPlatformNonCriticalPackage(null));
+    }
+
     public void testFilterRecord() {
         assertFalse(filterRecord(null).test(wcn("com.google.p/abc")));
         assertFalse(filterRecord(null).test(wcn("com.android.p/abc")));
@@ -105,6 +134,19 @@
         assertFalse(filterRecord("all-non-platform").test(wcn("com.android.p/abc")));
         assertFalse(filterRecord("all-non-platform").test(wcn(null)));
 
+        for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) {
+            assertTrue(filterRecord("all-platform-critical").test((() -> componentName)));
+            assertFalse(filterRecord("all-platform-non-critical").test((() -> componentName)));
+            assertTrue(filterRecord("all-platform").test((() -> componentName)));
+        }
+        assertFalse(filterRecord("all-platform-critical").test(wcn("com.google.p/abc")));
+        assertFalse(filterRecord("all-platform-critical").test(wcn("com.android.p/abc")));
+        assertFalse(filterRecord("all-platform-critical").test(wcn(null)));
+
+        assertTrue(filterRecord("all-platform-non-critical").test(wcn("com.android.p/abc")));
+        assertFalse(filterRecord("all-platform-non-critical").test(wcn("com.google.p/abc")));
+        assertFalse(filterRecord("all-platform-non-critical").test(wcn(null)));
+
         // Partial string match.
         assertTrue(filterRecord("abc").test(wcn("com.google.p/.abc")));
         assertFalse(filterRecord("abc").test(wcn("com.google.p/.def")));
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/data/keyboards/Vendor_054c_Product_0268.kl b/data/keyboards/Vendor_054c_Product_0268.kl
index 522db3c..b463dd8 100644
--- a/data/keyboards/Vendor_054c_Product_0268.kl
+++ b/data/keyboards/Vendor_054c_Product_0268.kl
@@ -21,8 +21,6 @@
 key 0x126    DPAD_DOWN
 key 0x127    DPAD_LEFT
 
-key 0x120    BUTTON_SELECT
-key 0x123    BUTTON_START
 key 0x12e    BUTTON_A
 key 0x12d    BUTTON_B
 key 0x12f    BUTTON_X
@@ -34,9 +32,6 @@
 key 0x121    BUTTON_THUMBL
 key 0x122    BUTTON_THUMBR
 
-# PS key
-key 0x2d0    BUTTON_MODE
-
 # Left Analog Stick
 axis 0x00    X
 axis 0x01    Y
@@ -74,3 +69,11 @@
 
 # Square
 # axis 0x37
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x120    BUTTON_SELECT
+# Start
+key 0x123    BUTTON_START
+# PS key
+key 0x2d0    BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8000.kl
@@ -0,0 +1,57 @@
+# 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.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+#   and when connected over Bluetooth
+#
+
+key 0x220   DPAD_UP
+key 0x223   DPAD_RIGHT
+key 0x221   DPAD_DOWN
+key 0x222   DPAD_LEFT
+
+key 0x130   BUTTON_A
+key 0x131   BUTTON_B
+key 0x134   BUTTON_X
+key 0x133   BUTTON_Y
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+key 0x13d   BUTTON_THUMBL
+key 0x13e   BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# L2 trigger
+axis 0x02   LTRIGGER
+
+# R2 trigger
+axis 0x05   RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a   BUTTON_SELECT
+# Start
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
new file mode 100644
index 0000000..3d93f0f
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8100.kl
@@ -0,0 +1,57 @@
+# 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.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12
+#   and when connected over Bluetooth
+#
+
+key 0x220   DPAD_UP
+key 0x223   DPAD_RIGHT
+key 0x221   DPAD_DOWN
+key 0x222   DPAD_LEFT
+
+key 0x130   BUTTON_A
+key 0x131   BUTTON_B
+key 0x134   BUTTON_X
+key 0x133   BUTTON_Y
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+key 0x13d   BUTTON_THUMBL
+key 0x13e   BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# L2 trigger
+axis 0x02   LTRIGGER
+
+# R2 trigger
+axis 0x05   RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a   BUTTON_SELECT
+# Start
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
new file mode 100644
index 0000000..5fe35f7
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0268_Version_8111.kl
@@ -0,0 +1,57 @@
+# 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.
+
+#
+# Sony Playstation(R)3 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.12 and when
+#   connected over USB
+#
+
+key 0x220   DPAD_UP
+key 0x223   DPAD_RIGHT
+key 0x221   DPAD_DOWN
+key 0x222   DPAD_LEFT
+
+key 0x130   BUTTON_A
+key 0x131   BUTTON_B
+key 0x134   BUTTON_X
+key 0x133   BUTTON_Y
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+key 0x13d   BUTTON_THUMBL
+key 0x13e   BUTTON_THUMBR
+
+# left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# L2 trigger
+axis 0x02   LTRIGGER
+
+# R2 trigger
+axis 0x05   RTRIGGER
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Select
+key 0x13a   BUTTON_SELECT
+# Start
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
diff --git a/data/keyboards/Vendor_054c_Product_05c4.kl b/data/keyboards/Vendor_054c_Product_05c4.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_05c4.kl
+++ b/data/keyboards/Vendor_054c_Product_05c4.kl
@@ -60,7 +60,6 @@
 key 0x138    BUTTON_SELECT
 # Options
 key 0x139    BUTTON_START
-
 # PS key
 key 0x13c    BUTTON_MODE
 
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8000.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8100.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_05c4_Version_8111.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+#   connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc.kl b/data/keyboards/Vendor_054c_Product_09cc.kl
index a1284a4..cd7ab1f 100644
--- a/data/keyboards/Vendor_054c_Product_09cc.kl
+++ b/data/keyboards/Vendor_054c_Product_09cc.kl
@@ -60,7 +60,6 @@
 key 0x138    BUTTON_SELECT
 # Options
 key 0x139    BUTTON_START
-
 # PS key
 key 0x13c    BUTTON_MODE
 
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8000.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
new file mode 100644
index 0000000..19fcb86
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8100.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10
+#   and when connected over Bluetooth
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
new file mode 100644
index 0000000..d38bdec
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_09cc_Version_8111.kl
@@ -0,0 +1,68 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 Controller
+# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when
+#   connected over USB
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/data/keyboards/Vendor_054c_Product_0ba0.kl b/data/keyboards/Vendor_054c_Product_0ba0.kl
new file mode 100644
index 0000000..bc6fc3b
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0.kl
@@ -0,0 +1,70 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x130    BUTTON_X
+# Cross
+key 0x131    BUTTON_A
+# Circle
+key 0x132    BUTTON_B
+# Triangle
+key 0x133    BUTTON_Y
+
+key 0x134    BUTTON_L1
+key 0x135    BUTTON_R1
+key 0x136    BUTTON_L2
+key 0x137    BUTTON_R2
+
+# L2 axis
+axis 0x03   LTRIGGER
+# R2 axis
+axis 0x04   RTRIGGER
+
+
+# Left Analog Stick
+axis 0x00    X
+axis 0x01    Y
+# Right Analog Stick
+axis 0x02    Z
+axis 0x05    RZ
+
+# Left stick click
+key 0x13a    BUTTON_THUMBL
+# Right stick click
+key 0x13b    BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x138    BUTTON_SELECT
+# Options
+key 0x139    BUTTON_START
+# PS key
+key 0x13c    BUTTON_MODE
+
+# Touchpad press
+# The touchpad for this joystick will become a separate input device in future releases
+# and this button will be equivalent to left mouse button
+# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions
+key 0x13d   BUTTON_1
diff --git a/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
new file mode 100644
index 0000000..8b85a38
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ba0_Version_8111.kl
@@ -0,0 +1,67 @@
+# 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.
+
+#
+# Sony Playstation(R) DualShock 4 USB Dongle
+# - Version 0x8111 is for Linux hid-sony driver >=4.10
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 0x134   BUTTON_X
+# Cross
+key 0x130   BUTTON_A
+# Circle
+key 0x131   BUTTON_B
+# Triangle
+key 0x133   BUTTON_Y
+
+key 0x136   BUTTON_L1
+key 0x137   BUTTON_R1
+key 0x138   BUTTON_L2
+key 0x139   BUTTON_R2
+
+# L2 axis
+axis 0x02   LTRIGGER
+# R2 axis
+axis 0x05   RTRIGGER
+
+# Left Analog Stick
+axis 0x00   X
+axis 0x01   Y
+# Right Analog Stick
+axis 0x03   Z
+axis 0x04   RZ
+
+# Left stick click
+key 0x13d   BUTTON_THUMBL
+# Right stick click
+key 0x13e   BUTTON_THUMBR
+
+# Hat
+axis 0x10   HAT_X
+axis 0x11   HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share
+key 0x13a   BUTTON_SELECT
+# Options
+key 0x13b   BUTTON_START
+# PS key
+key 0x13c   BUTTON_MODE
+
+# In kernel versions >= 4.10, the touchpad is a separate input device,
+# so the touchpad button click will not be covered by this layout.
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index e4cd6a8..6c9eee0 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -47,11 +47,11 @@
   constexpr unique_cptr() : ptr_(nullptr) {}
   constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
   explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
-  unique_cptr(unique_cptr&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; }
+  unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; }
 
   ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); }
 
-  inline unique_cptr& operator=(unique_cptr&& o) {
+  inline unique_cptr& operator=(unique_cptr&& o) noexcept {
     if (&o == this) {
       return *this;
     }
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e6d2a6f..f2d50cd 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -146,7 +146,7 @@
                              frame[FrameInfoIndex::IntendedVsync] + mFrameInterval);
 
     // If we hit the deadline, cool!
-    if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline) {
+    if (frame[FrameInfoIndex::FrameCompleted] < mSwapDeadline || totalDuration < mFrameInterval) {
         if (isTripleBuffered) {
             mData->reportJankType(JankType::kHighInputLatency);
             (*mGlobalData)->reportJankType(JankType::kHighInputLatency);
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 02e1f2f..2eed70f 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -159,8 +159,9 @@
 
     public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
     public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
-    private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio";
-    private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio";
+    private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio";
+    private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio";
+    private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio";
     private static String sLastInternalScanFingerprint;
 
     private static final String[] ID3_GENRES = {
@@ -1192,6 +1193,9 @@
         if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
                 || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
                 || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
+                || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR)
+                || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR)
+                || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
                 || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
                 || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
                 || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
new file mode 100644
index 0000000..bb91d4b
--- /dev/null
+++ b/media/jni/OWNERS
@@ -0,0 +1,2 @@
+# extra for MTP related files
+per-file android_mtp_*.cpp=marcone@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com
diff --git a/media/tests/MtpTests/OWNERS b/media/tests/MtpTests/OWNERS
new file mode 100644
index 0000000..1928ba8
--- /dev/null
+++ b/media/tests/MtpTests/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+marcone@google.com
+jsharkey@android.com
+jameswei@google.com
+rmojumder@google.com
+
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index fd1212a..774a035 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -130,6 +130,22 @@
             mProbeSpec = null;
         }
 
+        mNetworkCallback = new NetworkCallback() {
+            @Override
+            public void onLost(Network lostNetwork) {
+                // If the network disappears while the app is up, exit.
+                if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
+            }
+        };
+        mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), mNetworkCallback);
+
+        // If the network has disappeared, exit.
+        final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
+        if (networkCapabilities == null) {
+            finishAndRemoveTask();
+            return;
+        }
+
         // Also initializes proxy system properties.
         mNetwork = mNetwork.getPrivateDnsBypassingCopy();
         mCm.bindProcessToNetwork(mNetwork);
@@ -139,24 +155,6 @@
         // setContentView initializes the WebView logic which in turn reads the system properties.
         setContentView(R.layout.activity_captive_portal_login);
 
-        // Exit app if Network disappears.
-        final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
-        if (networkCapabilities == null) {
-            finishAndRemoveTask();
-            return;
-        }
-        mNetworkCallback = new NetworkCallback() {
-            @Override
-            public void onLost(Network lostNetwork) {
-                if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
-            }
-        };
-        final NetworkRequest.Builder builder = new NetworkRequest.Builder();
-        for (int transportType : networkCapabilities.getTransportTypes()) {
-            builder.addTransportType(transportType);
-        }
-        mCm.registerNetworkCallback(builder.build(), mNetworkCallback);
-
         getActionBar().setDisplayShowHomeEnabled(false);
         getActionBar().setElevation(0); // remove shadow
         getActionBar().setTitle(getHeaderTitle());
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/ExtServices/tests/AndroidTest.xml b/packages/ExtServices/tests/AndroidTest.xml
new file mode 100644
index 0000000..c3d32de
--- /dev/null
+++ b/packages/ExtServices/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?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 Tests for ExtServices">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="ExtServicesUnitTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="ExtServicesUnitTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.ext.services.tests.unit" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a46c3e6..76a216d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1125,4 +1125,7 @@
 
     <!-- time label for event have that happened very recently [CHAR LIMIT=60] -->
     <string name="time_unit_just_now">Just now</string>
+
+    <!-- The notice header of Third-party licenses. not translatable -->
+    <string name="notice_header" translatable="false"></string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 541877c..3c58160 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -105,7 +105,7 @@
                 BluetoothProfile.A2DP);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index ac5f537..656f23f 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;
         }
     }
@@ -100,7 +99,7 @@
                 BluetoothProfile.A2DP_SINK);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
@@ -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/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e96c44d..8fa9597 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -250,7 +250,7 @@
 
         int preferredProfiles = 0;
         for (LocalBluetoothProfile profile : mProfiles) {
-            if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
+            if (connectAllProfiles ? profile.accessProfileEnabled() : profile.isAutoConnectable()) {
                 if (profile.isPreferred(mDevice)) {
                     ++preferredProfiles;
                     connectInt(profile);
@@ -736,7 +736,7 @@
         List<LocalBluetoothProfile> connectableProfiles =
                 new ArrayList<LocalBluetoothProfile>();
         for (LocalBluetoothProfile profile : mProfiles) {
-            if (profile.isConnectable()) {
+            if (profile.accessProfileEnabled()) {
                 connectableProfiles.add(profile);
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 7f83860..3bb8450 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -106,7 +106,7 @@
                 BluetoothProfile.HEADSET);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 1d2fda9..06d60e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -103,7 +103,7 @@
                 BluetoothProfile.HEARING_AID);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return false;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 03f6afb..4ae9b32 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;
         }
     }
@@ -108,7 +107,7 @@
     }
 
     @Override
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
@@ -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..1c04e83 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;
         }
     }
@@ -102,7 +97,7 @@
     }
 
     @Override
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
@@ -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/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index dc17e44..1e06481 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -95,7 +95,7 @@
                 BluetoothProfile.HID_HOST);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
index 0447f37..4b0ca74 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
@@ -26,9 +26,9 @@
 public interface LocalBluetoothProfile {
 
     /**
-     * Returns true if the user can initiate a connection, false otherwise.
+     * Return {@code true} if the user can initiate a connection for this profile in UI.
      */
-    boolean isConnectable();
+    boolean accessProfileEnabled();
 
     /**
      * Returns true if the user can enable auto connection for this profile.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 08fbbed..75cbb65 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -29,6 +29,7 @@
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothPbap;
 import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothSap;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
@@ -101,6 +102,7 @@
     private final boolean mUsePbapPce;
     private final boolean mUseMapClient;
     private HearingAidProfile mHearingAidProfile;
+    private SapProfile mSapProfile;
 
     /**
      * Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -196,12 +198,14 @@
         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)) {
             if (mA2dpSinkProfile == null) {
                 if(DEBUG) Log.d(TAG, "Adding local A2DP Sink profile");
-                mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter, mDeviceManager, this);
+                mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter,
+                        mDeviceManager, this);
                 addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME,
                         BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
             }
         } else if (mA2dpSinkProfile != null) {
-            Log.w(TAG, "Warning: A2DP Sink profile was previously added but the UUID is now missing.");
+            Log.w(TAG, "Warning: A2DP Sink profile was previously added but the "
+                    + "UUID is now missing.");
         }
 
         // Headset / Handsfree
@@ -217,7 +221,8 @@
                         BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
             }
         } else if (mHeadsetProfile != null) {
-            Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
+            Log.w(TAG, "Warning: HEADSET profile was previously added but the "
+                    + "UUID is now missing.");
         }
 
         // Headset HF
@@ -249,7 +254,8 @@
             }
         } else if (mMapClientProfile != null) {
             Log.w(TAG,
-                    "Warning: MAP Client profile was previously added but the UUID is now missing.");
+                    "Warning: MAP Client profile was previously added but the "
+                            + "UUID is now missing.");
         } else {
             Log.d(TAG, "MAP Client Uuid not found.");
         }
@@ -266,7 +272,7 @@
             Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
         }
 
-        //PBAP Client
+        // PBAP Client
         if (mUsePbapPce) {
             if (mPbapClientProfile == null) {
                 if(DEBUG) Log.d(TAG, "Adding local PBAP Client profile");
@@ -280,18 +286,27 @@
                 "Warning: PBAP Client profile was previously added but the UUID is now missing.");
         }
 
-        //Hearing Aid Client
+        // Hearing Aid Client
         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
             if (mHearingAidProfile == null) {
                 if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
-                mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
+                mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter,
+                        mDeviceManager, this);
                 addProfile(mHearingAidProfile, HearingAidProfile.NAME,
                         BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
             }
         } else if (mHearingAidProfile != null) {
-            Log.w(TAG, "Warning: Hearing Aid profile was previously added but the UUID is now missing.");
+            Log.w(TAG, "Warning: Hearing Aid profile was previously added but the "
+                    + "UUID is now missing.");
         }
 
+        // SAP
+        if (mSapProfile == null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) {
+            Log.d(TAG, "Adding local SAP profile");
+            mSapProfile = new SapProfile(mContext, mLocalAdapter, mDeviceManager, this);
+            addProfile(mSapProfile, SapProfile.NAME,
+                BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
+        }
         mEventManager.registerProfileIntentReceiver();
 
         // There is no local SDP record for HID and Settings app doesn't control PBAP Server.
@@ -635,6 +650,11 @@
             removedProfiles.remove(mHearingAidProfile);
         }
 
+        if (mSapProfile != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) {
+            profiles.add(mSapProfile);
+            removedProfiles.remove(mSapProfile);
+        }
+
         if (DEBUG) {
             Log.d(TAG,"New Profiles" + profiles.toString());
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index ad0d8ba..57712e3 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;
     }
 
@@ -109,7 +108,7 @@
                 BluetoothProfile.MAP_CLIENT);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
@@ -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/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 511c4ce..e59a036 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -107,7 +107,7 @@
                 BluetoothProfile.MAP);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
index dfd1622..e1e5dbe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
@@ -32,7 +32,7 @@
     // Order of this profile in device profiles list
     private static final int ORDINAL = 2;
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return false;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index b18b19b..0d566c7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -80,7 +80,7 @@
             BluetoothProfile.PAN);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index bdbfc9d..c83ff35 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;
         }
     }
@@ -115,7 +110,7 @@
                 BluetoothProfile.PBAP_CLIENT);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
@@ -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/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index e9d8cb5..adef0841 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -80,7 +80,7 @@
         BluetoothPbap pbap = new BluetoothPbap(context, new PbapServiceListener());
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 2c455d5f..e83c62d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -36,7 +36,6 @@
  */
 final class SapProfile implements LocalBluetoothProfile {
     private static final String TAG = "SapProfile";
-    private static boolean V = true;
 
     private BluetoothSap mService;
     private boolean mIsProfileReady;
@@ -59,7 +58,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 = (BluetoothSap) proxy;
             // We just bound to the service, so refresh the UI for any connected SAP devices.
             List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,7 +80,7 @@
         }
 
         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;
         }
@@ -106,7 +105,7 @@
                 BluetoothProfile.SAP);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
@@ -115,50 +114,47 @@
     }
 
     public boolean connect(BluetoothDevice device) {
-        if (mService == null) return false;
-        List<BluetoothDevice> sinks = mService.getConnectedDevices();
-        if (sinks != null) {
-            for (BluetoothDevice sink : sinks) {
-                mService.disconnect(sink);
-            }
+        if (mService == null) {
+            return false;
         }
         return mService.connect(device);
     }
 
     public boolean disconnect(BluetoothDevice device) {
-        if (mService == null) return false;
-        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-        if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
-            if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
-                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
-            }
-            return mService.disconnect(device);
-        } else {
+        if (mService == null) {
             return false;
         }
+        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        }
+        return mService.disconnect(device);
     }
 
     public int getConnectionStatus(BluetoothDevice device) {
-        if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
-        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-
-        return !deviceList.isEmpty() && deviceList.get(0).equals(device)
-                ? mService.getConnectionState(device)
-                : 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);
@@ -169,7 +165,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,
@@ -207,11 +205,11 @@
     }
 
     protected void finalize() {
-        if (V) Log.d(TAG, "finalize()");
+        Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
                 BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.SAP,
-                                                                       mService);
+                        mService);
                 mService = null;
             }catch (Throwable t) {
                 Log.w(TAG, "Error cleaning up SAP proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index 42306f6..9870b3c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -107,12 +107,13 @@
         mXmlFiles = xmlFiles;
     }
 
-    public static boolean generateHtml(List<File> xmlFiles, File outputFile) {
+    public static boolean generateHtml(List<File> xmlFiles, File outputFile,
+            String noticeHeader) {
         LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles);
-        return genertor.generateHtml(outputFile);
+        return genertor.generateHtml(outputFile, noticeHeader);
     }
 
-    private boolean generateHtml(File outputFile) {
+    private boolean generateHtml(File outputFile, String noticeHeader) {
         for (File xmlFile : mXmlFiles) {
             parse(xmlFile);
         }
@@ -125,7 +126,8 @@
         try {
             writer = new PrintWriter(outputFile);
 
-            generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer);
+            generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer,
+                noticeHeader);
 
             writer.flush();
             writer.close();
@@ -239,13 +241,18 @@
 
     @VisibleForTesting
     static void generateHtml(Map<String, String> fileNameToContentIdMap,
-            Map<String, String> contentIdToFileContentMap, PrintWriter writer) {
+            Map<String, String> contentIdToFileContentMap, PrintWriter writer,
+            String noticeHeader) {
         List<String> fileNameList = new ArrayList();
         fileNameList.addAll(fileNameToContentIdMap.keySet());
         Collections.sort(fileNameList);
 
         writer.println(HTML_HEAD_STRING);
 
+        if (!TextUtils.isEmpty(noticeHeader)) {
+            writer.println(noticeHeader);
+        }
+
         int count = 0;
         Map<String, Integer> contentIdToOrderMap = new HashMap();
         List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
index 2aaea65..8b562f70 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -21,6 +21,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.settingslib.R;
 import com.android.settingslib.utils.AsyncLoader;
 
 import java.io.File;
@@ -108,6 +109,7 @@
 
     @VisibleForTesting
     boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
-        return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
+        return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile,
+                    mContext.getString(R.string.notice_header));
     }
 }
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/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
new file mode 100644
index 0000000..6ff3e70
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.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.BluetoothSap;
+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 SapProfileTest {
+
+    @Mock
+    private LocalBluetoothAdapter mAdapter;
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothSap mService;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private SapProfile 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.SAP));
+
+        mProfile = new SapProfile(RuntimeEnvironment.application, mAdapter,
+                mDeviceManager, mProfileManager);
+        mServiceListener.onServiceConnected(BluetoothProfile.SAP, mService);
+    }
+
+    @Test
+    public void connect_shouldConnectBluetoothSap() {
+        mProfile.connect(mBluetoothDevice);
+        verify(mService).connect(mBluetoothDevice);
+    }
+
+    @Test
+    public void disconnect_shouldDisconnectBluetoothSap() {
+        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/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index 96b2a14..b00476b2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -50,7 +50,7 @@
             + "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
             + "</licenses2>";
 
-    private static final String EXPECTED_HTML_STRING =
+    private static final String HTML_HEAD_STRING =
             "<html><head>\n"
             + "<style type=\"text/css\">\n"
             + "body { padding: 0; font-family: sans-serif; }\n"
@@ -63,8 +63,12 @@
             + "</head>"
             + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
             + "<div class=\"toc\">\n"
-            + "<ul>\n"
-            + "<li><a href=\"#id0\">/file0</a></li>\n"
+            + "<ul>\n";
+
+    private static final String HTML_CUSTOM_HEADING = "Custom heading";
+
+    private static final String HTML_BODY_STRING =
+            "<li><a href=\"#id0\">/file0</a></li>\n"
             + "<li><a href=\"#id0\">/file1</a></li>\n"
             + "</ul>\n"
             + "</div><!-- table of contents -->\n"
@@ -81,6 +85,11 @@
             + "</td></tr><!-- same-license -->\n"
             + "</table></body></html>\n";
 
+    private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING;
+
+    private static final String EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING =
+            HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_BODY_STRING;
+
     @Test
     public void testParseValidXmlStream() throws XmlPullParserException, IOException {
         Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
@@ -117,7 +126,23 @@
 
         StringWriter output = new StringWriter();
         LicenseHtmlGeneratorFromXml.generateHtml(
-                fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output));
+                fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), "");
         assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING);
     }
+
+    @Test
+    public void testGenerateHtmlWithCustomHeading() {
+        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+        fileNameToContentIdMap.put("/file0", "0");
+        fileNameToContentIdMap.put("/file1", "0");
+        contentIdToFileContentMap.put("0", "license content #0");
+
+        StringWriter output = new StringWriter();
+        LicenseHtmlGeneratorFromXml.generateHtml(
+                fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output),
+                HTML_CUSTOM_HEADING);
+        assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING);
+    }
 }
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 4fc190d..2530abc 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -16,6 +16,8 @@
 
 package com.android.shell;
 
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 
 import static com.android.shell.BugreportPrefs.STATE_HIDE;
@@ -42,6 +44,7 @@
 
 import libcore.io.Streams;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ChooserActivity;
 import com.android.internal.logging.MetricsLogger;
@@ -232,9 +235,11 @@
      */
     private boolean mTakingScreenshot;
 
+    @GuardedBy("sNotificationBundle")
     private static final Bundle sNotificationBundle = new Bundle();
 
     private boolean mIsWatch;
+    private boolean mIsTv;
 
     private int mLastProgressPercent;
 
@@ -255,6 +260,9 @@
         final Configuration conf = mContext.getResources().getConfiguration();
         mIsWatch = (conf.uiMode & Configuration.UI_MODE_TYPE_MASK) ==
                 Configuration.UI_MODE_TYPE_WATCH;
+        PackageManager packageManager = getPackageManager();
+        mIsTv = packageManager.hasSystemFeature(FEATURE_LEANBACK)
+                || packageManager.hasSystemFeature(FEATURE_TELEVISION);
         NotificationManager nm = NotificationManager.from(mContext);
         nm.createNotificationChannel(
                 new NotificationChannel(NOTIFICATION_CHANNEL_ID,
@@ -500,8 +508,8 @@
                 .setProgress(info.max, info.progress, false)
                 .setOngoing(true);
 
-        // Wear bugreport doesn't need the bug info dialog, screenshot and cancel action.
-        if (!mIsWatch) {
+        // Wear and ATV bugreport doesn't need the bug info dialog, screenshot and cancel action.
+        if (!(mIsWatch || mIsTv)) {
             final Action cancelAction = new Action.Builder(null, mContext.getString(
                     com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build();
             final Intent infoIntent = new Intent(mContext, BugreportProgressService.class);
@@ -1053,10 +1061,12 @@
     }
 
     private static Notification.Builder newBaseNotification(Context context) {
-        if (sNotificationBundle.isEmpty()) {
-            // Rename notifcations from "Shell" to "Android System"
-            sNotificationBundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
-                    context.getString(com.android.internal.R.string.android_system_label));
+        synchronized (sNotificationBundle) {
+            if (sNotificationBundle.isEmpty()) {
+                // Rename notifcations from "Shell" to "Android System"
+                sNotificationBundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+                        context.getString(com.android.internal.R.string.android_system_label));
+            }
         }
         return new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
                 .addExtras(sNotificationBundle)
diff --git a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
index 0734e0d..4fbc226 100644
--- a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
+++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
@@ -20,6 +20,7 @@
 import android.database.MatrixCursor;
 import android.database.MatrixCursor.RowBuilder;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
@@ -68,6 +69,18 @@
     }
 
     @Override
+    public Cursor queryChildDocuments(
+            String parentDocumentId, String[] projection, String sortOrder)
+            throws FileNotFoundException {
+        final Cursor c = super.queryChildDocuments(parentDocumentId, projection, sortOrder);
+        final Bundle extras = new Bundle();
+        extras.putCharSequence(DocumentsContract.EXTRA_INFO,
+                getContext().getText(R.string.bugreport_confirm));
+        c.setExtras(extras);
+        return c;
+    }
+
+    @Override
     public Cursor queryDocument(String documentId, String[] projection)
             throws FileNotFoundException {
         if (DOC_ID_ROOT.equals(documentId)) {
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/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index cd831d1..0c498bb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -24,8 +24,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import sun.misc.Resource;
-
 public class NavigationBarCompat {
     /**
      * Touch slopes and thresholds for quick step operations. Drag slop is the point where the
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/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 9e4810c..35abb0a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -342,12 +342,11 @@
                 case SimPuk:
                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
                     SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
-                    if (securityMode != SecurityMode.None
-                            || !mLockPatternUtils.isLockScreenDisabled(
+                    if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled(
                             KeyguardUpdateMonitor.getCurrentUser())) {
-                        showSecurityScreen(securityMode);
-                    } else {
                         finish = true;
+                    } else {
+                        showSecurityScreen(securityMode);
                     }
                     break;
 
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/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 2031b27..1fefa18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -53,6 +53,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+    private final Object mCallbacksLock = new Object();
     private final Context mContext;
     private final GlobalSetting mModeSetting;
     private final GlobalSetting mConfigSetting;
@@ -113,16 +114,20 @@
 
     @Override
     public void addCallback(Callback callback) {
-        if (callback == null) {
-            Slog.e(TAG, "Attempted to add a null callback.");
-            return;
+        synchronized (mCallbacksLock) {
+            if (callback == null) {
+                Slog.e(TAG, "Attempted to add a null callback.");
+                return;
+            }
+            mCallbacks.add(callback);
         }
-        mCallbacks.add(callback);
     }
 
     @Override
     public void removeCallback(Callback callback) {
-        mCallbacks.remove(callback);
+        synchronized (mCallbacksLock) {
+            mCallbacks.remove(callback);
+        }
     }
 
     @Override
@@ -186,28 +191,40 @@
     }
 
     private void fireNextAlarmChanged() {
-        Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
+        }
     }
 
     private void fireEffectsSuppressorChanged() {
-        Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
+        }
     }
 
     private void fireZenChanged(int zen) {
-        Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
+        }
     }
 
     private void fireZenAvailableChanged(boolean available) {
-        Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
+        }
     }
 
     private void fireManualRuleChanged(ZenRule rule) {
-        Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
+        }
     }
 
     @VisibleForTesting
     protected void fireConfigChanged(ZenModeConfig config) {
-        Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
+        }
     }
 
     @VisibleForTesting
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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7602090..5e8ffb7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,9 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.os.Process.INVALID_UID;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
@@ -49,10 +52,12 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.net.ConnectionInfo;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.IConnectivityManager;
 import android.net.IIpConnectivityMetrics;
+import android.net.INetd;
 import android.net.INetdEventCallback;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
@@ -75,7 +80,6 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.NetworkWatchlistManager;
-import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.UidRange;
@@ -83,7 +87,9 @@
 import android.net.VpnService;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
+import android.net.netlink.InetDiagMessage;
 import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.NetdService;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -153,7 +159,6 @@
 import com.android.server.connectivity.NetworkMonitor;
 import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
-import com.android.server.connectivity.PacManager;
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.Tethering;
@@ -256,7 +261,8 @@
     // 0 is full bad, 100 is full good
     private int mDefaultInetConditionPublished = 0;
 
-    private INetworkManagementService mNetd;
+    private INetworkManagementService mNMS;
+    private INetd mNetd;
     private INetworkStatsService mStatsService;
     private INetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
@@ -387,9 +393,9 @@
     private static final int EVENT_PROMPT_UNVALIDATED = 29;
 
     /**
-     * used internally to (re)configure mobile data always-on settings.
+     * used internally to (re)configure always-on networks.
      */
-    private static final int EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON = 30;
+    private static final int EVENT_CONFIGURE_ALWAYS_ON_NETWORKS = 30;
 
     /**
      * used to add a network listener with a pending intent
@@ -745,6 +751,12 @@
         mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
                 NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
 
+        // The default WiFi request is a background request so that apps using WiFi are
+        // migrated to a better network (typically ethernet) when one comes up, instead
+        // of staying on WiFi forever.
+        mDefaultWifiRequest = createDefaultInternetRequestForTransport(
+                NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST);
+
         mHandlerThread = new HandlerThread("ConnectivityServiceThread");
         mHandlerThread.start();
         mHandler = new InternalHandler(mHandlerThread.getLooper());
@@ -756,7 +768,7 @@
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
 
         mContext = checkNotNull(context, "missing Context");
-        mNetd = checkNotNull(netManager, "missing INetworkManagementService");
+        mNMS = checkNotNull(netManager, "missing INetworkManagementService");
         mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
         mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
         mPolicyManagerInternal = checkNotNull(
@@ -764,6 +776,7 @@
                 "missing NetworkPolicyManagerInternal");
         mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
 
+        mNetd = NetdService.getInstance();
         mKeyStore = KeyStore.getInstance();
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
 
@@ -846,7 +859,7 @@
 
         mTethering = makeTethering();
 
-        mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
+        mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
 
         //set up the listener for user state for creating user VPNs
         IntentFilter intentFilter = new IntentFilter();
@@ -861,8 +874,8 @@
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
         try {
-            mNetd.registerObserver(mTethering);
-            mNetd.registerObserver(mDataActivityObserver);
+            mNMS.registerObserver(mTethering);
+            mNMS.registerObserver(mDataActivityObserver);
         } catch (RemoteException e) {
             loge("Error registering observer :" + e);
         }
@@ -893,7 +906,7 @@
 
         mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
 
-        mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+        mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties);
         registerPrivateDnsSettingsCallbacks();
     }
 
@@ -909,7 +922,7 @@
                 return mDefaultRequest;
             }
         };
-        return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
+        return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
                 IoThread.get().getLooper(), new MockableSystemProperties(),
                 deps);
     }
@@ -941,8 +954,8 @@
     // 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it
     //    by subclassing SettingsObserver.
     @VisibleForTesting
-    void updateMobileDataAlwaysOn() {
-        mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+    void updateAlwaysOnNetworks() {
+        mHandler.sendEmptyMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
     }
 
     // See FakeSettingsProvider comment above.
@@ -951,22 +964,30 @@
         mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
     }
 
-    private void handleMobileDataAlwaysOn() {
+    private void handleAlwaysOnNetworkRequest(
+            NetworkRequest networkRequest, String settingName, boolean defaultValue) {
         final boolean enable = toBool(Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1));
-        final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null);
+                mContext.getContentResolver(), settingName, encodeBool(defaultValue)));
+        final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null);
         if (enable == isEnabled) {
             return;  // Nothing to do.
         }
 
         if (enable) {
             handleRegisterNetworkRequest(new NetworkRequestInfo(
-                    null, mDefaultMobileDataRequest, new Binder()));
+                    null, networkRequest, new Binder()));
         } else {
-            handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID);
+            handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID);
         }
     }
 
+    private void handleConfigureAlwaysOnNetworks() {
+        handleAlwaysOnNetworkRequest(
+                mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
+        handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED,
+                false);
+    }
+
     private void registerSettingsCallbacks() {
         // Watch for global HTTP proxy changes.
         mSettingsObserver.observe(
@@ -976,7 +997,12 @@
         // Watch for whether or not to keep mobile data always on.
         mSettingsObserver.observe(
                 Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON),
-                EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+                EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
+
+        // Watch for whether or not to keep wifi always on.
+        mSettingsObserver.observe(
+                Settings.Global.getUriFor(Settings.Global.WIFI_ALWAYS_REQUESTED),
+                EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
     }
 
     private void registerPrivateDnsSettingsCallbacks() {
@@ -1473,6 +1499,20 @@
     };
 
     /**
+     * Ensures that the system cannot call a particular method.
+     */
+    private boolean disallowedBecauseSystemCaller() {
+        // TODO: start throwing a SecurityException when GnssLocationProvider stops calling
+        // requestRouteToHost.
+        if (isSystem(Binder.getCallingUid())) {
+            log("This method exists only for app backwards compatibility"
+                    + " and must not be called by system services.");
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface.
      * @param networkType the type of the network over which traffic to the
@@ -1483,6 +1523,9 @@
      */
     @Override
     public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+        if (disallowedBecauseSystemCaller()) {
+            return false;
+        }
         enforceChangePermission();
         if (mProtectedNetworks.contains(networkType)) {
             enforceConnectivityInternalPermission();
@@ -1560,7 +1603,7 @@
         if (DBG) log("Adding legacy route " + bestRoute +
                 " for UID/PID " + uid + "/" + Binder.getCallingPid());
         try {
-            mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
+            mNMS.addLegacyRouteForNetId(netId, bestRoute, uid);
         } catch (Exception e) {
             // never crash - catch them all
             if (DBG) loge("Exception trying to add a route: " + e);
@@ -1680,6 +1723,11 @@
                 "ConnectivityService");
     }
 
+    private boolean checkNetworkStackPermission() {
+        return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.NETWORK_STACK);
+    }
+
     private void enforceConnectivityRestrictedNetworksPermission() {
         try {
             mContext.enforceCallingOrSelfPermission(
@@ -1789,7 +1837,7 @@
     }
 
     void systemReady() {
-        loadGlobalProxy();
+        mProxyTracker.loadGlobalProxy();
         registerNetdEventCallback();
 
         synchronized (this) {
@@ -1806,8 +1854,8 @@
         // for user to unlock device too.
         updateLockdownVpn();
 
-        // Configure whether mobile data is always on.
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON));
+        // Create network requests for always-on networks.
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS));
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY));
 
@@ -1845,7 +1893,7 @@
 
         if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
             try {
-                mNetd.addIdleTimer(iface, timeout, type);
+                mNMS.addIdleTimer(iface, timeout, type);
             } catch (Exception e) {
                 // You shall not crash!
                 loge("Exception in setupDataActivityTracking " + e);
@@ -1864,7 +1912,7 @@
                               caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
             try {
                 // the call fails silently if no idle timer setup for this interface
-                mNetd.removeIdleTimer(iface);
+                mNMS.removeIdleTimer(iface);
             } catch (Exception e) {
                 loge("Exception in removeDataActivityTracking " + e);
             }
@@ -1872,6 +1920,18 @@
     }
 
     /**
+     * Update data activity tracking when network state is updated.
+     */
+    private void updateDataActivityTracking(NetworkAgentInfo newNetwork,
+            NetworkAgentInfo oldNetwork) {
+        if (newNetwork != null) {
+            setupDataActivityTracking(newNetwork);
+        }
+        if (oldNetwork != null) {
+            removeDataActivityTracking(oldNetwork);
+        }
+    }
+    /**
      * Reads the network specific MTU size from resources.
      * and set it on it's iface.
      */
@@ -1899,7 +1959,7 @@
 
         try {
             if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
-            mNetd.setMtu(iface, mtu);
+            mNMS.setMtu(iface, mtu);
         } catch (Exception e) {
             Slog.e(TAG, "exception in setMtu()" + e);
         }
@@ -2553,7 +2613,7 @@
         }
         nai.clearLingerState();
         if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
-            removeDataActivityTracking(nai);
+            updateDataActivityTracking(null /* newNetwork */, nai);
             notifyLockdownVpn(nai);
             ensureNetworkTransitionWakelock(nai.name());
         }
@@ -2573,7 +2633,7 @@
             // NetworkFactories, so network traffic isn't interrupted for an unnecessarily
             // long time.
             try {
-                mNetd.removeNetwork(nai.network.netId);
+                mNMS.removeNetwork(nai.network.netId);
             } catch (Exception e) {
                 loge("Exception removing network: " + e);
             }
@@ -2771,20 +2831,6 @@
                 }
             }
 
-            // TODO: remove this code once we know that the Slog.wtf is never hit.
-            //
-            // Find all networks that are satisfying this request and remove the request
-            // from their request lists.
-            // TODO - it's my understanding that for a request there is only a single
-            // network satisfying it, so this loop is wasteful
-            for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) {
-                if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) {
-                    Slog.wtf(TAG, "Request " + nri.request + " satisfied by " +
-                            otherNai.name() + ", but mNetworkAgentInfos says " +
-                            (nai != null ? nai.name() : "null"));
-                }
-            }
-
             // Maintain the illusion.  When this request arrived, we might have pretended
             // that a network connected to serve it, even though the network was already
             // connected.  Now that this request has gone away, we might have to pretend
@@ -3098,8 +3144,8 @@
                     handlePromptUnvalidated((Network) msg.obj);
                     break;
                 }
-                case EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON: {
-                    handleMobileDataAlwaysOn();
+                case EVENT_CONFIGURE_ALWAYS_ON_NETWORKS: {
+                    handleConfigureAlwaysOnNetworks();
                     break;
                 }
                 // Sent by KeepaliveTracker to process an app request on the state machine thread.
@@ -3409,31 +3455,6 @@
         mProxyTracker.setGlobalProxy(proxyProperties);
     }
 
-    private void loadGlobalProxy() {
-        ContentResolver res = mContext.getContentResolver();
-        String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST);
-        int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
-        String exclList = Settings.Global.getString(res,
-                Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
-        String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
-        if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
-            ProxyInfo proxyProperties;
-            if (!TextUtils.isEmpty(pacFileUrl)) {
-                proxyProperties = new ProxyInfo(pacFileUrl);
-            } else {
-                proxyProperties = new ProxyInfo(host, port, exclList);
-            }
-            if (!proxyProperties.isValid()) {
-                if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
-                return;
-            }
-
-            synchronized (mProxyTracker.mProxyLock) {
-                mProxyTracker.mGlobalProxy = proxyProperties;
-            }
-        }
-    }
-
     @Override
     @Nullable
     public ProxyInfo getGlobalProxy() {
@@ -3752,7 +3773,7 @@
                     Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
                     return false;
                 }
-                setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+                setLockdownTracker(new LockdownVpnTracker(mContext, mNMS, this, vpn, profile));
             } else {
                 setLockdownTracker(null);
             }
@@ -4007,7 +4028,7 @@
                 loge("Starting user already has a VPN");
                 return;
             }
-            userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
+            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
             mVpns.put(userId, userVpn);
             if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
                 updateLockdownVpn();
@@ -4527,6 +4548,10 @@
     // priority networks like Wi-Fi are active.
     private final NetworkRequest mDefaultMobileDataRequest;
 
+    // Request used to optionally keep wifi data active even when higher
+    // priority networks like ethernet are active.
+    private final NetworkRequest mDefaultWifiRequest;
+
     private NetworkAgentInfo getNetworkForRequest(int requestId) {
         synchronized (mNetworkForRequestId) {
             return mNetworkForRequestId.get(requestId);
@@ -4624,7 +4649,7 @@
         mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
         // Start or stop clat accordingly to network state.
-        networkAgent.updateClat(mNetd);
+        networkAgent.updateClat(mNMS);
         if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
@@ -4663,9 +4688,9 @@
         final String prefix = "iface:" + iface;
         try {
             if (add) {
-                mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask);
+                mNetd.wakeupAddInterface(iface, prefix, mark, mask);
             } else {
-                mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask);
+                mNetd.wakeupDelInterface(iface, prefix, mark, mask);
             }
         } catch (Exception e) {
             loge("Exception modifying wakeup packet monitoring: " + e);
@@ -4681,7 +4706,7 @@
         for (String iface : interfaceDiff.added) {
             try {
                 if (DBG) log("Adding iface " + iface + " to network " + netId);
-                mNetd.addInterfaceToNetwork(iface, netId);
+                mNMS.addInterfaceToNetwork(iface, netId);
                 wakeupModifyInterface(iface, caps, true);
             } catch (Exception e) {
                 loge("Exception adding interface: " + e);
@@ -4691,7 +4716,7 @@
             try {
                 if (DBG) log("Removing iface " + iface + " from network " + netId);
                 wakeupModifyInterface(iface, caps, false);
-                mNetd.removeInterfaceFromNetwork(iface, netId);
+                mNMS.removeInterfaceFromNetwork(iface, netId);
             } catch (Exception e) {
                 loge("Exception removing interface: " + e);
             }
@@ -4715,7 +4740,7 @@
             if (route.hasGateway()) continue;
             if (VDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNetd.addRoute(netId, route);
+                mNMS.addRoute(netId, route);
             } catch (Exception e) {
                 if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
                     loge("Exception in addRoute for non-gateway: " + e);
@@ -4726,7 +4751,7 @@
             if (route.hasGateway() == false) continue;
             if (VDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mNetd.addRoute(netId, route);
+                mNMS.addRoute(netId, route);
             } catch (Exception e) {
                 if ((route.getGateway() instanceof Inet4Address) || VDBG) {
                     loge("Exception in addRoute for gateway: " + e);
@@ -4737,7 +4762,7 @@
         for (RouteInfo route : routeDiff.removed) {
             if (VDBG) log("Removing Route [" + route + "] from network " + netId);
             try {
-                mNetd.removeRoute(netId, route);
+                mNMS.removeRoute(netId, route);
             } catch (Exception e) {
                 loge("Exception in removeRoute: " + e);
             }
@@ -4849,7 +4874,7 @@
         final String newPermission = getNetworkPermission(newNc);
         if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
             try {
-                mNetd.setNetworkPermission(nai.network.netId, newPermission);
+                mNMS.setNetworkPermission(nai.network.netId, newPermission);
             } catch (RemoteException e) {
                 loge("Exception in setNetworkPermission: " + e);
             }
@@ -4909,12 +4934,12 @@
             if (!newRanges.isEmpty()) {
                 final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
                 newRanges.toArray(addedRangesArray);
-                mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray);
+                mNMS.addVpnUidRanges(nai.network.netId, addedRangesArray);
             }
             if (!prevRanges.isEmpty()) {
                 final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
                 prevRanges.toArray(removedRangesArray);
-                mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray);
+                mNMS.removeVpnUidRanges(nai.network.netId, removedRangesArray);
             }
         } catch (Exception e) {
             // Never crash!
@@ -5083,9 +5108,9 @@
 
     private void makeDefault(NetworkAgentInfo newNetwork) {
         if (DBG) log("Switching to new default network: " + newNetwork);
-        setupDataActivityTracking(newNetwork);
+
         try {
-            mNetd.setDefaultNetId(newNetwork.network.netId);
+            mNMS.setDefaultNetId(newNetwork.network.netId);
         } catch (Exception e) {
             loge("Exception setting default network :" + e);
         }
@@ -5258,6 +5283,7 @@
             }
         }
         if (isNewDefault) {
+            updateDataActivityTracking(newNetwork, oldDefaultNetwork);
             // Notify system services that this network is up.
             makeDefault(newNetwork);
             // Log 0 -> X and Y -> X default network transitions, where X is the new default.
@@ -5480,12 +5506,12 @@
             try {
                 // This should never fail.  Specifying an already in use NetID will cause failure.
                 if (networkAgent.isVPN()) {
-                    mNetd.createVirtualNetwork(networkAgent.network.netId,
+                    mNMS.createVirtualNetwork(networkAgent.network.netId,
                             !networkAgent.linkProperties.getDnsServers().isEmpty(),
                             (networkAgent.networkMisc == null ||
                                 !networkAgent.networkMisc.allowBypass));
                 } else {
-                    mNetd.createPhysicalNetwork(networkAgent.network.netId,
+                    mNMS.createPhysicalNetwork(networkAgent.network.netId,
                             getNetworkPermission(networkAgent.networkCapabilities));
                 }
             } catch (Exception e) {
@@ -5922,4 +5948,49 @@
             pw.println("    Get airplane mode.");
         }
     }
+
+    /**
+     * Caller either needs to be an active VPN, or hold the NETWORK_STACK permission
+     * for testing.
+     */
+    private Vpn enforceActiveVpnOrNetworkStackPermission() {
+        if (checkNetworkStackPermission()) {
+            return null;
+        }
+        final int uid = Binder.getCallingUid();
+        final int user = UserHandle.getUserId(uid);
+        synchronized (mVpns) {
+            Vpn vpn = mVpns.get(user);
+            try {
+                if (vpn.getVpnInfo().ownerUid == uid) return vpn;
+            } catch (NullPointerException e) {
+                /* vpn is null, or VPN is not connected and getVpnInfo() is null. */
+            }
+        }
+        throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK "
+                + "permission");
+    }
+
+    /**
+     * @param connectionInfo the connection to resolve.
+     * @return {@code uid} if the connection is found and the app has permission to observe it
+     * (e.g., if it is associated with the calling VPN app's tunnel) or {@code INVALID_UID} if the
+     * connection is not found.
+     */
+    public int getConnectionOwnerUid(ConnectionInfo connectionInfo) {
+        final Vpn vpn = enforceActiveVpnOrNetworkStackPermission();
+        if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) {
+            throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
+        }
+
+        final int uid = InetDiagMessage.getConnectionOwnerUid(connectionInfo.protocol,
+                connectionInfo.local, connectionInfo.remote);
+
+        /* Filter out Uids not associated with the VPN. */
+        if (vpn != null && !vpn.appliesToUid(uid)) {
+            return INVALID_UID;
+        }
+
+        return uid;
+    }
 }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 380f6a7..8c25917 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -19,6 +19,8 @@
 import static android.Manifest.permission.DUMP;
 import static android.net.IpSecManager.INVALID_RESOURCE_ID;
 import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNSPEC;
 import static android.system.OsConstants.EINVAL;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_DGRAM;
@@ -63,6 +65,8 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
@@ -1426,6 +1430,17 @@
                         + "or Encryption algorithms");
     }
 
+    private int getFamily(String inetAddress) {
+        int family = AF_UNSPEC;
+        InetAddress checkAddress = NetworkUtils.numericToInetAddress(inetAddress);
+        if (checkAddress instanceof Inet4Address) {
+            family = AF_INET;
+        } else if (checkAddress instanceof Inet6Address) {
+            family = AF_INET6;
+        }
+        return family;
+    }
+
     /**
      * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
      * IllegalArgumentException if they are not.
@@ -1479,6 +1494,26 @@
         // Require a valid source address for all transforms.
         checkInetAddress(config.getSourceAddress());
 
+        // Check to ensure source and destination have the same address family.
+        String sourceAddress = config.getSourceAddress();
+        String destinationAddress = config.getDestinationAddress();
+        int sourceFamily = getFamily(sourceAddress);
+        int destinationFamily = getFamily(destinationAddress);
+        if (sourceFamily != destinationFamily) {
+            throw new IllegalArgumentException(
+                    "Source address ("
+                            + sourceAddress
+                            + ") and destination address ("
+                            + destinationAddress
+                            + ") have different address families.");
+        }
+
+        // Throw an error if UDP Encapsulation is not used in IPv4.
+        if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
+            throw new IllegalArgumentException(
+                    "UDP Encapsulation is not supported for this address family");
+        }
+
         switch (config.getMode()) {
             case IpSecTransform.MODE_TRANSPORT:
                 break;
@@ -1490,23 +1525,19 @@
         }
     }
 
-    private static final String TUNNEL_OP = "STOPSHIP"; // = AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+    private static final String TUNNEL_OP = AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS;
 
     private void enforceTunnelPermissions(String callingPackage) {
         checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
-        if (false) { // STOPSHIP if this line is present
-            switch (getAppOpsManager().noteOp(
-                        TUNNEL_OP,
-                        Binder.getCallingUid(), callingPackage)) {
-                case AppOpsManager.MODE_DEFAULT:
-                    mContext.enforceCallingOrSelfPermission(
-                            android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
-                    break;
-                case AppOpsManager.MODE_ALLOWED:
-                    return;
-                default:
-                    throw new SecurityException("Request to ignore AppOps for non-legacy API");
-            }
+        switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
+            case AppOpsManager.MODE_DEFAULT:
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
+                break;
+            case AppOpsManager.MODE_ALLOWED:
+                return;
+            default:
+                throw new SecurityException("Request to ignore AppOps for non-legacy API");
         }
     }
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index c79cf71..ab0a565 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -160,6 +160,8 @@
 
     private static final int MAX_UID_RANGES_PER_COMMAND = 10;
 
+    private static final  String[] EMPTY_STRING_ARRAY = new String[0];
+
     /**
      * Name representing {@link #setGlobalAlert(long)} limit when delivered to
      * {@link INetworkManagementEventObserver#limitReached(String, String)}.
@@ -1229,18 +1231,12 @@
     @Override
     public void startTethering(String[] dhcpRange) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        // cmd is "tether start first_start first_stop second_start second_stop ..."
         // an odd number of addrs will fail
 
-        final Command cmd = new Command("tether", "start");
-        for (String d : dhcpRange) {
-            cmd.appendArg(d);
-        }
-
         try {
-            mConnector.execute(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherStart(dhcpRange);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1248,9 +1244,9 @@
     public void stopTethering() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("tether", "stop");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherStop();
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1258,25 +1254,21 @@
     public boolean isTetheringStarted() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("tether", "status");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            final boolean isEnabled = mNetdService.tetherIsEnabled();
+            return isEnabled;
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
-
-        // 210 Tethering services started
-        event.checkCode(TetherStatusResult);
-        return event.getMessage().endsWith("started");
     }
 
     @Override
     public void tetherInterface(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("tether", "interface", "add", iface);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherInterfaceAdd(iface);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
         List<RouteInfo> routes = new ArrayList<>();
         // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it
@@ -1289,9 +1281,9 @@
     public void untetherInterface(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("tether", "interface", "remove", iface);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherInterfaceRemove(iface);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         } finally {
             removeInterfaceFromLocalNetwork(iface);
         }
@@ -1301,11 +1293,10 @@
     public String[] listTetheredInterfaces() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            return NativeDaemonEvent.filterMessageList(
-                    mConnector.executeForList("tether", "interface", "list"),
-                    TetherInterfaceListResult);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            final List<String> result = mNetdService.tetherInterfaceList();
+            return result.toArray(EMPTY_STRING_ARRAY);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1314,16 +1305,11 @@
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         int netId = (network != null) ? network.netId : ConnectivityManager.NETID_UNSET;
-        final Command cmd = new Command("tether", "dns", "set", netId);
-
-        for (String s : dns) {
-            cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
-        }
 
         try {
-            mConnector.execute(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.tetherDnsSet(netId, dns);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1331,10 +1317,10 @@
     public String[] getDnsForwarders() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            return NativeDaemonEvent.filterMessageList(
-                    mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            final List<String> result = mNetdService.tetherDnsList();
+            return result.toArray(EMPTY_STRING_ARRAY);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1560,10 +1546,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 +1581,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 +1615,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 +1635,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 +1648,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 +1671,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 +1691,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/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 0955bed..0f9fe83 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1644,6 +1644,7 @@
         intent.putExtras(data);
         // Pass the subscription along with the intent.
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
         intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
@@ -1698,6 +1699,7 @@
         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED);
             intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+            intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
         }
         // If the phoneId is invalid, the broadcast is for overall call state.
         if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 59093c1..289143c 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -87,7 +87,7 @@
         "media.metrics", // system/bin/mediametrics
         "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
         "com.android.bluetooth",  // Bluetooth service
-        "statsd",  // Stats daemon
+        "/system/bin/statsd",  // Stats daemon
     };
 
     public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d0a865..6c98711 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -717,8 +717,6 @@
     // Whether we should use SCHED_FIFO for UI and RenderThreads.
     private boolean mUseFifoUiScheduling = false;
 
-    private static final String SYSUI_COMPONENT_NAME = "com.android.systemui/.SystemUIService";
-
     BroadcastQueue mFgBroadcastQueue;
     BroadcastQueue mBgBroadcastQueue;
     // Convenient for easy iteration over the queues. Foreground is first
@@ -810,7 +808,7 @@
                 boolean asProto) {
             if (asProto) return;
             doDump(fd, pw, new String[]{"activities"}, asProto);
-            doDump(fd, pw, new String[]{"service", SYSUI_COMPONENT_NAME}, asProto);
+            doDump(fd, pw, new String[]{"service", "all-platform-critical"}, asProto);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0b9832d..6fa17d8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -627,6 +627,13 @@
     // If absolute volume is supported in AVRCP device
     private boolean mAvrcpAbsVolSupported = false;
 
+    // Pre-scale for Bluetooth Absolute Volume
+    private float[] mPrescaleAbsoluteVolume = new float[] {
+        0.5f,    // Pre-scale for index 1
+        0.7f,    // Pre-scale for index 2
+        0.85f,   // Pre-scale for index 3
+    };
+
     private static Long mLastDeviceConnectMsgTime = new Long(0);
 
     private NotificationManager mNm;
@@ -878,6 +885,23 @@
         mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
 
         mRecordMonitor.initMonitor();
+
+        final float[] preScale = new float[3];
+        preScale[0] = mContext.getResources().getFraction(
+                com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
+                1, 1);
+        preScale[1] = mContext.getResources().getFraction(
+                com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
+                1, 1);
+        preScale[2] = mContext.getResources().getFraction(
+                com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
+                1, 1);
+        for (int i = 0; i < preScale.length; i++) {
+            if (0.0f <= preScale[i] && preScale[i] <= 1.0f) {
+                mPrescaleAbsoluteVolume[i] = preScale[i];
+            }
+        }
+
     }
 
     public void systemReady() {
@@ -4879,18 +4903,12 @@
             if (index == 0) {
                 // 0% for volume 0
                 index = 0;
-            } else if (index == 1) {
-                // 50% for volume 1
-                index = (int)(mIndexMax * 0.5) /10;
-            } else if (index == 2) {
-                // 70% for volume 2
-                index = (int)(mIndexMax * 0.70) /10;
-            } else if (index == 3) {
-                // 85% for volume 3
-                index = (int)(mIndexMax * 0.85) /10;
+            } else if (index > 0 && index <= 3) {
+                // Pre-scale for volume steps 1 2 and 3
+                index = (int) (mIndexMax * mPrescaleAbsoluteVolume[index - 1]) / 10;
             } else {
                 // otherwise, full gain
-                index = (mIndexMax + 5)/10;
+                index = (mIndexMax + 5) / 10;
             }
             return index;
         }
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index dc65e1e..b7bbd42 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -142,6 +142,35 @@
     }
 
     /**
+     * Read the global proxy settings and cache them in memory.
+     */
+    public void loadGlobalProxy() {
+        ContentResolver res = mContext.getContentResolver();
+        String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST);
+        int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
+        String exclList = Settings.Global.getString(res,
+                Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+        String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
+        if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
+            ProxyInfo proxyProperties;
+            if (!TextUtils.isEmpty(pacFileUrl)) {
+                proxyProperties = new ProxyInfo(pacFileUrl);
+            } else {
+                proxyProperties = new ProxyInfo(host, port, exclList);
+            }
+            if (!proxyProperties.isValid()) {
+                if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
+                return;
+            }
+
+            synchronized (mProxyLock) {
+                mGlobalProxy = proxyProperties;
+            }
+        }
+        // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ?
+    }
+
+    /**
      * Sends the system broadcast informing apps about a new proxy configuration.
      *
      * Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
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/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2a80f0e..48082b6 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -163,8 +163,8 @@
     // TODO: create separate trackers for each unique VPN to support
     // automated reconnection
 
-    private Context mContext;
-    private NetworkInfo mNetworkInfo;
+    private final Context mContext;
+    private final NetworkInfo mNetworkInfo;
     private String mPackage;
     private int mOwnerUID;
     private String mInterface;
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/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 2b1d919..1e6bb04 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -320,9 +320,8 @@
     }
 
     private static boolean getEnableLegacyDhcpServer(Context ctx) {
-        // TODO: make the default false (0) and update javadoc in Settings.java
         final ContentResolver cr = ctx.getContentResolver();
-        final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+        final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
         return intVal != 0;
     }
 
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/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 451acf4..7745e3c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1879,30 +1879,36 @@
         // Read partner-provided list of excluded input devices
         XmlPullParser parser = null;
         // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
-        File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
-        FileReader confreader = null;
-        try {
-            confreader = new FileReader(confFile);
-            parser = Xml.newPullParser();
-            parser.setInput(confreader);
-            XmlUtils.beginDocument(parser, "devices");
+        final File[] baseDirs = {
+            Environment.getRootDirectory(),
+            Environment.getVendorDirectory()
+        };
+        for (File baseDir: baseDirs) {
+            File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
+            FileReader confreader = null;
+            try {
+                confreader = new FileReader(confFile);
+                parser = Xml.newPullParser();
+                parser.setInput(confreader);
+                XmlUtils.beginDocument(parser, "devices");
 
-            while (true) {
-                XmlUtils.nextElement(parser);
-                if (!"device".equals(parser.getName())) {
-                    break;
+                while (true) {
+                    XmlUtils.nextElement(parser);
+                    if (!"device".equals(parser.getName())) {
+                        break;
+                    }
+                    String name = parser.getAttributeValue(null, "name");
+                    if (name != null) {
+                        names.add(name);
+                    }
                 }
-                String name = parser.getAttributeValue(null, "name");
-                if (name != null) {
-                    names.add(name);
-                }
+            } catch (FileNotFoundException e) {
+                // It's ok if the file does not exist.
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
+            } finally {
+                try { if (confreader != null) confreader.close(); } catch (IOException e) { }
             }
-        } catch (FileNotFoundException e) {
-            // It's ok if the file does not exist.
-        } catch (Exception e) {
-            Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
-        } finally {
-            try { if (confreader != null) confreader.close(); } catch (IOException e) { }
         }
 
         return names.toArray(new String[names.size()]);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 60e9eaa..3f03169 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -159,8 +159,10 @@
     static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
     static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
 
+    // Perform polling and persist all (FLAG_PERSIST_ALL).
     private static final int MSG_PERFORM_POLL = 1;
-    private static final int MSG_REGISTER_GLOBAL_ALERT = 2;
+    // Perform polling, persist network, and register the global alert again.
+    private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2;
 
     /** Flags to control detail level of poll event. */
     private static final int FLAG_PERSIST_NETWORK = 0x1;
@@ -168,6 +170,14 @@
     private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
     private static final int FLAG_PERSIST_FORCE = 0x100;
 
+    /**
+     * When global alert quota is high, wait for this delay before processing each polling,
+     * and do not schedule further polls once there is already one queued.
+     * This avoids firing the global alert too often on devices with high transfer speeds and
+     * high quota.
+     */
+    private static final int PERFORM_POLL_DELAY_MS = 1000;
+
     private static final String TAG_NETSTATS_ERROR = "netstats_error";
 
     private final Context mContext;
@@ -920,7 +930,7 @@
         }
 
         // Create baseline stats
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL, FLAG_PERSIST_ALL));
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL));
 
         return normalizedRequest;
    }
@@ -1055,13 +1065,12 @@
             mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
             if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
-                // kick off background poll to collect network stats; UID stats
-                // are handled during normal polling interval.
-                final int flags = FLAG_PERSIST_NETWORK;
-                mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
-
-                // re-arm global alert for next update
-                mHandler.obtainMessage(MSG_REGISTER_GLOBAL_ALERT).sendToTarget();
+                // kick off background poll to collect network stats unless there is already
+                // such a call pending; UID stats are handled during normal polling interval.
+                if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
+                    mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
+                            PERFORM_POLL_DELAY_MS);
+                }
             }
         }
     };
@@ -1673,11 +1682,11 @@
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_PERFORM_POLL: {
-                    final int flags = msg.arg1;
-                    mService.performPoll(flags);
+                    mService.performPoll(FLAG_PERSIST_ALL);
                     return true;
                 }
-                case MSG_REGISTER_GLOBAL_ALERT: {
+                case MSG_PERFORM_POLL_REGISTER_ALERT: {
+                    mService.performPoll(FLAG_PERSIST_NETWORK);
                     mService.registerGlobalAlert();
                     return true;
                 }
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/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 271d205..fae4db9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17529,7 +17529,8 @@
         // Prepare the application profiles for the new code paths.
         // This needs to be done before invoking dexopt so that any install-time profile
         // can be used for optimizations.
-        mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));
+        mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()),
+                /* updateReferenceProfileContent= */ true);
 
         // Check whether we need to dexopt the app.
         //
@@ -22606,8 +22607,18 @@
         //
         // We also have to cover non system users because we do not call the usual install package
         // methods for them.
+        //
+        // NOTE: in order to speed up first boot time we only create the current profile and do not
+        // update the content of the reference profile. A system image should already be configured
+        // with the right profile keys and the profiles for the speed-profile prebuilds should
+        // already be copied. That's done in #performDexOptUpgrade.
+        //
+        // TODO(calin, mathieuc): We should use .dm files for prebuilds profiles instead of
+        // manually copying them in #performDexOptUpgrade. When we do that we should have a more
+        // granular check here and only update the existing profiles.
         if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
-            mArtManagerService.prepareAppProfiles(pkg, userId);
+            mArtManagerService.prepareAppProfiles(pkg, userId,
+                /* updateReferenceProfileContent= */ false);
         }
 
         if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 0ba7822..1fb51b7 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm.dex;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -57,8 +59,6 @@
 import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
-import libcore.util.NonNull;
-import libcore.util.Nullable;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -389,7 +389,8 @@
      *   - create the current primary profile to save time at app startup time.
      *   - copy the profiles from the associated dex metadata file to the reference profile.
      */
-    public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) {
+    public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user,
+            boolean updateReferenceProfileContent) {
         final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
         if (user < 0) {
             Slog.wtf(TAG, "Invalid user id: " + user);
@@ -404,8 +405,14 @@
             for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {
                 String codePath = codePathsProfileNames.keyAt(i);
                 String profileName = codePathsProfileNames.valueAt(i);
-                File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
-                String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
+                String dexMetadataPath = null;
+                // Passing the dex metadata file to the prepare method will update the reference
+                // profile content. As such, we look for the dex metadata file only if we need to
+                // perform an update.
+                if (updateReferenceProfileContent) {
+                    File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
+                    dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
+                }
                 synchronized (mInstaller) {
                     boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
                             profileName, codePath, dexMetadataPath);
@@ -423,9 +430,10 @@
     /**
      * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
      */
-    public void prepareAppProfiles(PackageParser.Package pkg, int[] user) {
+    public void prepareAppProfiles(PackageParser.Package pkg, int[] user,
+            boolean updateReferenceProfileContent) {
         for (int i = 0; i < user.length; i++) {
-            prepareAppProfiles(pkg, user[i]);
+            prepareAppProfiles(pkg, user[i], updateReferenceProfileContent);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index e3e1590..602ce3b 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -93,7 +93,7 @@
     @GuardedBy("mPackageUseInfoMap")
     private final Map<String, PackageUseInfo> mPackageUseInfoMap;
 
-    public PackageDexUsage() {
+    /* package */ PackageDexUsage() {
         super("package-dex-usage.list", "PackageDexUsage_DiskWriter", /*lock*/ false);
         mPackageUseInfoMap = new HashMap<>();
     }
@@ -116,7 +116,7 @@
      * @return true if the dex load constitutes new information, or false if this information
      *         has been seen before.
      */
-    public boolean record(String owningPackageName, String dexPath, int ownerUserId,
+    /* package */ boolean record(String owningPackageName, String dexPath, int ownerUserId,
             String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit,
             String loadingPackageName, String classLoaderContext) {
         if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
@@ -193,7 +193,7 @@
      * Convenience method for sync reads which does not force the user to pass a useless
      * (Void) null.
      */
-    public void read() {
+    /* package */ void read() {
       read((Void) null);
     }
 
@@ -558,7 +558,7 @@
      * Remove the usage data associated with package {@code packageName}.
      * @return true if the package usage was found and removed successfully.
      */
-    public boolean removePackage(String packageName) {
+    /* package */ boolean removePackage(String packageName) {
         synchronized (mPackageUseInfoMap) {
             return mPackageUseInfoMap.remove(packageName) != null;
         }
@@ -653,11 +653,12 @@
         return packages;
     }
 
-    public void clear() {
+    /* package */ void clear() {
         synchronized (mPackageUseInfoMap) {
             mPackageUseInfoMap.clear();
         }
     }
+
     // Creates a deep copy of the class' mPackageUseInfoMap.
     private Map<String, PackageUseInfo> clonePackageUseInfoMap() {
         Map<String, PackageUseInfo> clone = new HashMap<>();
@@ -679,7 +680,7 @@
         throw new IllegalArgumentException("Unknown bool encoding: " + bool);
     }
 
-    public String dump() {
+    /* package */ String dump() {
         StringWriter sw = new StringWriter();
         write(sw);
         return sw.toString();
diff --git a/services/core/java/com/android/server/pm/dex/TEST_MAPPING b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
new file mode 100644
index 0000000..ad52559
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+  "presubmit": [
+    {
+      "name": "DexLoggerTests"
+    },
+    {
+      "name": "DexManagerTests"
+    },
+    {
+      "name": "DexoptOptionsTests"
+    },
+    {
+      "name": "DexoptUtilsTest"
+    },
+    {
+      "name": "PackageDexUsageTests"
+    },
+    {
+      "name": "DexLoggerIntegrationTests"
+    }
+  ]
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 89efe12..306f73a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -98,7 +98,6 @@
         "libsensorservicehidl",
         "libgui",
         "libusbhost",
-        "libsuspend",
         "libtinyalsa",
         "libEGL",
         "libGLESv2",
@@ -130,6 +129,7 @@
         "android.hardware.vr@1.0",
         "android.frameworks.schedulerservice@1.0",
         "android.frameworks.sensorservice@1.0",
+        "android.system.suspend@1.0",
     ],
 
     static_libs: [
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 02ad6c7..0ff60e4 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -30,6 +30,8 @@
 
 #include <android/hardware/power/1.0/IPower.h>
 #include <android/hardware/power/1.1/IPower.h>
+#include <android/system/suspend/1.0/ISystemSuspend.h>
+#include <android/system/suspend/1.0/ISystemSuspendCallback.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <jni.h>
 
@@ -39,7 +41,6 @@
 #include <log/log.h>
 #include <utils/misc.h>
 #include <utils/Log.h>
-#include <suspend/autosuspend.h>
 
 using android::hardware::Return;
 using android::hardware::Void;
@@ -49,6 +50,8 @@
 using android::hardware::power::V1_1::PowerStateSubsystem;
 using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
 using android::hardware::hidl_vec;
+using android::system::suspend::V1_0::ISystemSuspend;
+using android::system::suspend::V1_0::ISystemSuspendCallback;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
@@ -63,6 +66,7 @@
 extern sp<IPowerV1_0> getPowerHalV1_0();
 extern sp<IPowerV1_1> getPowerHalV1_1();
 extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
+extern sp<ISystemSuspend> getSuspendHal();
 
 // Java methods used in getLowPowerStats
 static jmethodID jgetAndUpdatePlatformState = NULL;
@@ -70,16 +74,19 @@
 static jmethodID jputVoter = NULL;
 static jmethodID jputState = NULL;
 
-static void wakeup_callback(bool success)
-{
-    ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
-    int ret = sem_post(&wakeup_sem);
-    if (ret < 0) {
-        char buf[80];
-        strerror_r(errno, buf, sizeof(buf));
-        ALOGE("Error posting wakeup sem: %s\n", buf);
+class WakeupCallback : public ISystemSuspendCallback {
+public:
+    Return<void> notifyWakeup(bool success) override {
+        ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
+        int ret = sem_post(&wakeup_sem);
+        if (ret < 0) {
+            char buf[80];
+            strerror_r(errno, buf, sizeof(buf));
+            ALOGE("Error posting wakeup sem: %s\n", buf);
+        }
+        return Void();
     }
-}
+};
 
 static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
 {
@@ -101,11 +108,14 @@
             return -1;
         }
         ALOGV("Registering callback...");
-        autosuspend_set_wakeup_callback(&wakeup_callback);
+        sp<ISystemSuspend> suspendHal = getSuspendHal();
+        suspendHal->registerCallback(new WakeupCallback());
     }
 
     // Wait for wakeup.
     ALOGV("Waiting for wakeup...");
+    // TODO(b/116747600): device can suspend and wakeup after sem_wait() finishes and before wakeup
+    // reason is recorded, i.e. BatteryStats might occasionally miss wakeup events.
     int ret = sem_wait(&wakeup_sem);
     if (ret < 0) {
         char buf[80];
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index b2d35d4..0c9b5f4 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -19,6 +19,7 @@
 //#define LOG_NDEBUG 0
 
 #include <android/hardware/power/1.1/IPower.h>
+#include <android/system/suspend/1.0/ISystemSuspend.h>
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
 
@@ -35,7 +36,7 @@
 #include <utils/Log.h>
 #include <hardware/power.h>
 #include <hardware_legacy/power.h>
-#include <suspend/autosuspend.h>
+#include <hidl/ServiceManagement.h>
 
 #include "com_android_server_power_PowerManagerService.h"
 
@@ -44,6 +45,9 @@
 using android::hardware::power::V1_0::PowerHint;
 using android::hardware::power::V1_0::Feature;
 using android::String8;
+using android::system::suspend::V1_0::ISystemSuspend;
+using android::system::suspend::V1_0::IWakeLock;
+using android::system::suspend::V1_0::WakeLockType;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
@@ -171,6 +175,46 @@
     }
 }
 
+static sp<ISystemSuspend> gSuspendHal = nullptr;
+static sp<IWakeLock> gSuspendBlocker = nullptr;
+static std::mutex gSuspendMutex;
+
+// Assume SystemSuspend HAL is always alive.
+// TODO: Force device to restart if SystemSuspend HAL dies.
+sp<ISystemSuspend> getSuspendHal() {
+    static std::once_flag suspendHalFlag;
+    std::call_once(suspendHalFlag, [](){
+        ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default");
+        gSuspendHal = ISystemSuspend::getService();
+        assert(gSuspendHal != nullptr);
+    });
+    return gSuspendHal;
+}
+
+void enableAutoSuspend() {
+    static bool enabled = false;
+
+    std::lock_guard<std::mutex> lock(gSuspendMutex);
+    if (!enabled) {
+        sp<ISystemSuspend> suspendHal = getSuspendHal();
+        suspendHal->enableAutosuspend();
+        enabled = true;
+    }
+    if (gSuspendBlocker) {
+        gSuspendBlocker->release();
+        gSuspendBlocker.clear();
+    }
+}
+
+void disableAutoSuspend() {
+    std::lock_guard<std::mutex> lock(gSuspendMutex);
+    if (!gSuspendBlocker) {
+        sp<ISystemSuspend> suspendHal = getSuspendHal();
+        gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL,
+                "PowerManager.SuspendLockout");
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 static void nativeInit(JNIEnv* env, jobject obj) {
@@ -207,13 +251,13 @@
 static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
     if (enable) {
         android::base::Timer t;
-        autosuspend_enable();
+        enableAutoSuspend();
         if (t.duration() > 100ms) {
             ALOGD("Excessive delay in autosuspend_enable() while turning screen off");
         }
     } else {
         android::base::Timer t;
-        autosuspend_disable();
+        disableAutoSuspend();
         if (t.duration() > 100ms) {
             ALOGD("Excessive delay in autosuspend_disable() while turning screen on");
         }
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 93c4829..6c2a894 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -390,7 +390,11 @@
                 [&result, &sidebandStream](Result res, const native_handle_t* handle) {
                     result = res;
                     if (res == Result::OK) {
-                        sidebandStream = handle;
+                        if (handle) {
+                            sidebandStream = native_handle_clone(handle);
+                        } else {
+                            result = Result::UNKNOWN;
+                        }
                     }
                 });
         if (result != Result::OK) {
@@ -398,7 +402,7 @@
                     result);
             return UNKNOWN_ERROR;
         }
-        connection.mSourceHandle = NativeHandle::create((native_handle_t*)sidebandStream, false);
+        connection.mSourceHandle = NativeHandle::create((native_handle_t*)sidebandStream, true);
     }
     connection.mSurface = surface;
     if (connection.mSurface != nullptr) {
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 0505204..47790ce 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -3,7 +3,6 @@
     srcs: ["java/**/*.java"],
 
     libs: [
-        "conscrypt",
         "services.core",
     ],
 }
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 77a3e21..6ba7d94 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -1,5 +1,8 @@
 package android.net.dhcp;
 
+import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
+import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
+
 import android.annotation.Nullable;
 import android.net.DhcpResults;
 import android.net.LinkAddress;
@@ -381,6 +384,26 @@
     }
 
     /**
+     * Returns whether a parameter is included in the parameter request list option of this packet.
+     *
+     * <p>If there is no parameter request list option in the packet, false is returned.
+     *
+     * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}.
+     */
+    public boolean hasRequestedParam(byte paramId) {
+        if (mRequestedParams == null) {
+            return false;
+        }
+
+        for (byte reqParam : mRequestedParams) {
+            if (reqParam == paramId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Creates a new L3 packet (including IP header) containing the
      * DHCP udp packet.  This method relies upon the delegated method
      * finishPacket() to insert the per-packet contents.
@@ -696,7 +719,11 @@
         addTlv(buf, DHCP_ROUTER, mGateways);
         addTlv(buf, DHCP_DNS_SERVER, mDnsServers);
         addTlv(buf, DHCP_DOMAIN_NAME, mDomainName);
+        addTlv(buf, DHCP_HOST_NAME, mHostName);
         addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo);
+        if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
+            addTlv(buf, DHCP_MTU, mMtu);
+        }
     }
 
     /**
@@ -1259,7 +1286,8 @@
         boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
         Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
         Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
-        Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
+        Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+        short mtu) {
         DhcpPacket pkt = new DhcpOfferPacket(
                 transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
                 INADDR_ANY /* clientIp */, yourIp, mac);
@@ -1267,9 +1295,11 @@
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
         pkt.mDomainName = domainName;
+        pkt.mHostName = hostname;
         pkt.mServerIdentifier = dhcpServerIdentifier;
         pkt.mSubnetMask = netMask;
         pkt.mBroadcastAddress = bcAddr;
+        pkt.mMtu = mtu;
         if (metered) {
             pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
         }
@@ -1283,7 +1313,8 @@
         boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
         Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
         Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
-        Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
+        Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+        short mtu) {
         DhcpPacket pkt = new DhcpAckPacket(
                 transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
                 mac);
@@ -1291,9 +1322,11 @@
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
         pkt.mDomainName = domainName;
+        pkt.mHostName = hostname;
         pkt.mSubnetMask = netMask;
         pkt.mServerIdentifier = dhcpServerIdentifier;
         pkt.mBroadcastAddress = bcAddr;
+        pkt.mMtu = mtu;
         if (metered) {
             pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
         }
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java
index 2b3d577..cee6fa9 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/services/net/java/android/net/dhcp/DhcpServer.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
 import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER;
 import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
+import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
 import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
 import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
@@ -46,6 +47,7 @@
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
@@ -350,6 +352,19 @@
         return isEmpty(request.mClientIp) && (request.mBroadcast || isEmpty(lease.getNetAddr()));
     }
 
+    /**
+     * Get the hostname from a lease if non-empty and requested in the incoming request.
+     * @param request The incoming request.
+     * @return The hostname, or null if not requested or empty.
+     */
+    @Nullable
+    private static String getHostnameIfRequested(@NonNull DhcpPacket request,
+            @NonNull DhcpLease lease) {
+        return request.hasRequestedParam(DHCP_HOST_NAME) && !TextUtils.isEmpty(lease.getHostname())
+                ? lease.getHostname()
+                : null;
+    }
+
     private boolean transmitOffer(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
             @NonNull MacAddress clientMac) {
         final boolean broadcastFlag = getBroadcastFlag(request, lease);
@@ -358,12 +373,14 @@
                 getPrefixMaskAsInet4Address(mServingParams.serverAddr.getPrefixLength());
         final Inet4Address broadcastAddr = getBroadcastAddress(
                 mServingParams.getServerInet4Addr(), mServingParams.serverAddr.getPrefixLength());
+        final String hostname = getHostnameIfRequested(request, lease);
         final ByteBuffer offerPacket = DhcpPacket.buildOfferPacket(
                 ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(),
                 request.mRelayIp, lease.getNetAddr(), request.mClientMac, timeout, prefixMask,
                 broadcastAddr, new ArrayList<>(mServingParams.defaultRouters),
                 new ArrayList<>(mServingParams.dnsServers),
-                mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
+                mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
+                mServingParams.metered, (short) mServingParams.linkMtu);
 
         return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag);
     }
@@ -374,13 +391,15 @@
         // transmitOffer above
         final boolean broadcastFlag = getBroadcastFlag(request, lease);
         final int timeout = getLeaseTimeout(lease);
+        final String hostname = getHostnameIfRequested(request, lease);
         final ByteBuffer ackPacket = DhcpPacket.buildAckPacket(ENCAP_BOOTP, request.mTransId,
                 broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp,
                 lease.getNetAddr(), request.mClientIp, request.mClientMac, timeout,
                 mServingParams.getPrefixMaskAsAddress(), mServingParams.getBroadcastAddress(),
                 new ArrayList<>(mServingParams.defaultRouters),
                 new ArrayList<>(mServingParams.dnsServers),
-                mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
+                mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
+                mServingParams.metered, (short) mServingParams.linkMtu);
 
         return transmitOfferOrAckPacket(ackPacket, request, lease, clientMac, broadcastFlag);
     }
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/net/java/android/net/netlink/InetDiagMessage.java b/services/net/java/android/net/netlink/InetDiagMessage.java
new file mode 100644
index 0000000..af9e601
--- /dev/null
+++ b/services/net/java/android/net/netlink/InetDiagMessage.java
@@ -0,0 +1,187 @@
+/*
+ * 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.netlink;
+
+import static android.os.Process.INVALID_UID;
+import static android.net.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
+import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.NETLINK_INET_DIAG;
+
+import android.os.Build;
+import android.os.Process;
+import android.system.ErrnoException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.net.DatagramSocket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * A NetlinkMessage subclass for netlink inet_diag messages.
+ *
+ * see also: &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
+ *
+ * @hide
+ */
+public class InetDiagMessage extends NetlinkMessage {
+    public static final String TAG = "InetDiagMessage";
+    private static final int TIMEOUT_MS = 500;
+
+    public static byte[] InetDiagReqV2(int protocol, InetSocketAddress local,
+                                       InetSocketAddress remote, int family, short flags) {
+        final byte[] bytes = new byte[StructNlMsgHdr.STRUCT_SIZE + StructInetDiagReqV2.STRUCT_SIZE];
+        final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+        byteBuffer.order(ByteOrder.nativeOrder());
+
+        final StructNlMsgHdr nlMsgHdr = new StructNlMsgHdr();
+        nlMsgHdr.nlmsg_len = bytes.length;
+        nlMsgHdr.nlmsg_type = SOCK_DIAG_BY_FAMILY;
+        nlMsgHdr.nlmsg_flags = flags;
+        nlMsgHdr.pack(byteBuffer);
+
+        final StructInetDiagReqV2 inetDiagReqV2 = new StructInetDiagReqV2(protocol, local, remote,
+                family);
+        inetDiagReqV2.pack(byteBuffer);
+        return bytes;
+    }
+
+    public StructInetDiagMsg mStructInetDiagMsg;
+
+    private InetDiagMessage(StructNlMsgHdr header) {
+        super(header);
+        mStructInetDiagMsg = new StructInetDiagMsg();
+    }
+
+    public static InetDiagMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
+        final InetDiagMessage msg = new InetDiagMessage(header);
+        msg.mStructInetDiagMsg = StructInetDiagMsg.parse(byteBuffer);
+        return msg;
+    }
+
+    private static int lookupUidByFamily(int protocol, InetSocketAddress local,
+                                         InetSocketAddress remote, int family, short flags,
+                                         FileDescriptor fd)
+            throws ErrnoException, InterruptedIOException {
+        byte[] msg = InetDiagReqV2(protocol, local, remote, family, flags);
+        NetlinkSocket.sendMessage(fd, msg, 0, msg.length, TIMEOUT_MS);
+        ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT_MS);
+
+        final NetlinkMessage nlMsg = NetlinkMessage.parse(response);
+        final StructNlMsgHdr hdr = nlMsg.getHeader();
+        if (hdr.nlmsg_type == NetlinkConstants.NLMSG_DONE) {
+            return INVALID_UID;
+        }
+        if (nlMsg instanceof InetDiagMessage) {
+            return ((InetDiagMessage) nlMsg).mStructInetDiagMsg.idiag_uid;
+        }
+        return INVALID_UID;
+    }
+
+    private static final int FAMILY[] = {AF_INET6, AF_INET};
+
+    private static int lookupUid(int protocol, InetSocketAddress local,
+                                 InetSocketAddress remote, FileDescriptor fd)
+            throws ErrnoException, InterruptedIOException {
+        int uid;
+
+        for (int family : FAMILY) {
+            /**
+             * For exact match lookup, swap local and remote for UDP lookups due to kernel
+             * bug which will not be fixed. See aosp/755889 and
+             * https://www.mail-archive.com/netdev@vger.kernel.org/msg248638.html
+             */
+            if (protocol == IPPROTO_UDP) {
+                uid = lookupUidByFamily(protocol, remote, local, family, NLM_F_REQUEST, fd);
+            } else {
+                uid = lookupUidByFamily(protocol, local, remote, family, NLM_F_REQUEST, fd);
+            }
+            if (uid != INVALID_UID) {
+                return uid;
+            }
+        }
+
+        /**
+         * For UDP it's possible for a socket to send packets to arbitrary destinations, even if the
+         * socket is not connected (and even if the socket is connected to a different destination).
+         * If we want this API to work for such packets, then on miss we need to do a second lookup
+         * with only the local address and port filled in.
+         * Always use flags == NLM_F_REQUEST | NLM_F_DUMP for wildcard.
+         */
+        if (protocol == IPPROTO_UDP) {
+            try {
+                InetSocketAddress wildcard = new InetSocketAddress(
+                        Inet6Address.getByName("::"), 0);
+                uid = lookupUidByFamily(protocol, local, wildcard, AF_INET6,
+                        (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
+                if (uid != INVALID_UID) {
+                    return uid;
+                }
+                wildcard = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), 0);
+                uid = lookupUidByFamily(protocol, local, wildcard, AF_INET,
+                        (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
+                if (uid != INVALID_UID) {
+                    return uid;
+                }
+            } catch (UnknownHostException e) {
+                Log.e(TAG, e.toString());
+            }
+        }
+        return INVALID_UID;
+    }
+
+    /**
+     * Use an inet_diag socket to look up the UID associated with the input local and remote
+     * address/port and protocol of a connection.
+     */
+    public static int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+                                            InetSocketAddress remote) {
+        try {
+            final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_INET_DIAG);
+            NetlinkSocket.connectToKernel(fd);
+
+            return lookupUid(protocol, local, remote, fd);
+
+        } catch (ErrnoException | SocketException | IllegalArgumentException
+                | InterruptedIOException e) {
+            Log.e(TAG, e.toString());
+        }
+        return INVALID_UID;
+    }
+
+    @Override
+    public String toString() {
+        return "InetDiagMessage{ "
+                + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, "
+                + "inet_diag_msg{"
+                + (mStructInetDiagMsg == null ? "" : mStructInetDiagMsg.toString()) + "} "
+                + "}";
+    }
+}
diff --git a/services/net/java/android/net/netlink/NetlinkConstants.java b/services/net/java/android/net/netlink/NetlinkConstants.java
index e331701..fc1551c 100644
--- a/services/net/java/android/net/netlink/NetlinkConstants.java
+++ b/services/net/java/android/net/netlink/NetlinkConstants.java
@@ -54,6 +54,12 @@
         return String.valueOf(family);
     }
 
+    public static String stringForProtocol(int protocol) {
+        if (protocol == OsConstants.IPPROTO_TCP) { return "IPPROTO_TCP"; }
+        if (protocol == OsConstants.IPPROTO_UDP) { return "IPPROTO_UDP"; }
+        return String.valueOf(protocol);
+    }
+
     public static String hexify(byte[] bytes) {
         if (bytes == null) { return "(null)"; }
         return HexDump.toHexString(bytes);
@@ -90,6 +96,9 @@
     public static final short RTM_GETRULE        = 34;
     public static final short RTM_NEWNDUSEROPT   = 68;
 
+    /* see &lt;linux_src&gt;/include/uapi/linux/sock_diag.h */
+    public static final short SOCK_DIAG_BY_FAMILY = 20;
+
     public static String stringForNlMsgType(short nlm_type) {
         switch (nlm_type) {
             case NLMSG_NOOP: return "NLMSG_NOOP";
diff --git a/services/net/java/android/net/netlink/NetlinkMessage.java b/services/net/java/android/net/netlink/NetlinkMessage.java
index 3bf75ca..a325db8 100644
--- a/services/net/java/android/net/netlink/NetlinkMessage.java
+++ b/services/net/java/android/net/netlink/NetlinkMessage.java
@@ -69,6 +69,8 @@
             case NetlinkConstants.RTM_DELNEIGH:
             case NetlinkConstants.RTM_GETNEIGH:
                 return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer);
+            case NetlinkConstants.SOCK_DIAG_BY_FAMILY:
+                return (NetlinkMessage) InetDiagMessage.parse(nlmsghdr, byteBuffer);
             default:
                 if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) {
                     // Netlink control message.  Just parse the header for now,
diff --git a/services/net/java/android/net/netlink/StructInetDiagMsg.java b/services/net/java/android/net/netlink/StructInetDiagMsg.java
new file mode 100644
index 0000000..da824ad
--- /dev/null
+++ b/services/net/java/android/net/netlink/StructInetDiagMsg.java
@@ -0,0 +1,67 @@
+/*
+ * 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.netlink;
+
+import static java.nio.ByteOrder.BIG_ENDIAN;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import android.util.Log;
+
+/**
+ * struct inet_diag_msg
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
+ *
+ * struct inet_diag_msg {
+ *      __u8    idiag_family;
+ *      __u8    idiag_state;
+ *      __u8    idiag_timer;
+ *      __u8    idiag_retrans;
+ *      struct  inet_diag_sockid id;
+ *      __u32   idiag_expires;
+ *      __u32   idiag_rqueue;
+ *      __u32   idiag_wqueue;
+ *      __u32   idiag_uid;
+ *      __u32   idiag_inode;
+ * };
+ *
+ * @hide
+ */
+public class StructInetDiagMsg {
+    public static final int STRUCT_SIZE = 4 + StructInetDiagSockId.STRUCT_SIZE + 20;
+    private static final int IDIAG_UID_OFFSET = StructNlMsgHdr.STRUCT_SIZE + 4 +
+            StructInetDiagSockId.STRUCT_SIZE + 12;
+    public int idiag_uid;
+
+    public static StructInetDiagMsg parse(ByteBuffer byteBuffer) {
+        StructInetDiagMsg struct = new StructInetDiagMsg();
+        struct.idiag_uid = byteBuffer.getInt(IDIAG_UID_OFFSET);
+        return struct;
+    }
+
+    @Override
+    public String toString() {
+        return "StructInetDiagMsg{ "
+                + "idiag_uid{" + idiag_uid + "}, "
+                + "}";
+    }
+}
diff --git a/services/net/java/android/net/netlink/StructInetDiagReqV2.java b/services/net/java/android/net/netlink/StructInetDiagReqV2.java
new file mode 100644
index 0000000..49a9325
--- /dev/null
+++ b/services/net/java/android/net/netlink/StructInetDiagReqV2.java
@@ -0,0 +1,80 @@
+/*
+ * 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.netlink;
+
+import static java.nio.ByteOrder.BIG_ENDIAN;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * struct inet_diag_req_v2
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
+ *
+ *      struct inet_diag_req_v2 {
+ *          __u8    sdiag_family;
+ *          __u8    sdiag_protocol;
+ *          __u8    idiag_ext;
+ *          __u8    pad;
+ *          __u32   idiag_states;
+ *          struct  inet_diag_sockid id;
+ *      };
+ *
+ * @hide
+ */
+public class StructInetDiagReqV2 {
+    public static final int STRUCT_SIZE = 8 + StructInetDiagSockId.STRUCT_SIZE;
+
+    private final byte sdiag_family;
+    private final byte sdiag_protocol;
+    private final StructInetDiagSockId id;
+    private final int INET_DIAG_REQ_V2_ALL_STATES = (int) 0xffffffff;
+
+
+    public StructInetDiagReqV2(int protocol, InetSocketAddress local, InetSocketAddress remote,
+                               int family) {
+        sdiag_family = (byte) family;
+        sdiag_protocol = (byte) protocol;
+        id = new StructInetDiagSockId(local, remote);
+    }
+
+    public void pack(ByteBuffer byteBuffer) {
+        // The ByteOrder must have already been set by the caller.
+        byteBuffer.put((byte) sdiag_family);
+        byteBuffer.put((byte) sdiag_protocol);
+        byteBuffer.put((byte) 0);
+        byteBuffer.put((byte) 0);
+        byteBuffer.putInt(INET_DIAG_REQ_V2_ALL_STATES);
+        id.pack(byteBuffer);
+    }
+
+    @Override
+    public String toString() {
+        final String familyStr = NetlinkConstants.stringForAddressFamily(sdiag_family);
+        final String protocolStr = NetlinkConstants.stringForAddressFamily(sdiag_protocol);
+
+        return "StructInetDiagReqV2{ "
+                + "sdiag_family{" + familyStr + "}, "
+                + "sdiag_protocol{" + protocolStr + "}, "
+                + "idiag_ext{" + 0 + ")}, "
+                + "pad{" + 0 + "}, "
+                + "idiag_states{" + Integer.toHexString(INET_DIAG_REQ_V2_ALL_STATES) + "}, "
+                + id.toString()
+                + "}";
+    }
+}
diff --git a/services/net/java/android/net/netlink/StructInetDiagSockId.java b/services/net/java/android/net/netlink/StructInetDiagSockId.java
new file mode 100644
index 0000000..2e9fa25
--- /dev/null
+++ b/services/net/java/android/net/netlink/StructInetDiagSockId.java
@@ -0,0 +1,86 @@
+/*
+ * 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.netlink;
+
+import static java.nio.ByteOrder.BIG_ENDIAN;
+
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * struct inet_diag_req_v2
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
+ *
+ * struct inet_diag_sockid {
+ *        __be16    idiag_sport;
+ *        __be16    idiag_dport;
+ *        __be32    idiag_src[4];
+ *        __be32    idiag_dst[4];
+ *        __u32     idiag_if;
+ *        __u32     idiag_cookie[2];
+ * #define INET_DIAG_NOCOOKIE (~0U)
+ * };
+ *
+ * @hide
+ */
+public class StructInetDiagSockId {
+    public static final int STRUCT_SIZE = 48;
+
+    private final InetSocketAddress mLocSocketAddress;
+    private final InetSocketAddress mRemSocketAddress;
+    private final byte[] INET_DIAG_NOCOOKIE = new byte[]{
+            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+    private final byte[] IPV4_PADDING = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+    public StructInetDiagSockId(InetSocketAddress loc, InetSocketAddress rem) {
+        mLocSocketAddress = loc;
+        mRemSocketAddress = rem;
+    }
+
+    public void pack(ByteBuffer byteBuffer) {
+        byteBuffer.order(BIG_ENDIAN);
+        byteBuffer.putShort((short) mLocSocketAddress.getPort());
+        byteBuffer.putShort((short) mRemSocketAddress.getPort());
+        byteBuffer.put(mLocSocketAddress.getAddress().getAddress());
+        if (mLocSocketAddress.getAddress() instanceof Inet4Address) {
+            byteBuffer.put(IPV4_PADDING);
+        }
+        byteBuffer.put(mRemSocketAddress.getAddress().getAddress());
+        if (mRemSocketAddress.getAddress() instanceof Inet4Address) {
+            byteBuffer.put(IPV4_PADDING);
+        }
+        byteBuffer.order(ByteOrder.nativeOrder());
+        byteBuffer.putInt(0);
+        byteBuffer.put(INET_DIAG_NOCOOKIE);
+    }
+
+    @Override
+    public String toString() {
+        return "StructInetDiagSockId{ "
+                + "idiag_sport{" + mLocSocketAddress.getPort() + "}, "
+                + "idiag_dport{" + mRemSocketAddress.getPort() + "}, "
+                + "idiag_src{" + mLocSocketAddress.getAddress().getHostAddress() + "}, "
+                + "idiag_dst{" + mRemSocketAddress.getAddress().getHostAddress() + "}, "
+                + "idiag_if{" + 0 + "} "
+                + "idiag_cookie{INET_DIAG_NOCOOKIE}"
+                + "}";
+    }
+}
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index f7bf393..5a73a4e 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -17,6 +17,7 @@
 package android.net.util;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
@@ -92,10 +93,17 @@
     }
 
     /**
-     * Log an error due to an exception, with the exception stacktrace.
+     * Log an error due to an exception, with the exception stacktrace if provided.
+     *
+     * <p>The error and exception message appear in the shared log, but the stacktrace is only
+     * logged in general log output (logcat).
      */
-    public void e(@NonNull String msg, @NonNull Throwable e) {
-        Log.e(mTag, record(Category.ERROR, msg + ": " + e.getMessage()), e);
+    public void e(@NonNull String msg, @Nullable Throwable exception) {
+        if (exception == null) {
+            e(msg);
+            return;
+        }
+        Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
     }
 
     public void i(String msg) {
diff --git a/services/tests/runtests.py b/services/tests/runtests.py
index 7980dc2..f19cc5d 100755
--- a/services/tests/runtests.py
+++ b/services/tests/runtests.py
@@ -22,8 +22,7 @@
                                'android.support.test.runner.AndroidJUnitRunner')
 
 PACKAGE_WHITELIST = (
-    'android.net',
-    'com.android.server.connectivity',
+    "com.android.server",
 )
 
 COLOR_RED = '\033[0;31m'
@@ -37,14 +36,27 @@
                 COLOR_NONE)
     return subprocess.check_call(shell_command, shell=True)
 
-
+# usage:
+#   ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py : run tests in com.android.server
+#   ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py -e package [package name, e.g. com.android.server]
+#   ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py -e class [class name, e.g. com.android.server.MountServiceTests]
+#
+#   The available INSTRUMENTED_PACKAGE_RUNNER may differ in different environments.
+#   In this case, use "adb shell pm list instrumentation" to query available runners
+#   and use the environment variable INSTRUMENTED_PACKAGE_RUNNER to overwrite
+#   the default one, e.g.,
+#   INSTRUMENTED_PACKAGE_RUNNER=com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner \
+#       ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py
+#
 def main():
     build_top = os.environ.get('ANDROID_BUILD_TOP', None)
     out_dir = os.environ.get('OUT', None)
+    runner = os.environ.get('INSTRUMENTED_PACKAGE_RUNNER', None)
     if build_top is None or out_dir is None:
         print 'You need to source and lunch before you can use this script'
         return 1
-
+    if runner is None:
+        runner = INSTRUMENTED_PACKAGE_RUNNER
     print 'Building tests...'
     run('make -j32 -C %s -f build/core/main.mk '
         'MODULES-IN-frameworks-base-services-tests-servicestests' % build_top,
@@ -57,19 +69,19 @@
     apk_path = (
             '%s/data/app/FrameworksServicesTests/FrameworksServicesTests.apk' %
             out_dir)
-    run('adb install -r -g "%s"' % apk_path)
+    run('adb install -t -r -g "%s"' % apk_path)
 
     print 'Running tests...'
     if len(sys.argv) != 1:
         run('adb shell am instrument -w %s "%s"' %
-            (' '.join(sys.argv[1:]), INSTRUMENTED_PACKAGE_RUNNER))
+            (' '.join(sys.argv[1:]), runner))
         return 0
 
     # It would be nice if the activity manager accepted a list of packages, but
     # in lieu of that...
     for package in PACKAGE_WHITELIST:
         run('adb shell am instrument -w -e package %s %s' %
-            (package, INSTRUMENTED_PACKAGE_RUNNER))
+            (package, runner))
 
     return 0
 
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index a85f21c..00ebae6 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 \
@@ -55,6 +57,7 @@
     libbacktrace \
     libbase \
     libbinder \
+    libbinderthreadstate \
     libc++ \
     libcutils \
     liblog \
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/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
index b98bc89..3fa776c 100644
--- a/services/tests/uiservicestests/Android.mk
+++ b/services/tests/uiservicestests/Android.mk
@@ -45,6 +45,7 @@
     libbacktrace \
     libbase \
     libbinder \
+    libbinderthreadstate \
     libc++ \
     libcutils \
     liblog \
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
new file mode 100644
index 0000000..cd28974
--- /dev/null
+++ b/services/tests/wmtests/Android.mk
@@ -0,0 +1,46 @@
+#########################################################################
+# 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 := \
+    frameworks-base-testutils \
+    services.core \
+    android-support-test \
+    mockito-target-minus-junit4 \
+    platform-test-annotations \
+    truth-prebuilt \
+    testables \
+    ub-uiautomator \
+
+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..7e4bd42
--- /dev/null
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?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" />
+
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+    <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
+    <uses-permission android:name="android.permission.REORDER_TASKS" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+    <application android:testOnly="true">
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityA" />
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityB" />
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
+        <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity" />
+    </application>
+
+    <instrumentation
+        android:name="android.support.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..38a62ae
--- /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="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
index f741c70..089dc61 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java
@@ -16,6 +16,16 @@
 
 package com.android.server.am;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.app.ActivityOptions;
 import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
@@ -24,26 +34,16 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.server.am.LaunchParamsController.LaunchParams;
-import org.junit.runner.RunWith;
+
 import org.junit.Before;
 import org.junit.Test;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.doAnswer;
-
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for exercising resizing bounds due to activity options.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:ActivityLaunchParamsModifierTests
+ *  atest WmTests:ActivityLaunchParamsModifierTests
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/wmtests/src/com/android/server/am/ActivityManagerInternalTest.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityManagerInternalTest.java
index bce87dc..f393d14 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -33,17 +33,8 @@
 /**
  * Test class for {@link ActivityManagerInternal}.
  *
- * To run the tests, use
- *
- * runtest -c com.android.server.am.ActivityManagerInternalTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.am.ActivityManagerInternalTest -w \
- *     com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ *  atest WmTests:ActivityManagerInternalTest
  */
 @RunWith(AndroidJUnit4.class)
 public class ActivityManagerInternalTest {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/wmtests/src/com/android/server/am/ActivityManagerServiceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityManagerServiceTest.java
index c70d1e1..c41197d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -28,6 +28,7 @@
 import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.util.DebugUtils.valueToString;
+
 import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
 import static com.android.server.am.ActivityManagerService.DISPATCH_UIDS_CHANGED_UI_MSG;
 import static com.android.server.am.ActivityManagerService.Injector;
@@ -88,17 +89,8 @@
 /**
  * Test class for {@link ActivityManagerService}.
  *
- * To run the tests, use
- *
- * runtest -c com.android.server.am.ActivityManagerServiceTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.am.ActivityManagerServiceTest -w \
- *     com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ *  atest WmTests:ActivityManagerServiceTest
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -830,4 +822,4 @@
             mRestricted = restricted;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/wmtests/src/com/android/server/am/ActivityManagerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityManagerTest.java
index ba25b16..a2f942e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityManagerTest.java
@@ -18,13 +18,18 @@
 
 import android.app.ActivityManager;
 import android.app.IActivityManager;
-import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.test.AndroidTestCase;
 
 import java.util.List;
 
+/**
+ * Tests for {@link ActivityManager}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityManagerTest
+ */
 public class ActivityManagerTest extends AndroidTestCase {
 
     IActivityManager service;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityRecordTests.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityRecordTests.java
index 5ee1c40..616cc0f 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityRecordTests.java
@@ -22,11 +22,7 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
-import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
 import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
@@ -57,17 +53,16 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.util.MutableBoolean;
 
-import org.junit.runner.RunWith;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.invocation.InvocationOnMock;
 
 /**
  * Tests for the {@link ActivityRecord} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.ActivityRecordTests
+ *  atest WmTests:com.android.server.am.ActivityRecordTests
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityStackSupervisorTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 1ce41a6..2209b66 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -62,7 +62,7 @@
  * Tests for the {@link ActivityStackSupervisor} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests
+ *  atest WmTests:ActivityStackSupervisorTests
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityStackTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStackTests.java
index 01425ed..cac264b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStackTests.java
@@ -33,13 +33,9 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 import android.content.pm.ActivityInfo;
 import android.os.UserHandle;
@@ -47,15 +43,15 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import org.junit.runner.RunWith;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link ActivityStack} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.ActivityStackTests
+ *  atest WmTests:ActivityStackTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityStartControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStartControllerTests.java
index 7948e4c..37a5a17 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -19,6 +19,13 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.content.Intent;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
@@ -27,15 +34,8 @@
 import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
 import com.android.server.am.ActivityStarter.Factory;
 
-import org.junit.runner.RunWith;
 import org.junit.Test;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.times;
+import org.junit.runner.RunWith;
 
 import java.util.Random;
 
@@ -43,7 +43,7 @@
  * Tests for the {@link ActivityStartController} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:ActivityStartControllerTests
+ *  atest WmTests:ActivityStartControllerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/am/ActivityStartInterceptorTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStartInterceptorTest.java
index b4b34c5..8f2d284 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -55,7 +55,7 @@
  * Unit tests for {@link ActivityStartInterceptorTest}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.am.ActivityStartInterceptorTest
+ *  bit WmTests:ActivityStartInterceptorTest
  */
 @SmallTest
 public class ActivityStartInterceptorTest {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/am/ActivityStarterTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityStarterTests.java
index 1520859..048df43 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityStarterTests.java
@@ -32,10 +32,26 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+
+import static com.android.server.am.ActivityManagerService.ANIMATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
-import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.WindowLayout;
@@ -50,39 +66,18 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.view.Gravity;
 
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.times;
-
 import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.am.ActivityStarter.Factory;
 import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
 import com.android.server.am.TaskRecord.TaskRecordFactory;
 
-import java.util.ArrayList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link ActivityStarter} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:ActivityStarterTests
+ *  atest WmTests:ActivityStarterTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/am/ActivityTestsBase.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
rename to services/tests/wmtests/src/com/android/server/am/ActivityTestsBase.java
index 1cd111f..d9a346b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/am/ActivityTestsBase.java
@@ -21,21 +21,17 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
+
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
 import android.app.ActivityOptions;
-import com.android.server.wm.DisplayWindowController;
-
-import org.junit.Rule;
-import org.mockito.invocation.InvocationOnMock;
-
 import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.Context;
@@ -52,20 +48,21 @@
 import android.support.test.InstrumentationRegistry;
 import android.testing.DexmakerShareClassLoaderRule;
 
-
 import com.android.internal.app.IVoiceInteractor;
-
 import com.android.server.AttributeCache;
 import com.android.server.wm.AppWindowContainerController;
+import com.android.server.wm.DisplayWindowController;
 import com.android.server.wm.PinnedStackWindowController;
 import com.android.server.wm.StackWindowController;
 import com.android.server.wm.TaskWindowContainerController;
 import com.android.server.wm.WindowManagerService;
 import com.android.server.wm.WindowTestUtils;
+
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.mockito.MockitoAnnotations;
-
+import org.mockito.invocation.InvocationOnMock;
 
 /**
  * A base class to handle common operations in activity related unit tests.
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/wmtests/src/com/android/server/am/AppErrorDialogTest.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
rename to services/tests/wmtests/src/com/android/server/am/AppErrorDialogTest.java
index 3d11c4c..2289dfc 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/AppErrorDialogTest.java
@@ -32,7 +32,10 @@
 import java.io.File;
 
 /**
- * runtest -c com.android.server.am.AppErrorDialogTest frameworks-services
+ * Tests for {@link AppErrorDialog}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:AppErrorDialogTest
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/am/AssistDataRequesterTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
rename to services/tests/wmtests/src/com/android/server/am/AssistDataRequesterTest.java
index ce88d84..285a84f 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -62,7 +62,8 @@
  * Note: Currently, we only support fetching the screenshot for the current application, so the
  * screenshot checks are hardcoded accordingly.
  *
- * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+ * Build/Install/Run:
+ *  atest WmTests:AssistDataRequesterTest
  */
 @MediumTest
 @RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/am/ClientLifecycleManagerTests.java
similarity index 66%
rename from services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java
rename to services/tests/wmtests/src/com/android/server/am/ClientLifecycleManagerTests.java
index ef6d5e8..3098db2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/ClientLifecycleManagerTests.java
@@ -1,3 +1,19 @@
+/*
+ * 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 static org.mockito.Mockito.mock;
@@ -15,6 +31,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/**
+ * Test class for {@link ClientLifecycleManager}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ClientLifecycleManagerTests
+ */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/wmtests/src/com/android/server/am/CoreSettingsObserverTest.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
rename to services/tests/wmtests/src/com/android/server/am/CoreSettingsObserverTest.java
index da30c11..adf3f94 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -37,7 +37,6 @@
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -48,17 +47,8 @@
 /**
  * Test class for {@link CoreSettingsObserver}.
  *
- * To run the tests, use
- *
- * runtest -c com.android.server.am.CoreSettingsObserverTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.am.CoreSettingsObserverTest -w \
- *     com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ * Build/Install/Run:
+ *  atest WmTests:CoreSettingsObserverTest
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java b/services/tests/wmtests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
rename to services/tests/wmtests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
index d9b3e1c..b1e0b51 100644
--- a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
@@ -36,6 +36,9 @@
 
 /**
  * Tests for {@link GlobalSettingsToPropertiesMapper}
+ *
+ * Build/Install/Run:
+ *  atest WmTests:GlobalSettingsToPropertiesMapperTest
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/am/LaunchParamsControllerTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
rename to services/tests/wmtests/src/com/android/server/am/LaunchParamsControllerTests.java
index 161c287..5f2bc4d 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/LaunchParamsControllerTests.java
@@ -16,20 +16,14 @@
 
 package com.android.server.am;
 
-import android.app.ActivityOptions;
-import android.content.pm.ActivityInfo.WindowLayout;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.MediumTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.server.am.LaunchParamsController.LaunchParams;
-import org.junit.runner.RunWith;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
-
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doNothing;
@@ -40,18 +34,24 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
-import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import com.android.server.am.LaunchParamsController.LaunchParams;
+import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for exercising {@link LaunchParamsController}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:LaunchParamsControllerTests
+ *  atest WmTests:LaunchParamsControllerTests
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/am/LockTaskControllerTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
rename to services/tests/wmtests/src/com/android/server/am/LockTaskControllerTest.java
index f46d712..a2e9769 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/LockTaskControllerTest.java
@@ -35,9 +35,20 @@
 import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED;
 import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_PINNED;
 
-import static org.junit.Assert.*;
-import static org.mockito.ArgumentMatchers.*;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
@@ -76,7 +87,7 @@
  * Unit tests for {@link LockTaskController}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.am.LockTaskControllerTest
+ *  bit WmTests:LockTaskControllerTest
  */
 @Presubmit
 @SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/wmtests/src/com/android/server/am/MemoryStatUtilTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
rename to services/tests/wmtests/src/com/android/server/am/MemoryStatUtilTest.java
index 5518ca5..18b0988 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -16,9 +16,9 @@
 
 package com.android.server.am;
 
+import static com.android.server.am.MemoryStatUtil.MemoryStat;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
-import static com.android.server.am.MemoryStatUtil.MemoryStat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
@@ -29,6 +29,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/**
+ * Tests for {@link MemoryStatUtil}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:MemoryStatUtilTest
+ */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MemoryStatUtilTest {
diff --git a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
rename to services/tests/wmtests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
index 2baf995..f812ee6 100644
--- a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
@@ -20,14 +20,11 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
-import android.annotation.Nullable;
 import android.app.ActivityOptions;
-import android.os.Handler;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.ArrayMap;
 import android.view.RemoteAnimationAdapter;
 
 import com.android.server.testutils.OffsettableClock;
@@ -40,7 +37,10 @@
 import org.mockito.MockitoAnnotations;
 
 /**
- * atest PendingRemoteAnimationRegistryTest
+ * Tests for {@link PendingRemoteAnimationRegistry}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:PendingRemoteAnimationRegistryTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/wmtests/src/com/android/server/am/PersistentConnectionTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
rename to services/tests/wmtests/src/com/android/server/am/PersistentConnectionTest.java
index 54f93a8..5c42081 100644
--- a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/PersistentConnectionTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.server.am;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -41,6 +42,12 @@
 import java.util.Arrays;
 import java.util.Collections;
 
+/**
+ * Tests for {@link PersistentConnection}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:PersistentConnectionTest
+ */
 @SmallTest
 public class PersistentConnectionTest extends AndroidTestCase {
     private static class MyConnection extends PersistentConnection<IDeviceAdminService> {
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/am/RecentTasksTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
rename to services/tests/wmtests/src/com/android/server/am/RecentTasksTest.java
index b73ac89..739467a 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/RecentTasksTest.java
@@ -35,16 +35,12 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 
 import static java.lang.Integer.MAX_VALUE;
 
-import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -53,7 +49,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Debug;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -80,7 +75,10 @@
 import java.util.Set;
 
 /**
- * atest FrameworksServicesTests:RecentTasksTest
+ * Tests for {@link RecentTasks}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:RecentTasksTest
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/am/RecentsAnimationTest.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
rename to services/tests/wmtests/src/com/android/server/am/RecentsAnimationTest.java
index eefd973..ee576e3 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/RecentsAnimationTest.java
@@ -16,15 +16,13 @@
 
 package com.android.server.am;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -39,13 +37,18 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.IRecentsAnimationRunner;
+
 import com.android.server.AttributeCache;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 /**
- * atest FrameworksServicesTests:RecentsAnimationTest
+ * Tests for {@link RecentsAnimation}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:RecentsAnimationTest
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/am/RunningTasksTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
rename to services/tests/wmtests/src/com/android/server/am/RunningTasksTest.java
index c6ce7e1..4e5fbd8 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/RunningTasksTest.java
@@ -41,8 +41,11 @@
 
 import java.util.ArrayList;
 
-/**
- * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+ /**
+ * Tests for {@link RunningTasks}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:RunningTasksTest
  */
 @MediumTest
 @Presubmit
@@ -123,4 +126,4 @@
                 .build();
         return task;
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/am/SafeActivityOptionsTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java
rename to services/tests/wmtests/src/com/android/server/am/SafeActivityOptionsTest.java
index 168bc17..3f05c8c 100644
--- a/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/SafeActivityOptionsTest.java
@@ -27,6 +27,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/**
+ * Tests for {@link SafeActivityOptions}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:SafeActivityOptionsTest
+ */
 @MediumTest
 @Presubmit
 @FlakyTest
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
rename to services/tests/wmtests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
index 3d323f0..7fb63d3 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
@@ -16,37 +16,29 @@
 
 package com.android.server.am;
 
-import android.content.pm.ActivityInfo.WindowLayout;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.MediumTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import android.view.Gravity;
-
-import org.junit.runner.RunWith;
-import org.junit.Before;
-import org.junit.Test;
-
-import org.mockito.invocation.InvocationOnMock;
-
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 
 import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
 
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.doAnswer;
 import static org.junit.Assert.assertEquals;
 
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Gravity;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for exercising resizing task bounds.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:TaskLaunchParamsModifierTests
+ *  atest WmTests:TaskLaunchParamsModifierTests
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/am/TaskPersisterTest.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
rename to services/tests/wmtests/src/com/android/server/am/TaskPersisterTest.java
index 9e6055d..cdafb52 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/TaskPersisterTest.java
@@ -17,20 +17,16 @@
 package com.android.server.am;
 
 import android.content.pm.UserInfo;
-import android.os.Environment;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.test.AndroidTestCase;
-import android.util.Log;
 import android.util.SparseBooleanArray;
 
-import com.android.server.am.TaskPersister;
-
-import java.io.File;
-import java.util.Random;
-
 /**
- * atest FrameworksServicesTests:TaskPersisterTest
+ * Tests for {@link TaskPersister}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:TaskPersisterTest
  */
 public class TaskPersisterTest extends AndroidTestCase {
     private static final String TEST_USER_NAME = "AM-Test-User";
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/am/TaskRecordTests.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
rename to services/tests/wmtests/src/com/android/server/am/TaskRecordTests.java
index 057fdc8..dbb3e7e 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/am/TaskRecordTests.java
@@ -29,20 +29,16 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.service.voice.IVoiceInteractionSession;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Xml;
 
-import com.android.frameworks.servicestests.R;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.TaskRecord.TaskRecordFactory;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -57,15 +53,13 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.Reader;
-import java.nio.file.Files;
 import java.util.ArrayList;
-import java.util.Comparator;
 
 /**
  * Tests for exercising {@link TaskRecord}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.am.TaskRecordTests
+ *  atest WmTests:TaskRecordTests
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/am/TaskStackChangedListenerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
rename to services/tests/wmtests/src/com/android/server/am/TaskStackChangedListenerTest.java
index 0359096..28689cb 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/TaskStackChangedListenerTest.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -32,23 +33,30 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.res.Resources.Theme;
 import android.os.RemoteException;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiDevice;
 import android.text.TextUtils;
-import android.util.Pair;
+
 import com.android.internal.annotations.GuardedBy;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link TaskStackListener}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:TaskStackChangedListenerTest
+ */
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class TaskStackChangedListenerTest {
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/wmtests/src/com/android/server/am/UserControllerTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
rename to services/tests/wmtests/src/com/android/server/am/UserControllerTest.java
index cc4f519..fb5ded5 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,30 @@
 
 package com.android.server.am;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+
+import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
+import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
 import android.app.IUserSwitchObserver;
 import android.content.Context;
 import android.content.IIntentReceiver;
@@ -48,30 +72,11 @@
 import java.util.List;
 import java.util.Set;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
-import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
-
 /**
- * Usage: bit FrameworksServicesTests:com.android.server.am.UserControllerTest
+ * Tests for {@link UserController}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:UserControllerTest
  */
 @Presubmit
 public class UserControllerTest extends AndroidTestCase {
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
rename to services/tests/wmtests/src/com/android/server/policy/FakeWindowState.java
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
rename to services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerInsetsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
rename to services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTest.java
rename to services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
rename to services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
similarity index 89%
rename from services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
rename to services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
index 164c80b..a396946 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -18,13 +18,10 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
+
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
@@ -33,12 +30,9 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -46,7 +40,7 @@
  * Tests for the {@link TaskStack} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.AnimatingAppWindowTokenRegistryTest
+ *  atest WmTests:AnimatingAppWindowTokenRegistryTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index be7d781..6b3266d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -20,6 +20,7 @@
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+
 import static org.junit.Assert.assertEquals;
 
 import android.content.Context;
@@ -36,7 +37,8 @@
 /**
  * Test class for {@link AppTransition}.
  *
- * atest AppTransitionTests
+ * Build/Install/Run:
+ *  atest WmTests:AppTransitionTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowContainerControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index e0645b1..09a0847 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -16,30 +16,32 @@
 
 package com.android.server.wm;
 
-import android.support.test.filters.FlakyTest;
-import org.junit.Test;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.res.Configuration.EMPTY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
 import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
 
+import org.junit.Test;
+
 /**
  * Test class for {@link AppWindowContainerController}.
  *
- * atest FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests
+ * Build/Install/Run:
+ *  atest WmTests:AppWindowContainerControllerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index f6599dc..8dedb584a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -16,16 +16,6 @@
 
 package com.android.server.wm;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.Surface;
-import android.view.WindowManager;
-
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -38,15 +28,27 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_UNSET;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Tests for the {@link AppWindowToken} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenTests
+ *  atest WmTests:AppWindowTokenTests
  */
 @SmallTest
 // TODO: b/68267650
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index ff631e7..7fbb4ee 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -21,6 +21,12 @@
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
 import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Rect;
@@ -32,23 +38,12 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import com.android.server.wm.BoundsAnimationController.BoundsAnimator;
 import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import com.android.server.wm.BoundsAnimationController.BoundsAnimator;
-
 /**
  * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
  * depending on the various interactions.
@@ -60,7 +55,7 @@
  * appropriately.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests
+ *  bit WmTests:BoundsAnimationControllerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
index 192e156..6a6f954 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -28,6 +27,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
 import static android.content.res.Configuration.EMPTY;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -35,6 +35,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -45,7 +46,7 @@
  * Test class for {@link ConfigurationContainer}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.ConfigurationContainerTests
+ *  atest WmTests:ConfigurationContainerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 6769e40..a12a802f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -38,8 +38,10 @@
 import org.junit.runner.RunWith;
 
 /**
+ * Tests for {@link Dimmer}.
+ *
  * Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ *  atest WmTests:DimmerTests;
  */
 @Presubmit
 @RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index ac196f9..a9744ea 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -28,6 +28,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.hamcrest.Matchers.is;
@@ -38,9 +39,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import android.annotation.SuppressLint;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -57,6 +55,9 @@
 
 import com.android.server.wm.utils.WmDisplayCutout;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -67,7 +68,7 @@
  * Tests for the {@link DisplayContent} class.
  *
  * Build/Install/Run:
- *  atest com.android.server.wm.DisplayContentTests
+ *  atest WmTests:DisplayContentTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index a09656c..68b0998 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.any;
@@ -39,19 +40,23 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Tests for the {@link DragDropController} class.
  *
- * atest com.android.server.wm.DragDropControllerTests
+ * Build/Install/Run:
+ *  atest WmTests:DragDropControllerTests
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index 96745fa..f0faf28 100644
--- a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -2,6 +2,15 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
@@ -14,15 +23,12 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
+/**
+ * Tests for {@link PinnedStackController}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:PinnedStackControllerTest
+ */
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index a2af9b8..fdebaa6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -19,8 +19,10 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
@@ -35,7 +37,9 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.view.IRecentsAnimationRunner;
 import android.view.SurfaceControl;
+
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -43,7 +47,10 @@
 import org.mockito.MockitoAnnotations;
 
 /**
- * atest FrameworksServicesTests:com.android.server.wm.RecentsAnimationControllerTest
+ * Tests for {@link RecentsAnimationController}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:RecentsAnimationControllerTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 95361f0..383e53f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -31,7 +31,6 @@
 import android.os.Binder;
 import android.os.IInterface;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.IRemoteAnimationFinishedCallback;
@@ -53,7 +52,10 @@
 import org.mockito.MockitoAnnotations;
 
 /**
- * atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
+ * Tests for {@link RemoteAnimationController}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:RemoteAnimationControllerTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 204e26c..317f36c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -1,22 +1,21 @@
 package com.android.server.wm;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static org.junit.Assert.assertTrue;
 
 import android.content.res.Configuration;
 import android.graphics.Rect;
-
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link RootWindowContainer} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests
+ *  atest WmTests:RootWindowContainerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
rename to services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index a2ccee4..7e7cfed 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -32,6 +32,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
 import static org.junit.Assert.assertEquals;
 
 import android.app.Activity;
@@ -70,7 +71,7 @@
  * Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.ScreenDecorWindowTests
+ *  atest WmTests:ScreenDecorWindowTests
  */
 // TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
 // TODO: Test non-Activity windows.
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
index ab0a2bd..fb2986c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
@@ -16,25 +16,24 @@
 
 package com.android.server.wm;
 
-import android.graphics.Rect;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Test class for {@link StackWindowController}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:StackWindowControllerTests
+ *  atest WmTests:StackWindowControllerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index edac8a5..9ea3702 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -24,11 +23,11 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.animation.AnimationHandler;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
 import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
 import android.animation.ValueAnimator;
 import android.graphics.Matrix;
@@ -37,7 +36,6 @@
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
 import android.view.Choreographer;
 import android.view.Choreographer.FrameCallback;
 import android.view.SurfaceControl;
@@ -46,7 +44,6 @@
 import android.view.animation.TranslateAnimation;
 
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
-import com.android.server.wm.SurfaceAnimationRunner.AnimatorFactory;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -61,7 +58,8 @@
 /**
  * Test class for {@link SurfaceAnimationRunner}.
  *
- * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimationRunnerTest
+ * Build/Install/Run:
+ *  atest WmTests:SurfaceAnimationRunnerTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 16b8458..66f4b194 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -46,12 +46,11 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
-
 /**
  * Test class for {@link SurfaceAnimatorTest}.
  *
- * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimatorTest
+ * Build/Install/Run:
+ *  atest WmTests:SurfaceAnimatorTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 7bf7dd7..ae73089 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -16,10 +16,14 @@
 
 package com.android.server.wm;
 
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
+import static com.android.server.wm.WindowManagerService.dipToPixel;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import android.graphics.Rect;
 import android.support.test.filters.SmallTest;
@@ -28,18 +32,16 @@
 import android.util.Log;
 import android.view.Display;
 
-import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link TaskPositioner} class.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskPositionerTests
+ * Build/Install/Run:
+ *  atest WmTests:TaskPositionerTests
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 6070516..659ec41 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -30,6 +31,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.InputChannel;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,7 +39,8 @@
 /**
  * Tests for the {@link TaskPositioningController} class.
  *
- * atest com.android.server.wm.TaskPositioningControllerTests
+ * Build/Install/Run:
+ *  atest WmTests:TaskPositioningControllerTests
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index 649de4a..1aefa277 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 
@@ -31,7 +32,8 @@
 /**
  * Test class for {@link TaskSnapshotCache}.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotCacheTest
+ * Build/Install/Run:
+ *  atest WmTests:TaskSnapshotCacheTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 5650050..77521a6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -19,10 +19,11 @@
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.TRANSIT_UNSET;
-import static com.android.server.wm.TaskSnapshotController.*;
+
+import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THEME;
+import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
+
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
 
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
@@ -30,13 +31,15 @@
 import android.util.ArraySet;
 
 import com.google.android.collect.Sets;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskSnapshotController}.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotControllerTest
+ * Build/Install/Run:
+ *  atest WmTests:TaskSnapshotControllerTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 325d42a..ed81daf 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -18,12 +18,12 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.res.Configuration;
@@ -33,8 +33,8 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.ArraySet;
-
 import android.view.View;
+
 import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
 
 import org.junit.Test;
@@ -45,7 +45,8 @@
 /**
  * Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader}
  *
- * atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest
+ * Build/Install/Run:
+ *  atest WmTests:TaskSnapshotPersisterLoaderTest
  */
 @MediumTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index b19373e..287fff5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
@@ -47,7 +48,8 @@
 /**
  * Test class for {@link TaskSnapshotSurface}.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotSurfaceTest
+ * Build/Install/Run:
+ *  atest WmTests:TaskSnapshotSurfaceTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index ca1994f..e66b0d9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -18,26 +18,25 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.Before;
-import org.junit.After;
-
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.TaskStackContainersTests
+ *  atest WmTests:TaskStackContainersTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index eaf71f0..0deb4af 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -16,27 +16,24 @@
 
 package com.android.server.wm;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link TaskStack} class.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.TaskStackTests
+ *  atest WmTests:TaskStackTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index 1dd9365..e734bc0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -16,22 +16,23 @@
 
 package com.android.server.wm;
 
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Test class for {@link TaskWindowContainerController}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.TaskWindowContainerControllerTests
+ *  atest WmTests:TaskWindowContainerControllerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
rename to services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 353aa7d..032f416 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import com.android.internal.os.IResultReceiver;
-
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -27,6 +25,8 @@
 import android.view.DragEvent;
 import android.view.IWindow;
 
+import com.android.internal.os.IResultReceiver;
+
 public class TestIWindow extends IWindow.Stub {
     @Override
     public void executeCommand(String command, String parameters,
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
rename to services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index a5c47de..903352b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -29,7 +29,8 @@
 /**
  * Test class for {@link AppTransition}.
  *
- * runtest frameworks-services -c com.android.server.wm.UnknownAppVisibilityControllerTest
+ * Build/Install/Run:
+ *  atest WmTests:UnknownAppVisibilityControllerTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
index ca520ed..aac905e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -40,7 +40,8 @@
 /**
  * Tests for the {@link WindowAnimationSpec} class.
  *
- *  atest FrameworksServicesTests:com.android.server.wm.WindowAnimationSpecTest
+ * Build/Install/Run:
+ *  atest WmTests:WindowAnimationSpecTest
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 513c1ec..9c59a17 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -16,7 +16,16 @@
 
 package com.android.server.wm;
 
-import org.junit.Test;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
+import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE;
+import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import android.app.WindowConfiguration;
 import android.content.res.Configuration;
@@ -27,22 +36,13 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.view.DisplayInfo;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
-import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE;
-import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
+import org.junit.Test;
 
 /**
  * Test class to for {@link android.app.WindowConfiguration}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowConfigurationTests
+ *  atest WmTests:WindowConfigurationTests
  */
 @SmallTest
 @FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
index 502cb6e..05f7c34 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
@@ -16,26 +16,26 @@
 
 package com.android.server.wm;
 
-import android.app.WindowConfiguration;
-import android.content.res.Configuration;
-import android.support.test.filters.FlakyTest;
-import org.junit.Test;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.content.res.Configuration.EMPTY;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.content.res.Configuration;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+
 /**
  * Test class for {@link WindowContainerController}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowContainerControllerTests
+ *  atest WmTests:WindowContainerControllerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 6c7830e..5beeee1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -16,20 +16,6 @@
 
 package com.android.server.wm;
 
-import android.support.test.filters.FlakyTest;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import java.util.Comparator;
-
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -44,7 +30,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyFloat;
 import static org.mockito.Mockito.eq;
@@ -53,11 +38,25 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Comparator;
+
 /**
  * Test class for {@link WindowContainer}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.WindowContainerTests
+ *  atest WmTests:WindowContainerTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index e076399..e1acd78 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -20,9 +20,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Matchers.eq;
 
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
@@ -31,12 +32,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.mockito.Mock;
-
 import java.util.function.Consumer;
 
 /**
  * Tests for {@link WindowContainer#forAllWindows} and various implementations.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:WindowContainerTraversalTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 5a56332..6a00de4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -16,9 +16,12 @@
 
 package com.android.server.wm;
 
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static android.view.DisplayCutout.fromBoundingRect;
+import static android.view.WindowManager.LayoutParams.FILL_PARENT;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.app.ActivityManager.TaskDescription;
 import android.content.res.Configuration;
@@ -31,18 +34,17 @@
 import android.view.IWindow;
 import android.view.WindowManager;
 
-import static android.view.DisplayCutout.fromBoundingRect;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.FILL_PARENT;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import com.android.server.wm.utils.WmDisplayCutout;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
  *
- * Build/Install/Run: bit FrameworksServicesTests:com.android.server.wm.WindowFrameTests
+ * Build/Install/Run:
+ *  atest WmTests:WindowFrameTests
  */
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 85e846d..0f07e7e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -16,17 +16,6 @@
 
 package com.android.server.wm;
 
-import android.view.WindowManager;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.FlakyTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import java.util.LinkedList;
-
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
@@ -55,10 +44,22 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.LinkedList;
+
 /**
  * Tests for the {@link WindowState} class.
  *
- * atest FrameworksServicesTests:com.android.server.wm.WindowStateTests
+ * Build/Install/Run:
+ *  atest WmTests:WindowStateTests
  */
 @SmallTest
 @FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowSurfacePlacerTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowSurfacePlacerTest.java
index e173b7d..13f7916 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowSurfacePlacerTest.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+
 import static junit.framework.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
@@ -31,6 +32,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/**
+ * Tests for {@link WindowSurfacePlacer}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:WindowSurfacePlacerTest
+ */
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 2e4740b..6ab9c30 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -16,21 +16,12 @@
 
 package com.android.server.wm;
 
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.IBinder;
-import android.view.IApplicationToken;
-import android.view.IWindow;
-import android.view.WindowManager;
-
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyFloat;
@@ -39,6 +30,15 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.IApplicationToken;
+import android.view.IWindow;
+import android.view.WindowManager;
+
 import org.mockito.invocation.InvocationOnMock;
 
 /**
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index e3b7174..5ea87b1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -16,29 +16,29 @@
 
 package com.android.server.wm;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link WindowToken} class.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowTokenTests
+ *  atest WmTests:WindowTokenTests
  */
 @SmallTest
 @FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index 5085254..f60ae75 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -30,13 +30,12 @@
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
 import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.Preconditions;
-import com.android.server.wm.WindowManagerTraceProto;
 
 import org.junit.After;
 import org.junit.Before;
@@ -54,7 +53,7 @@
  * Test class for {@link WindowTracing}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowTracingTest
+ *  atest WmTests:WindowTracingTest
  */
 @SmallTest
 @FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
rename to services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 547be55..a730b5c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -27,15 +27,14 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
 
 import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
 import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -51,7 +50,7 @@
  * Tests for the {@link WindowLayersController} class.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ *  atest WmTests:ZOrderingTests
  */
 @SmallTest
 @FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
rename to services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
index 40a10e0..f0f3023 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java
@@ -21,10 +21,11 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
-import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+import static com.android.server.wm.utils.CoordinateTransforms
+        .transformPhysicalToLogicalCoordinates;
 
 import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertThat;
 
 import android.graphics.Matrix;
 import android.graphics.Point;
@@ -35,6 +36,12 @@
 import org.junit.Test;
 import org.junit.rules.ErrorCollector;
 
+/**
+ * Tests for {@link CoordinateTransforms}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:CoordinateTransformsTest
+ */
 public class CoordinateTransformsTest {
 
     private static final int W = 200;
@@ -95,4 +102,4 @@
     public interface DevicePointAssertable {
         void mapsToLogicalPoint(int x, int y);
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/InsetUtilsTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
rename to services/tests/wmtests/src/com/android/server/wm/utils/InsetUtilsTest.java
index d0f0fe3..ff3d883 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/InsetUtilsTest.java
@@ -22,11 +22,16 @@
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.Pair;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/**
+ * Tests for {@link InsetUtils}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:InsetUtilsTest
+ */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
rename to services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
index 6bbc7eb..c800fa2 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationCacheTest.java
@@ -29,13 +29,16 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Pair;
 
-import com.android.server.wm.utils.RotationCache.RotationDependentComputation;
-
-import org.hamcrest.Matchers;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/**
+ * Tests for {@link RotationCache}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:RotationCacheTest
+ */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @FlakyTest(bugId = 74078662)
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
rename to services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index f7addf6..f4c208b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.utils;
 
-
 import static android.view.DisplayCutout.NO_CUTOUT;
 import static android.view.DisplayCutout.fromBoundingRect;
 
@@ -38,7 +37,8 @@
 /**
  * Tests for {@link WmDisplayCutout}
  *
- * Run with: atest WmDisplayCutoutTest
+ * Build/Install/Run:
+ *  atest WmTests:WmDisplayCutoutTest
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 36f8063..4da7285 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -539,7 +539,8 @@
             // We do not show the USB notification if the primary volume supports mass storage.
             // The legacy mass storage UI will be used instead.
             final StorageManager storageManager = StorageManager.from(mContext);
-            final StorageVolume primary = storageManager.getPrimaryVolume();
+            final StorageVolume primary =
+                    storageManager != null ? storageManager.getPrimaryVolume() : null;
 
             boolean massStorageSupported = primary != null && primary.allowMassStorage();
             mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean(
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
new file mode 100644
index 0000000..b3b0900
--- /dev/null
+++ b/startop/iorap/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+java_library_static {
+  name: "libiorap-java",
+
+  aidl: {
+    include_dirs: [
+      "system/iorap/binder",
+    ],
+  },
+
+  srcs: [
+      ":iorap-aidl",
+      "**/*.java",
+  ],
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
new file mode 100644
index 0000000..1d38f4c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
@@ -0,0 +1,139 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an activity has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onActivityHintEvent<br /><br />
+ *
+ * Once an activity hint is in {@link #TYPE_STARTED} it must transition to another type.
+ * All other states could be terminal, see below: <br /><br />
+ *
+ * <pre>
+ *
+ *          ┌──────────────────────────────────────┐
+ *          │                                      ▼
+ *        ┌─────────┐     ╔════════════════╗     ╔═══════════╗
+ *    ──▶ │ STARTED │ ──▶ ║   COMPLETED    ║ ──▶ ║ CANCELLED ║
+ *        └─────────┘     ╚════════════════╝     ╚═══════════╝
+ *                          │
+ *                          │
+ *                          ▼
+ *                        ╔════════════════╗
+ *                        ║ POST_COMPLETED ║
+ *                        ╚════════════════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/ActivityHint.dot -->
+ *
+ * @hide
+ */
+public class ActivityHintEvent implements Parcelable {
+
+    public static final int TYPE_STARTED = 0;
+    public static final int TYPE_CANCELLED = 1;
+    public static final int TYPE_COMPLETED = 2;
+    public static final int TYPE_POST_COMPLETED = 3;
+    private static final int TYPE_MAX = TYPE_POST_COMPLETED;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_STARTED,
+            TYPE_CANCELLED,
+            TYPE_COMPLETED,
+            TYPE_POST_COMPLETED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+    public final ActivityInfo activityInfo;
+
+    public ActivityHintEvent(@Type int type, ActivityInfo activityInfo) {
+        this.type = type;
+        this.activityInfo = activityInfo;
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+        Objects.requireNonNull(activityInfo, "activityInfo");
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{type: %d, activityInfo: %s}", type, activityInfo);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof ActivityHintEvent) {
+            return equals((ActivityHintEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(ActivityHintEvent other) {
+        return type == other.type &&
+                Objects.equals(activityInfo, other.activityInfo);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+        activityInfo.writeToParcel(out, flags);
+    }
+
+    private ActivityHintEvent(Parcel in) {
+        this.type = in.readInt();
+        this.activityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<ActivityHintEvent> CREATOR
+            = new Parcelable.Creator<ActivityHintEvent>() {
+        public ActivityHintEvent createFromParcel(Parcel in) {
+            return new ActivityHintEvent(in);
+        }
+
+        public ActivityHintEvent[] newArray(int size) {
+            return new ActivityHintEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
new file mode 100644
index 0000000..f47a42c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
@@ -0,0 +1,104 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import java.util.Objects;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Provide minimal information for launched activities to iorap.<br /><br />
+ *
+ * This uniquely identifies a system-wide activity by providing the {@link #packageName} and
+ * {@link #activityName}.
+ *
+ * @see ActivityHintEvent
+ * @see AppIntentEvent
+ *
+ * @hide
+ */
+public class ActivityInfo implements Parcelable {
+
+    /** The name of the package, for example {@code com.android.calculator}. */
+    public final String packageName;
+    /** The name of the activity, for example {@code .activities.activity.MainActivity} */
+    public final String activityName;
+
+    public ActivityInfo(String packageName, String activityName) {
+        this.packageName = packageName;
+        this.activityName = activityName;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        Objects.requireNonNull(packageName, "packageName");
+        Objects.requireNonNull(activityName, "activityName");
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{packageName: %s, activityName: %s}", packageName, activityName);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof ActivityInfo) {
+            return equals((ActivityInfo) other);
+        }
+        return false;
+    }
+
+    private boolean equals(ActivityInfo other) {
+        return Objects.equals(packageName, other.packageName) &&
+                Objects.equals(activityName, other.activityName);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(packageName);
+        out.writeString(activityName);
+    }
+
+    private ActivityInfo(Parcel in) {
+        packageName = in.readString();
+        activityName = in.readString();
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<ActivityInfo> CREATOR
+            = new Parcelable.Creator<ActivityInfo>() {
+        public ActivityInfo createFromParcel(Parcel in) {
+            return new ActivityInfo(in);
+        }
+
+        public ActivityInfo[] newArray(int size) {
+            return new ActivityInfo[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
new file mode 100644
index 0000000..1cd37b5
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
@@ -0,0 +1,138 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Notifications for iorapd specifying when a system-wide intent defaults change.<br /><br />
+ *
+ * Intent defaults provide a mechanism for an app to register itself as an automatic handler.
+ * For example the camera app might be registered as the default handler for
+ * {@link android.provider.MediaStore#INTENT_ACTION_STILL_IMAGE_CAMERA} intent. Subsequently,
+ * if an arbitrary other app requests for a still image camera photo to be taken, the system
+ * will launch the respective default camera app to be launched to handle that request.<br /><br />
+ *
+ * In some cases iorapd might need to know default intents, e.g. for boot-time pinning of
+ * applications that resolve from the default intent. If the application would now be resolved
+ * differently, iorapd would unpin the old application and pin the new application.<br /><br />
+ *
+ * @hide
+ */
+public class AppIntentEvent implements Parcelable {
+
+    /** @see android.content.Intent#CATEGORY_DEFAULT */
+    public static final int TYPE_DEFAULT_INTENT_CHANGED = 0;
+    private static final int TYPE_MAX = 0;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_DEFAULT_INTENT_CHANGED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+
+    public final ActivityInfo oldActivityInfo;
+    public final ActivityInfo newActivityInfo;
+
+    // TODO: Probably need the corresponding action here as well.
+
+    public static AppIntentEvent createDefaultIntentChanged(ActivityInfo oldActivityInfo,
+            ActivityInfo newActivityInfo) {
+        return new AppIntentEvent(TYPE_DEFAULT_INTENT_CHANGED, oldActivityInfo,
+                newActivityInfo);
+    }
+
+    private AppIntentEvent(@Type int type, ActivityInfo oldActivityInfo,
+            ActivityInfo newActivityInfo) {
+        this.type = type;
+        this.oldActivityInfo = oldActivityInfo;
+        this.newActivityInfo = newActivityInfo;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+        Objects.requireNonNull(oldActivityInfo, "oldActivityInfo");
+        Objects.requireNonNull(oldActivityInfo, "newActivityInfo");
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{oldActivityInfo: %s, newActivityInfo: %s}", oldActivityInfo,
+                newActivityInfo);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof AppIntentEvent) {
+            return equals((AppIntentEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(AppIntentEvent other) {
+        return type == other.type &&
+                Objects.equals(oldActivityInfo, other.oldActivityInfo) &&
+                Objects.equals(newActivityInfo, other.newActivityInfo);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+        oldActivityInfo.writeToParcel(out, flags);
+        newActivityInfo.writeToParcel(out, flags);
+    }
+
+    private AppIntentEvent(Parcel in) {
+        this.type = in.readInt();
+        this.oldActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+        this.newActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<AppIntentEvent> CREATOR
+            = new Parcelable.Creator<AppIntentEvent>() {
+        public AppIntentEvent createFromParcel(Parcel in) {
+            return new AppIntentEvent(in);
+        }
+
+        public AppIntentEvent[] newArray(int size) {
+            return new AppIntentEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java
new file mode 100644
index 0000000..34aedd7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.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.google.android.startop.iorap;
+
+/**
+ * Convenience short-hand to throw {@link IllegalAccessException} when the arguments
+ * are out-of-range.
+ */
+public class CheckHelpers {
+    /** @throws IllegalAccessException if {@param type} is not in {@code [0..maxValue]} */
+    public static void checkTypeInRange(int type, int maxValue) {
+        if (type < 0) {
+            throw new IllegalArgumentException(
+                    String.format("type must be non-negative (value=%d)", type));
+        }
+        if (type > maxValue) {
+            throw new IllegalArgumentException(
+                    String.format("type out of range (value=%d, max=%d)", type, maxValue));
+        }
+    }
+
+    /** @throws IllegalAccessException if {@param state} is not in {@code [0..maxValue]} */
+    public static void checkStateInRange(int state, int maxValue) {
+        if (state < 0) {
+            throw new IllegalArgumentException(
+                    String.format("state must be non-negative (value=%d)", state));
+        }
+        if (state > maxValue) {
+            throw new IllegalArgumentException(
+                    String.format("state out of range (value=%d, max=%d)", state, maxValue));
+        }
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
new file mode 100644
index 0000000..aa4eea7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
@@ -0,0 +1,131 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.Uri;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Forward package manager events to iorapd. <br /><br />
+ *
+ * Knowing when packages are modified by the system are a useful tidbit to help with performance:
+ * for example when a package is replaced, it could be a hint used to invalidate any collected
+ * io profiles used for prefetching or pinning.
+ *
+ * @hide
+ */
+public class PackageEvent implements Parcelable {
+
+    /** @see android.content.Intent#ACTION_PACKAGE_REPLACED */
+    public static final int TYPE_REPLACED = 0;
+    private static final int TYPE_MAX = 0;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_REPLACED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+
+    /** The path that a package is installed in, for example {@code /data/app/.../base.apk}. */
+    public final Uri packageUri;
+    /** The name of the package, for example {@code com.android.calculator}. */
+    public final String packageName;
+
+    @NonNull
+    public static PackageEvent createReplaced(Uri packageUri, String packageName) {
+        return new PackageEvent(TYPE_REPLACED, packageUri, packageName);
+    }
+
+    private PackageEvent(@Type int type, Uri packageUri, String packageName) {
+        this.type = type;
+        this.packageUri = packageUri;
+        this.packageName = packageName;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+        Objects.requireNonNull(packageUri, "packageUri");
+        Objects.requireNonNull(packageName, "packageName");
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof PackageEvent) {
+            return equals((PackageEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(PackageEvent other) {
+        return type == other.type &&
+                Objects.equals(packageUri, other.packageUri) &&
+                Objects.equals(packageName, other.packageName);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{packageUri: %s, packageName: %s}", packageUri, packageName);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+        packageUri.writeToParcel(out, flags);
+        out.writeString(packageName);
+    }
+
+    private PackageEvent(Parcel in) {
+        this.type = in.readInt();
+        this.packageUri = Uri.CREATOR.createFromParcel(in);
+        this.packageName = in.readString();
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<PackageEvent> CREATOR
+            = new Parcelable.Creator<PackageEvent>() {
+        public PackageEvent createFromParcel(Parcel in) {
+            return new PackageEvent(in);
+        }
+
+        public PackageEvent[] newArray(int size) {
+            return new PackageEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
new file mode 100644
index 0000000..2c79319
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
@@ -0,0 +1,120 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.NonNull;
+
+/**
+ * Uniquely identify an {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * used for asynchronous callbacks by the server. <br /><br />
+ *
+ * As all system server binder calls must be {@code oneway}, this means all invocations
+ * into {@link com.google.android.startop.iorap.IIorap} are non-blocking. The request ID
+ * exists to associate all calls with their respective callbacks in
+ * {@link com.google.android.startop.iorap.ITaskListener}.
+ *
+ * @see com.google.android.startop.iorap.IIorap
+ *
+ * @hide
+ */
+public class RequestId implements Parcelable {
+
+    public final long requestId;
+
+    private static Object mLock = new Object();
+    private static long mNextRequestId = 0;
+
+    /**
+     * Create a monotonically increasing request ID.<br /><br />
+     *
+     * It is invalid to re-use the same request ID for multiple method calls on
+     * {@link com.google.android.startop.iorap.IIorap}; a new request ID must be created
+     * each time.
+     */
+    @NonNull public static RequestId nextValueForSequence() {
+        long currentRequestId;
+        synchronized (mLock) {
+            currentRequestId = mNextRequestId;
+            ++mNextRequestId;
+        }
+        return new RequestId(currentRequestId);
+    }
+
+    private RequestId(long requestId) {
+        this.requestId = requestId;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        if (requestId < 0) {
+            throw new IllegalArgumentException("request id must be non-negative");
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{requestId: %ld}", requestId);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof RequestId) {
+            return equals((RequestId) other);
+        }
+        return false;
+    }
+
+    private boolean equals(RequestId other) {
+        return requestId == other.requestId;
+    }
+
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeLong(requestId);
+    }
+
+    private RequestId(Parcel in) {
+        requestId = in.readLong();
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<RequestId> CREATOR
+            = new Parcelable.Creator<RequestId>() {
+        public RequestId createFromParcel(Parcel in) {
+            return new RequestId(in);
+        }
+
+        public RequestId[] newArray(int size) {
+            return new RequestId[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
new file mode 100644
index 0000000..75d47f9
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward system service events to iorapd.
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceEvent implements Parcelable {
+
+    /** @see com.android.server.SystemService#onBootPhase */
+    public static final int TYPE_BOOT_PHASE = 0;
+    /** @see com.android.server.SystemService#onStart */
+    public static final int TYPE_START = 1;
+    private static final int TYPE_MAX = TYPE_START;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_BOOT_PHASE,
+            TYPE_START,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+
+    // TODO: do we want to pass the exact build phase enum?
+
+    public SystemServiceEvent(@Type int type) {
+        this.type = type;
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{type: %d}", type);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof SystemServiceEvent) {
+            return equals((SystemServiceEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(SystemServiceEvent other) {
+        return type == other.type;
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+    }
+
+    private SystemServiceEvent(Parcel in) {
+        this.type = in.readInt();
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<SystemServiceEvent> CREATOR
+            = new Parcelable.Creator<SystemServiceEvent>() {
+        public SystemServiceEvent createFromParcel(Parcel in) {
+            return new SystemServiceEvent(in);
+        }
+
+        public SystemServiceEvent[] newArray(int size) {
+            return new SystemServiceEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
new file mode 100644
index 0000000..b77c03c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
@@ -0,0 +1,127 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward user events to iorapd.<br /><br />
+ *
+ * Knowledge of the logged-in user is reserved to be used to set-up appropriate policies
+ * by iorapd (e.g. to handle user default pinned applications changing).
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceUserEvent implements Parcelable {
+
+    /** @see com.android.server.SystemService#onStartUser */
+    public static final int TYPE_START_USER = 0;
+    /** @see com.android.server.SystemService#onUnlockUser */
+    public static final int TYPE_UNLOCK_USER = 1;
+    /** @see com.android.server.SystemService#onSwitchUser*/
+    public static final int TYPE_SWITCH_USER = 2;
+    /** @see com.android.server.SystemService#onStopUser */
+    public static final int TYPE_STOP_USER = 3;
+    /** @see com.android.server.SystemService#onCleanupUser */
+    public static final int TYPE_CLEANUP_USER = 4;
+    private static final int TYPE_MAX = TYPE_CLEANUP_USER;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_START_USER,
+            TYPE_UNLOCK_USER,
+            TYPE_SWITCH_USER,
+            TYPE_STOP_USER,
+            TYPE_CLEANUP_USER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+    public final int userHandle;
+
+    public SystemServiceUserEvent(@Type int type, int userHandle) {
+        this.type = type;
+        this.userHandle = userHandle;
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+        if (userHandle < 0) {
+            throw new IllegalArgumentException("userHandle must be non-negative");
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{type: %d, userHandle: %d}", type, userHandle);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof SystemServiceUserEvent) {
+            return equals((SystemServiceUserEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(SystemServiceUserEvent other) {
+        return type == other.type &&
+                userHandle == other.userHandle;
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+        out.writeInt(userHandle);
+    }
+
+    private SystemServiceUserEvent(Parcel in) {
+        this.type = in.readInt();
+        this.userHandle = in.readInt();
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<SystemServiceUserEvent> CREATOR
+            = new Parcelable.Creator<SystemServiceUserEvent>() {
+        public SystemServiceUserEvent createFromParcel(Parcel in) {
+            return new SystemServiceUserEvent(in);
+        }
+
+        public SystemServiceUserEvent[] newArray(int size) {
+            return new SystemServiceUserEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
new file mode 100644
index 0000000..b5fd6d8
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
@@ -0,0 +1,130 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Result data accompanying a request for {@link com.google.android.startop.iorap.ITaskListener}
+ * callbacks.<br /><br />
+ *
+ * Following {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * iorapd will issue in-order callbacks for that corresponding {@link RequestId}.<br /><br />
+ *
+ * State transitions are as follows: <br /><br />
+ *
+ * <pre>
+ *          ┌─────────────────────────────┐
+ *          │                             ▼
+ *        ┌───────┐     ┌─────────┐     ╔═══════════╗
+ *    ──▶ │ BEGAN │ ──▶ │ ONGOING │ ──▶ ║ COMPLETED ║
+ *        └───────┘     └─────────┘     ╚═══════════╝
+ *          │             │
+ *          │             │
+ *          ▼             │
+ *        ╔═══════╗       │
+ *    ──▶ ║ ERROR ║ ◀─────┘
+ *        ╚═══════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/TaskResult.dot -->
+ *
+ * @hide
+ */
+public class TaskResult implements Parcelable {
+
+    public static final int STATE_BEGAN = 0;
+    public static final int STATE_ONGOING = 1;
+    public static final int STATE_COMPLETED = 2;
+    public static final int STATE_ERROR = 3;
+    private static final int STATE_MAX = STATE_ERROR;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "STATE_" }, value = {
+            STATE_BEGAN,
+            STATE_ONGOING,
+            STATE_COMPLETED,
+            STATE_ERROR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
+    @State public final int state;
+
+    @Override
+    public String toString() {
+        return String.format("{state: %d}", state);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof TaskResult) {
+            return equals((TaskResult) other);
+        }
+        return false;
+    }
+
+    private boolean equals(TaskResult other) {
+        return state == other.state;
+    }
+
+    public TaskResult(@State int state) {
+        this.state = state;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkStateInRange(state, STATE_MAX);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(state);
+    }
+
+    private TaskResult(Parcel in) {
+        state = in.readInt();
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<TaskResult> CREATOR
+            = new Parcelable.Creator<TaskResult>() {
+        public TaskResult createFromParcel(Parcel in) {
+            return new TaskResult(in);
+        }
+
+        public TaskResult[] newArray(int size) {
+            return new TaskResult[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
new file mode 100644
index 0000000..7605784
--- /dev/null
+++ b/startop/iorap/tests/Android.bp
@@ -0,0 +1,40 @@
+// 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.
+
+// TODO: once b/80095087 is fixed, rewrite this back to android_test
+java_library {
+    name: "libiorap-java-test-lib",
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+      // non-test dependencies
+      "libiorap-java",
+      // test android dependencies
+      "platform-test-annotations",
+      "android-support-test",
+      // test framework dependencies
+      "mockito-target-inline-minus-junit4",
+      // "mockito-target-minus-junit4",
+        // Mockito also requires JNI (see Android.mk)
+        // and android:debuggable=true (see AndroidManifest.xml)
+      "truth-prebuilt",
+    ],
+
+    // sdk_version: "current",
+    // certificate: "platform",
+
+    libs: ["android.test.base", "android.test.runner"],
+
+    // test_suites: ["device-tests"],
+}
diff --git a/startop/iorap/tests/Android.mk b/startop/iorap/tests/Android.mk
new file mode 100644
index 0000000..1b2aa46
--- /dev/null
+++ b/startop/iorap/tests/Android.mk
@@ -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.
+
+# android_test does not support JNI libraries
+# TODO: once b/80095087 is fixed, rewrite this back to android_test
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
+
+LOCAL_PACKAGE_NAME := libiorap-java-tests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    libiorap-java-test-lib
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+    libdexmakerjvmtiagent \
+    libstaticjvmtiagent \
+    libmultiplejvmtiagentsinterferenceagent
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.base \
+    android.test.runner
+
+# Use private APIs
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+include $(BUILD_PACKAGE)
diff --git a/startop/iorap/tests/AndroidManifest.xml b/startop/iorap/tests/AndroidManifest.xml
new file mode 100644
index 0000000..99f4add
--- /dev/null
+++ b/startop/iorap/tests/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+<!--suppress AndroidUnknownAttribute -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.startop.iorap.tests"
+    android:sharedUserId="com.google.android.startop.iorap.tests"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <!--suppress AndroidDomInspection -->
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.google.android.startop.iorap.tests" />
+
+      <!--
+       'debuggable=true' is required to properly load mockito jvmti dependencies,
+         otherwise it gives the following error at runtime:
+
+       Openjdkjvmti plugin was loaded on a non-debuggable Runtime.
+       Plugin was loaded too late to change runtime state to DEBUGGABLE. -->
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
new file mode 100644
index 0000000..4ba44a9
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.google.android.startop.iorap
+
+import android.net.Uri
+import android.os.ServiceManager
+import android.support.test.filters.MediumTest
+import org.junit.Test
+import org.junit.Ignore
+import org.mockito.Mockito.*
+
+// @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it")
+@MediumTest
+class IIorapIntegrationTest {
+    /**
+     * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found
+     */
+    private val iorapService : IIorap by lazy {
+        // TODO: connect to 'iorapd.stub' which doesn't actually do any work other than reply.
+        IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"))
+
+        // Use 'adb shell setenforce 0' otherwise this whole test fails,
+        // because the servicemanager is not allowed to hand out the binder token for iorapd.
+
+        // TODO: implement the selinux policies for iorapd.
+    }
+
+    // A dummy binder stub implementation is required to use with mockito#spy.
+    // Mockito overrides the methods at runtime and tracks how methods were invoked.
+    open class DummyTaskListener : ITaskListener.Stub()  {
+        // Note: make parameters nullable to avoid the kotlin IllegalStateExceptions
+        // from using the mockito matchers (eq, argThat, etc).
+        override fun onProgress(requestId: RequestId?, result: TaskResult?) {
+        }
+
+        override fun onComplete(requestId: RequestId?, result: TaskResult?) {
+        }
+    }
+
+    private fun testAnyMethod(func : (RequestId) -> Unit) {
+        val taskListener = spy(DummyTaskListener())!!
+
+        try {
+            iorapService.setTaskListener(taskListener)
+            // Note: Binder guarantees total order for oneway messages sent to the same binder
+            // interface, so we don't need any additional blocking here before sending later calls.
+
+            // Every new method call should have a unique request id.
+            val requestId = RequestId.nextValueForSequence()!!
+
+            // Apply the specific function under test.
+            func(requestId)
+
+            // Typical mockito behavior is to allow any-order callbacks, but we want to test order.
+            val inOrder = inOrder(taskListener)
+
+            // The "stub" behavior of iorapd is that every request immediately gets a response of
+            //   BEGAN,ONGOING,COMPLETED
+            inOrder.verify(taskListener, timeout(100)).
+                  onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_BEGAN })
+            inOrder.verify(taskListener, timeout(100)).
+                  onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_ONGOING })
+            inOrder.verify(taskListener, timeout(100)).
+                  onComplete(eq(requestId), argThat { it!!.state == TaskResult.STATE_COMPLETED })
+            inOrder.verifyNoMoreInteractions()
+
+        } finally {
+            iorapService.setTaskListener(null)
+        }
+    }
+
+    @Test
+    fun testOnPackageEvent() {
+        testAnyMethod { requestId : RequestId ->
+            iorapService.onPackageEvent(requestId,
+                    PackageEvent.createReplaced(
+                            Uri.parse("https://www.google.com"), "com.fake.package"))
+        }
+    }
+
+    @Test
+    fun testOnAppIntentEvent() {
+        testAnyMethod { requestId : RequestId ->
+            iorapService.onAppIntentEvent(requestId, AppIntentEvent.createDefaultIntentChanged(
+                    ActivityInfo("dont care", "dont care"),
+                    ActivityInfo("dont care 2", "dont care 2")))
+        }
+    }
+
+    @Test
+    fun testOnSystemServiceEvent() {
+        testAnyMethod { requestId : RequestId ->
+            iorapService.onSystemServiceEvent(requestId,
+                    SystemServiceEvent(SystemServiceEvent.TYPE_START))
+        }
+    }
+
+    @Test
+    fun testOnSystemServiceUserEvent() {
+        testAnyMethod { requestId : RequestId ->
+            iorapService.onSystemServiceUserEvent(requestId,
+                    SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER,0))
+        }
+    }
+}
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
new file mode 100644
index 0000000..4abbb3e
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.google.android.startop.iorap
+
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+import android.support.test.filters.SmallTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import com.google.common.truth.Truth.assertThat
+import org.junit.runners.Parameterized
+
+/**
+ * Basic unit tests to ensure that all of the [Parcelable]s in [com.google.android.startop.iorap]
+ * have a valid-conforming interface implementation.
+ */
+@SmallTest
+@RunWith(Parameterized::class)
+class ParcelablesTest<T : Parcelable>(private val inputData : InputData<T>) {
+    companion object {
+        private val initialRequestId = RequestId.nextValueForSequence()!!
+
+        @JvmStatic
+        @Parameterized.Parameters
+        fun data() = listOf(
+                InputData(
+                        newActivityInfo(),
+                        newActivityInfo(),
+                        ActivityInfo("some package", "some other activity")),
+                InputData(
+                        ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+                        ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+                        ActivityHintEvent(ActivityHintEvent.TYPE_POST_COMPLETED,
+                                newActivityInfo())),
+                InputData(
+                        AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+                                newActivityInfoOther()),
+                        AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+                                newActivityInfoOther()),
+                        AppIntentEvent.createDefaultIntentChanged(newActivityInfoOther(),
+                                newActivityInfo())),
+                InputData(
+                        PackageEvent.createReplaced(newUri(), "some package"),
+                        PackageEvent.createReplaced(newUri(), "some package"),
+                        PackageEvent.createReplaced(newUri(), "some other package")
+                ),
+                InputData(initialRequestId, cloneRequestId(initialRequestId),
+                        RequestId.nextValueForSequence()),
+                InputData(
+                        SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+                        SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+                        SystemServiceEvent(SystemServiceEvent.TYPE_START)),
+                InputData(
+                        SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+                        SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+                        SystemServiceUserEvent(SystemServiceUserEvent.TYPE_CLEANUP_USER, 12345)),
+                InputData(
+                        TaskResult(TaskResult.STATE_COMPLETED),
+                        TaskResult(TaskResult.STATE_COMPLETED),
+                        TaskResult(TaskResult.STATE_ONGOING))
+        )
+
+        private fun newActivityInfo() : ActivityInfo {
+            return ActivityInfo("some package", "some activity")
+        }
+
+        private fun newActivityInfoOther() : ActivityInfo {
+            return ActivityInfo("some package 2", "some activity 2")
+        }
+
+        private fun newUri() : Uri {
+            return Uri.parse("https://www.google.com")
+        }
+
+        private fun cloneRequestId(requestId: RequestId) : RequestId {
+            val constructor = requestId::class.java.declaredConstructors[0]
+            constructor.isAccessible = true
+            return constructor.newInstance(requestId.requestId) as RequestId
+        }
+    }
+
+    /**
+     * Test for [Object.equals] implementation.
+     */
+    @Test
+    fun testEquality() {
+        assertThat(inputData.valid).isEqualTo(inputData.valid)
+        assertThat(inputData.valid).isEqualTo(inputData.validCopy)
+        assertThat(inputData.valid).isNotEqualTo(inputData.validOther)
+    }
+
+    /**
+     * Test for [Parcelable] implementation.
+     */
+    @Test
+    fun testParcelRoundTrip() {
+        // calling writeToParcel and then T::CREATOR.createFromParcel would return the same data.
+        val assertParcels = { it : T, data : InputData<T> ->
+            val parcel = Parcel.obtain()
+            it.writeToParcel(parcel, 0)
+            parcel.setDataPosition(0) // future reads will see all previous writes.
+            assertThat(it).isEqualTo(data.createFromParcel(parcel))
+            parcel.recycle()
+        }
+
+        assertParcels(inputData.valid, inputData)
+        assertParcels(inputData.validCopy, inputData)
+        assertParcels(inputData.validOther, inputData)
+    }
+
+    data class InputData<T : Parcelable>(val valid : T, val validCopy : T, val validOther : T) {
+        val kls = valid.javaClass
+        init {
+            assertThat(valid).isNotSameAs(validCopy)
+            // Don't use isInstanceOf because of phantom warnings in intellij about Class!
+            assertThat(validCopy.javaClass).isEqualTo(valid.javaClass)
+            assertThat(validOther.javaClass).isEqualTo(valid.javaClass)
+        }
+
+        fun createFromParcel(parcel : Parcel) : T {
+            val field  = kls.getDeclaredField("CREATOR")
+            val creator = field.get(null) as Parcelable.Creator<T>
+
+            return creator.createFromParcel(parcel)
+        }
+    }
+}
diff --git a/startop/tools/view_compiler/Android.bp b/startop/tools/view_compiler/Android.bp
new file mode 100644
index 0000000..c3e9184
--- /dev/null
+++ b/startop/tools/view_compiler/Android.bp
@@ -0,0 +1,49 @@
+//
+// 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.
+//
+
+cc_library_host_static {
+    name: "libviewcompiler",
+    srcs: [
+        "java_lang_builder.cc",
+        "util.cc",
+    ],
+    static_libs: [
+        "libbase"
+    ]
+}
+
+cc_binary_host {
+    name: "viewcompiler",
+    srcs: [
+        "main.cc",
+    ],
+    static_libs: [
+        "libbase",
+        "libtinyxml2",
+        "libgflags",
+        "libviewcompiler",
+    ],
+}
+
+cc_test_host {
+    name: "view-compiler-tests",
+    srcs: [
+        "util_test.cc",
+    ],
+    static_libs: [
+        "libviewcompiler",
+    ]
+}
diff --git a/startop/tools/view_compiler/README.md b/startop/tools/view_compiler/README.md
new file mode 100644
index 0000000..5659501
--- /dev/null
+++ b/startop/tools/view_compiler/README.md
@@ -0,0 +1,25 @@
+# View Compiler
+
+This directory contains an experimental compiler for layout files.
+
+It will take a layout XML file and produce a CompiledLayout.java file with a
+specialized layout inflation function.
+
+To use it, let's assume you had a layout in `my_layout.xml` and your app was in
+the Java language package `com.example.myapp`. Run the following command:
+
+    viewcompiler my_layout.xml --package com.example.myapp --out CompiledView.java
+
+This will produce a `CompiledView.java`, which can then be compiled into your
+Android app. Then to use it, in places where you would have inflated
+`R.layouts.my_layout`, instead call `CompiledView.inflate`.
+
+Precompiling views like this generally improves the time needed to inflate them.
+
+This tool is still in its early stages and has a number of limitations.
+* Currently only one layout can be compiled at a time.
+* `merge` and `include` nodes are not supported.
+* View compilation is a manual process that requires code changes in the
+  application.
+* This only works for apps that do not use a custom layout inflater.
+* Other limitations yet to be discovered.
diff --git a/startop/tools/view_compiler/TEST_MAPPING b/startop/tools/view_compiler/TEST_MAPPING
new file mode 100644
index 0000000..cc4b17a
--- /dev/null
+++ b/startop/tools/view_compiler/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "view-compiler-tests"
+    }
+  ]
+}
diff --git a/startop/tools/view_compiler/java_lang_builder.cc b/startop/tools/view_compiler/java_lang_builder.cc
new file mode 100644
index 0000000..0b8754f
--- /dev/null
+++ b/startop/tools/view_compiler/java_lang_builder.cc
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+#include "java_lang_builder.h"
+
+#include "android-base/stringprintf.h"
+
+using android::base::StringPrintf;
+using std::string;
+
+void JavaLangViewBuilder::Start() const {
+  out_ << StringPrintf("package %s;\n", package_.c_str())
+       << "import android.content.Context;\n"
+          "import android.content.res.Resources;\n"
+          "import android.content.res.XmlResourceParser;\n"
+          "import android.util.AttributeSet;\n"
+          "import android.util.Xml;\n"
+          "import android.view.*;\n"
+          "import android.widget.*;\n"
+          "\n"
+          "public final class CompiledView {\n"
+          "\n"
+          "static <T extends View> T createView(Context context, AttributeSet attrs, View parent, "
+          "String name, LayoutInflater.Factory factory, LayoutInflater.Factory2 factory2) {"
+          "\n"
+          "  if (factory2 != null) {\n"
+          "    return (T)factory2.onCreateView(parent, name, context, attrs);\n"
+          "  } else if (factory != null) {\n"
+          "    return (T)factory.onCreateView(name, context, attrs);\n"
+          "  }\n"
+          // TODO: find a way to call the private factory
+          "  return null;\n"
+          "}\n"
+          "\n"
+          "  public static View inflate(Context context) {\n"
+          "    try {\n"
+          "      LayoutInflater inflater = LayoutInflater.from(context);\n"
+          "      LayoutInflater.Factory factory = inflater.getFactory();\n"
+          "      LayoutInflater.Factory2 factory2 = inflater.getFactory2();\n"
+          "      Resources res = context.getResources();\n"
+       << StringPrintf("      XmlResourceParser xml = res.getLayout(%s.R.layout.%s);\n",
+                       package_.c_str(),
+                       layout_name_.c_str())
+       << "      AttributeSet attrs = Xml.asAttributeSet(xml);\n"
+          // The Java-language XmlPullParser needs a call to next to find the start document tag.
+          "      xml.next(); // start document\n";
+}
+
+void JavaLangViewBuilder::Finish() const {
+  out_ << "    } catch (Exception e) {\n"
+          "      return null;\n"
+          "    }\n"  // end try
+          "  }\n"    // end inflate
+          "}\n";     // end CompiledView
+}
+
+void JavaLangViewBuilder::StartView(const string& class_name) {
+  const string view_var = MakeVar("view");
+  const string layout_var = MakeVar("layout");
+  std::string parent = "null";
+  if (!view_stack_.empty()) {
+    const StackEntry& parent_entry = view_stack_.back();
+    parent = parent_entry.view_var;
+  }
+  out_ << "      xml.next(); // <" << class_name << ">\n"
+       << StringPrintf("      %s %s = createView(context, attrs, %s, \"%s\", factory, factory2);\n",
+                       class_name.c_str(),
+                       view_var.c_str(),
+                       parent.c_str(),
+                       class_name.c_str())
+       << StringPrintf("      if (%s == null) %s = new %s(context, attrs);\n",
+                       view_var.c_str(),
+                       view_var.c_str(),
+                       class_name.c_str());
+  if (!view_stack_.empty()) {
+    out_ << StringPrintf("      ViewGroup.LayoutParams %s = %s.generateLayoutParams(attrs);\n",
+                         layout_var.c_str(),
+                         parent.c_str());
+  }
+  view_stack_.push_back({class_name, view_var, layout_var});
+}
+
+void JavaLangViewBuilder::FinishView() {
+  const StackEntry var = view_stack_.back();
+  view_stack_.pop_back();
+  if (!view_stack_.empty()) {
+    const string& parent = view_stack_.back().view_var;
+    out_ << StringPrintf("      xml.next(); // </%s>\n", var.class_name.c_str())
+         << StringPrintf("      %s.addView(%s, %s);\n",
+                         parent.c_str(),
+                         var.view_var.c_str(),
+                         var.layout_params_var.c_str());
+  } else {
+    out_ << StringPrintf("      return %s;\n", var.view_var.c_str());
+  }
+}
+
+const std::string JavaLangViewBuilder::MakeVar(std::string prefix) {
+  std::stringstream v;
+  v << prefix << view_id_++;
+  return v.str();
+}
diff --git a/startop/tools/view_compiler/java_lang_builder.h b/startop/tools/view_compiler/java_lang_builder.h
new file mode 100644
index 0000000..c8d20b2
--- /dev/null
+++ b/startop/tools/view_compiler/java_lang_builder.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+#ifndef JAVA_LANG_BUILDER_H_
+#define JAVA_LANG_BUILDER_H_
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+// Build Java language code to instantiate views.
+//
+// This has a very small interface to make it easier to generate additional
+// backends, such as a direct-to-DEX version.
+class JavaLangViewBuilder {
+ public:
+  JavaLangViewBuilder(std::string package, std::string layout_name, std::ostream& out = std::cout)
+      : package_(package), layout_name_(layout_name), out_(out) {}
+
+  // Begin generating a class. Adds the package boilerplate, etc.
+  void Start() const;
+  // Finish generating a class, closing off any open curly braces, etc.
+  void Finish() const;
+
+  // Begin creating a view (i.e. process the opening tag)
+  void StartView(const std::string& class_name);
+  // Finish a view, after all of its child nodes have been processed.
+  void FinishView();
+
+ private:
+  const std::string MakeVar(std::string prefix);
+
+  std::string const package_;
+  std::string const layout_name_;
+
+  std::ostream& out_;
+
+  size_t view_id_ = 0;
+
+  struct StackEntry {
+      // The class name for this view object
+      const std::string class_name;
+
+      // The variable name that is holding the view object
+      const std::string view_var;
+
+      // The variable name that holds the object's layout parameters
+      const std::string layout_params_var;
+  };
+  std::vector<StackEntry> view_stack_;
+};
+
+#endif  // JAVA_LANG_BUILDER_H_
diff --git a/startop/tools/view_compiler/main.cc b/startop/tools/view_compiler/main.cc
new file mode 100644
index 0000000..0ad7e24
--- /dev/null
+++ b/startop/tools/view_compiler/main.cc
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#include "gflags/gflags.h"
+
+#include "java_lang_builder.h"
+#include "util.h"
+
+#include "tinyxml2.h"
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace tinyxml2;
+using std::string;
+
+constexpr char kStdoutFilename[]{"stdout"};
+
+DEFINE_string(package, "", "The package name for the generated class (required)");
+DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
+
+namespace {
+class ViewCompilerXmlVisitor : public XMLVisitor {
+ public:
+  ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
+
+  bool VisitEnter(const XMLDocument& /*doc*/) override {
+    builder_->Start();
+    return true;
+  }
+
+  bool VisitExit(const XMLDocument& /*doc*/) override {
+    builder_->Finish();
+    return true;
+  }
+
+  bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
+    builder_->StartView(element.Name());
+    return true;
+  }
+
+  bool VisitExit(const XMLElement& /*element*/) override {
+    builder_->FinishView();
+    return true;
+  }
+
+ private:
+  JavaLangViewBuilder* builder_;
+};
+}  // end namespace
+
+int main(int argc, char** argv) {
+  constexpr size_t kProgramName = 0;
+  constexpr size_t kFileNameParam = 1;
+  constexpr size_t kNumRequiredArgs = 2;
+
+  gflags::SetUsageMessage(
+      "Compile XML layout files into equivalent Java language code\n"
+      "\n"
+      "  example usage:  viewcompiler layout.xml --package com.example.androidapp");
+  gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true);
+
+  gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package");
+  if (argc != kNumRequiredArgs || cmd.is_default) {
+    gflags::ShowUsageWithFlags(argv[kProgramName]);
+    return 1;
+  }
+
+  const char* const filename = argv[kFileNameParam];
+  const string layout_name = FindLayoutNameFromFilename(filename);
+
+  // We want to generate Java language code to inflate exactly this layout. This means
+  // generating code to walk the resource XML too.
+
+  XMLDocument xml;
+  xml.LoadFile(filename);
+
+  std::ofstream outfile;
+  if (FLAGS_out != kStdoutFilename) {
+    outfile.open(FLAGS_out);
+  }
+  JavaLangViewBuilder builder{
+      FLAGS_package, layout_name, FLAGS_out == kStdoutFilename ? std::cout : outfile};
+
+  ViewCompilerXmlVisitor visitor{&builder};
+  xml.Accept(&visitor);
+
+  return 0;
+}
\ No newline at end of file
diff --git a/startop/tools/view_compiler/util.cc b/startop/tools/view_compiler/util.cc
new file mode 100644
index 0000000..69df41d
--- /dev/null
+++ b/startop/tools/view_compiler/util.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util.h"
+
+using std::string;
+
+// TODO: see if we can borrow this from somewhere else, like aapt2.
+string FindLayoutNameFromFilename(const string& filename) {
+  size_t start = filename.rfind("/");
+  if (start == string::npos) {
+    start = 0;
+  } else {
+    start++;  // advance past '/' character
+  }
+  size_t end = filename.find(".", start);
+
+  return filename.substr(start, end - start);
+}
diff --git a/startop/tools/view_compiler/util.h b/startop/tools/view_compiler/util.h
new file mode 100644
index 0000000..03e0939
--- /dev/null
+++ b/startop/tools/view_compiler/util.h
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <string>
+
+std::string FindLayoutNameFromFilename(const std::string& filename);
+
+#endif  // UTIL_H_
diff --git a/startop/tools/view_compiler/util_test.cc b/startop/tools/view_compiler/util_test.cc
new file mode 100644
index 0000000..d1540d3
--- /dev/null
+++ b/startop/tools/view_compiler/util_test.cc
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#include "util.h"
+
+#include "gtest/gtest.h"
+
+using std::string;
+
+TEST(UtilTest, FindLayoutNameFromFilename) {
+  EXPECT_EQ("bar", ::FindLayoutNameFromFilename("foo/bar.xml"));
+  EXPECT_EQ("bar", ::FindLayoutNameFromFilename("bar.xml"));
+  EXPECT_EQ("bar", ::FindLayoutNameFromFilename("./foo/bar.xml"));
+  EXPECT_EQ("bar", ::FindLayoutNameFromFilename("/foo/bar.xml"));
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 096cf37..08bc9bc 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -141,6 +141,8 @@
      * The caller must specify the {@link #EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE} to indicate to
      * Telecom which {@link PhoneAccountHandle} the {@link Call} should be handed over to.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_REQUEST_HANDOVER =
             "android.telecom.event.REQUEST_HANDOVER";
@@ -149,6 +151,8 @@
      * Extra key used with the {@link #EVENT_REQUEST_HANDOVER} call event.  Specifies the
      * {@link PhoneAccountHandle} to which a call should be handed over to.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE =
             "android.telecom.extra.HANDOVER_PHONE_ACCOUNT_HANDLE";
@@ -161,6 +165,8 @@
      * {@link VideoProfile#STATE_BIDIRECTIONAL}, {@link VideoProfile#STATE_RX_ENABLED}, and
      * {@link VideoProfile#STATE_TX_ENABLED}.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EXTRA_HANDOVER_VIDEO_STATE =
             "android.telecom.extra.HANDOVER_VIDEO_STATE";
@@ -176,6 +182,8 @@
      * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}
      * is called to initate the handover.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EXTRA_HANDOVER_EXTRAS = "android.telecom.extra.HANDOVER_EXTRAS";
 
@@ -186,6 +194,8 @@
      * <p>
      * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_COMPLETE =
             "android.telecom.event.HANDOVER_COMPLETE";
@@ -198,6 +208,8 @@
      * <p>
      * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_SOURCE_DISCONNECTED =
             "android.telecom.event.HANDOVER_SOURCE_DISCONNECTED";
@@ -209,6 +221,8 @@
      * <p>
      * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_FAILED =
             "android.telecom.event.HANDOVER_FAILED";
@@ -434,8 +448,15 @@
          */
         public static final int PROPERTY_RTT = 0x00000400;
 
+        /**
+         * Indicates that the call has been identified as the network as an emergency call. This
+         * property may be set for both incoming and outgoing calls which the network identifies as
+         * emergency calls.
+         */
+        public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 0x00000800;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00000800
+        // Next PROPERTY value: 0x00001000
         //******************************************************************************************
 
         private final String mTelecomCallId;
@@ -601,6 +622,9 @@
             if(hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) {
                 builder.append(" PROPERTY_ASSISTED_DIALING_USED");
             }
+            if (hasProperty(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) {
+                builder.append(" PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL");
+            }
             builder.append("]");
             return builder.toString();
         }
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index 9c874bf..b906d0b 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -63,6 +63,10 @@
     /**
      * Telecom calls this method to inform the implemented {@link CallRedirectionService} of
      * a new outgoing call which is being placed.
+     *
+     * The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
+     * {@link #redirectCall(Uri, PhoneAccountHandle)}, and {@link #cancelCall()} only from here.
+     *
      * @param handle the phone number dialed by the user
      * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
      */
@@ -72,6 +76,9 @@
      * The implemented {@link CallRedirectionService} calls this method to response a request
      * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that no changes
      * are required to the outgoing call, and that the call should be placed as-is.
+     *
+     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     *
      */
     public final void placeCallUnmodified() {
         try {
@@ -84,6 +91,9 @@
      * The implemented {@link CallRedirectionService} calls this method to response a request
      * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that changes
      * are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing call.
+     *
+     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     *
      * @param handle the new phone number to dial
      * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call.
      *                           If {@code null}, no change will be made to the
@@ -100,6 +110,9 @@
      * The implemented {@link CallRedirectionService} calls this method to response a request
      * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that an outgoing
      * call should be canceled entirely.
+     *
+     * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+     *
      */
     public final void cancelCall() {
         try {
@@ -144,7 +157,6 @@
         @Override
         public void placeCall(ICallRedirectionAdapter adapter, Uri handle,
                               PhoneAccountHandle targetPhoneAccount) {
-            Log.v(this, "placeCall");
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = adapter;
             args.arg2 = handle;
@@ -154,14 +166,12 @@
     }
 
     @Override
-    public IBinder onBind(Intent intent) {
-        Log.v(this, "onBind");
+    public final IBinder onBind(Intent intent) {
         return new CallRedirectionBinder();
     }
 
     @Override
-    public boolean onUnbind(Intent intent) {
-        Log.v(this, "onUnbind");
+    public final boolean onUnbind(Intent intent) {
         return false;
     }
 }
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 024bd30..a39e885 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -22,6 +22,8 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.telecom.Connection.VideoProvider;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 import android.util.ArraySet;
 
 import java.util.ArrayList;
@@ -573,6 +575,20 @@
     }
 
     /**
+     * Updates RIL voice radio technology used for current conference after its creation.
+     *
+     * @hide
+     */
+    public void updateCallRadioTechAfterCreation() {
+        final Connection primaryConnection = getPrimaryConnection();
+        if (primaryConnection != null) {
+            setCallRadioTech(primaryConnection.getCallRadioTech());
+        } else {
+            Log.w(this, "No primary connection found while updateCallRadioTechAfterCreation");
+        }
+    }
+
+    /**
      * @hide
      * @deprecated Use {@link #setConnectionTime}.
      */
@@ -652,6 +668,37 @@
     }
 
     /**
+     * Sets RIL voice radio technology used for current conference.
+     *
+     * @param vrat the RIL voice radio technology used for current conference,
+     *             see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+     *
+     * @hide
+     */
+    public final void setCallRadioTech(@ServiceState.RilRadioTechnology int vrat) {
+        putExtra(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+                ServiceState.rilRadioTechnologyToNetworkType(vrat));
+    }
+
+    /**
+     * Returns RIL voice radio technology used for current conference.
+     *
+     * @return the RIL voice radio technology used for current conference,
+     *         see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+     *
+     * @hide
+     */
+    public final @ServiceState.RilRadioTechnology int getCallRadioTech() {
+        int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        Bundle extras = getExtras();
+        if (extras != null) {
+            voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        }
+        return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType);
+    }
+
+    /**
      * Inform this Conference that the state of its audio output has been changed externally.
      *
      * @param state The new audio state.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 5d5b15d..ee1ca5f 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -38,6 +38,8 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 import android.util.ArraySet;
 import android.view.Surface;
 
@@ -411,6 +413,13 @@
      */
     public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
 
+    /**
+     * Set by the framework to indicate that the network has identified a Connection as an emergency
+     * call.
+     * @hide
+     */
+    public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1 << 10;
+
     //**********************************************************************************************
     // Next PROPERTY value: 1<<10
     //**********************************************************************************************
@@ -584,6 +593,8 @@
      * {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has
      * successfully completed.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_COMPLETE =
             "android.telecom.event.HANDOVER_COMPLETE";
@@ -593,6 +604,8 @@
      * {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has failed
      * to complete.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_FAILED =
             "android.telecom.event.HANDOVER_FAILED";
@@ -800,6 +813,10 @@
             builder.append(isLong ? " PROPERTY_IS_RTT" : " rtt");
         }
 
+        if (can(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) {
+            builder.append(isLong ? " PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL" : " ecall");
+        }
+
         builder.append("]");
         return builder.toString();
     }
@@ -1879,6 +1896,24 @@
     }
 
     /**
+     * Returns RIL voice radio technology used for current connection.
+     *
+     * @return the RIL voice radio technology used for current connection,
+     *         see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+     *
+     * @hide
+     */
+    public final @ServiceState.RilRadioTechnology int getCallRadioTech() {
+        int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        Bundle extras = getExtras();
+        if (extras != null) {
+            voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        }
+        return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType);
+    }
+
+    /**
      * @return The status hints for this connection.
      */
     public final StatusHints getStatusHints() {
@@ -2312,6 +2347,26 @@
     }
 
     /**
+     * Sets RIL voice radio technology used for current connection.
+     *
+     * @param vrat the RIL Voice Radio Technology used for current connection,
+     *             see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+     *
+     * @hide
+     */
+    public final void setCallRadioTech(@ServiceState.RilRadioTechnology int vrat) {
+        putExtra(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+                ServiceState.rilRadioTechnologyToNetworkType(vrat));
+        // Propagates the call radio technology to its parent {@link android.telecom.Conference}
+        // This action only covers non-IMS CS conference calls.
+        // For IMS PS call conference call, it can be updated via its host connection
+        // {@link #Listener.onExtrasChanged} event.
+        if (getConference() != null) {
+            getConference().setCallRadioTech(vrat);
+        }
+    }
+
+    /**
      * Sets the label and icon status to display in the in-call UI.
      *
      * @param statusHints The status label and icon to set.
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..d33a537 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -317,6 +317,15 @@
             "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
 
     /**
+     * Optional extra for communicating the call network technology used by a
+     * {@link android.telecom.Connection} to Telecom and InCallUI.
+     *
+     * @see {@code NETWORK_TYPE_*} in {@link android.telephony.TelephonyManager}.
+     */
+    public static final String EXTRA_CALL_NETWORK_TYPE =
+            "android.telecom.extra.CALL_NETWORK_TYPE";
+
+    /**
      * An optional {@link android.content.Intent#ACTION_CALL} intent extra denoting the
      * package name of the app specifying an alternative gateway for the call.
      * The value is a string.
@@ -622,6 +631,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.
      */
@@ -1863,6 +1884,43 @@
         }
     }
 
+    /**
+     * Determines if there is an ongoing emergency call.  This can be either an outgoing emergency
+     * call, as identified by the dialed number, or because a call was identified by the network
+     * as an emergency call.
+     * @return {@code true} if there is an ongoing emergency call, {@code false} otherwise.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean isInEmergencyCall() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecomService().isInEmergencyCall();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException isInEmergencyCall: " + e);
+            return false;
+        }
+        return false;
+    }
+
+    /**
+     * Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
+     * @param intent The {@link Intent#ACTION_CALL} intent to handle.
+     * @hide
+     */
+    public void handleCallIntent(Intent intent) {
+        try {
+            if (isServiceConnected()) {
+                getTelecomService().handleCallIntent(intent);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException handleCallIntent: " + e);
+        }
+
+    }
+
     private ITelecomService getTelecomService() {
         if (mTelecomServiceOverride != null) {
             return mTelecomServiceOverride;
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index b4e7d56..df7d683 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -279,4 +279,14 @@
      * @see TelecomServiceImpl#acceptHandover
      */
     void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct);
+
+    /**
+     * @see TelecomServiceImpl#isInEmergencyCall
+     */
+    boolean isInEmergencyCall();
+
+    /**
+     * @see TelecomServiceImpl#handleCallIntent
+     */
+    void handleCallIntent(in Intent intent);
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9319750..6eaecc6 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -271,6 +271,14 @@
             KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
 
     /**
+     * Do only allow auto selection in Advanced Network Settings when in home network.
+     * Manual selection is allowed when in roaming network.
+     * @hide
+     */
+    public static final String
+            KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network";
+
+    /**
      * Control whether users receive a simplified network settings UI and improved network
      * selection.
      */
@@ -1070,6 +1078,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 +1095,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
@@ -1606,11 +1633,21 @@
      * When {@code false}, use default title for Enhanced 4G LTE Mode settings.
      * When {@code true}, use the variant.
      * @hide
+     * @deprecated use {@link #KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT}.
      */
+    @Deprecated
     public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL =
             "enhanced_4g_lte_title_variant_bool";
 
     /**
+     * The index indicates the carrier specified title string of Enahnce 4G LTE Mode settings.
+     * Default value is 0, which indicates the default title string.
+     * @hide
+     */
+    public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT =
+            "enhanced_4g_lte_title_variant_int";
+
+    /**
      * Indicates whether the carrier wants to notify the user when handover of an LTE video call to
      * WIFI fails.
      * <p>
@@ -1947,6 +1984,17 @@
             "skip_cf_fail_to_disable_dialog_bool";
 
     /**
+     * Flag specifying whether operator supports including no reply condition timer option on
+     * CFNRy (3GPP TS 24.082 3: Call Forwarding on No Reply) in the call forwarding settings UI.
+     * {@code true}  - include no reply condition timer option on CFNRy
+     * {@code false} - don't include no reply condition timer option on CFNRy
+     *
+     * @hide
+     */
+    public static final String KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL =
+            "support_no_reply_timer_for_cfnry_bool";
+
+    /**
      * List of the FAC (feature access codes) to dial as a normal call.
      * @hide
      */
@@ -2086,6 +2134,16 @@
     public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING =
             "call_redirection_service_component_name_string";
 
+    /**
+     * Flag specifying whether to show notification(call blocking disabled) when Enhanced Call
+     * Blocking(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL) is enabled and making emergency call.
+     * When true, notification is shown always.
+     * When false, notification is shown only when any setting of "Enhanced Blocked number" is
+     * enabled.
+     */
+    public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL =
+            "show_call_blocking_disabled_notification_always_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -2142,6 +2200,7 @@
         sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
         sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+        sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false);
         sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
 
@@ -2264,6 +2323,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 +2333,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);
@@ -2370,6 +2430,7 @@
 
         sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false);
+        sDefaults.putInt(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT, 0);
         sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false);
         sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
@@ -2399,6 +2460,7 @@
         sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
         sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
@@ -2422,6 +2484,7 @@
                 });
         sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "");
         sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index ee5cdc2..d7169b2 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -319,6 +319,29 @@
      */
     public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71;
 
+    /**
+     * Indicates that a new outgoing call cannot be placed because there is already an outgoing
+     * call dialing out.
+     */
+    public static final int ALREADY_DIALING = 72;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed while there is a ringing call.
+     */
+    public static final int CANT_CALL_WHILE_RINGING = 73;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed because calling has been disabled using
+     * the ro.telephony.disable-call system property.
+     */
+    public static final int CALLING_DISABLED = 74;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed because there is currently an ongoing
+     * foreground and background call.
+     */
+    public static final int TOO_MANY_ONGOING_CALLS = 75;
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Update toString() with the newly added disconnect type.
@@ -474,6 +497,14 @@
             return "NORMAL_UNSPECIFIED";
         case IMS_SIP_ALTERNATE_EMERGENCY_CALL:
             return "IMS_SIP_ALTERNATE_EMERGENCY_CALL";
+        case ALREADY_DIALING:
+            return "ALREADY_DIALING";
+        case CANT_CALL_WHILE_RINGING:
+            return "CANT_CALL_WHILE_RINGING";
+        case CALLING_DISABLED:
+            return "CALLING_DISABLED";
+        case TOO_MANY_ONGOING_CALLS:
+            return "TOO_MANY_ONGOING_CALLS";
         default:
             return "INVALID: " + cause;
         }
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
new file mode 100644
index 0000000..e373797
--- /dev/null
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.GroupCall;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.InternalGroupCallCallback;
+import android.telephony.mbms.InternalGroupCallSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.vendor.IMbmsGroupCallService;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This class provides functionality for accessing group call functionality over MBMS.
+ */
+public class MbmsGroupCallSession implements AutoCloseable {
+    private static final String LOG_TAG = "MbmsGroupCallSession";
+
+    /**
+     * Service action which must be handled by the middleware implementing the MBMS group call
+     * interface.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String MBMS_GROUP_CALL_SERVICE_ACTION =
+            "android.telephony.action.EmbmsGroupCall";
+
+    /**
+     * Metadata key that specifies the component name of the service to bind to for group calls.
+     * @hide
+     */
+    @TestApi
+    public static final String MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA =
+            "mbms-group-call-service-override";
+
+    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+    private AtomicReference<IMbmsGroupCallService> mService = new AtomicReference<>(null);
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            sIsInitialized.set(false);
+            mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                    "Received death notification");
+        }
+    };
+
+    private InternalGroupCallSessionCallback mInternalCallback;
+    private Set<GroupCall> mKnownActiveGroupCalls = new ArraySet<>();
+
+    private final Context mContext;
+    private int mSubscriptionId;
+
+    /** @hide */
+    private MbmsGroupCallSession(Context context, Executor executor, int subscriptionId,
+            MbmsGroupCallSessionCallback callback) {
+        mContext = context;
+        mSubscriptionId = subscriptionId;
+        mInternalCallback = new InternalGroupCallSessionCallback(callback, executor);
+    }
+
+    /**
+     * Create a new {@link MbmsGroupCallSession} using the given subscription ID.
+     *
+     * You may only have one instance of {@link MbmsGroupCallSession} per UID. If you call this
+     * method while there is an active instance of {@link MbmsGroupCallSession} in your process
+     * (in other words, one that has not had {@link #close()} called on it), this method will
+     * throw an {@link IllegalStateException}. If you call this method in a different process
+     * running under the same UID, an error will be indicated via
+     * {@link MbmsGroupCallSessionCallback#onError(int, String)}.
+     *
+     * Note that initialization may fail asynchronously. If you wish to try again after you
+     * receive such an asynchronous error, you must call {@link #close()} on the instance of
+     * {@link MbmsGroupCallSession} that you received before calling this method again.
+     *
+     * @param context The {@link Context} to use.
+     * @param executor The executor on which you wish to execute callbacks.
+     * @param subscriptionId The subscription ID to use.
+     * @param callback A callback object on which you wish to receive results of asynchronous
+     *                 operations.
+     * @return An instance of {@link MbmsGroupCallSession}, or null if an error occurred.
+     */
+    public static @Nullable MbmsGroupCallSession create(@NonNull Context context,
+            @NonNull Executor executor, int subscriptionId,
+            final @NonNull MbmsGroupCallSessionCallback callback) {
+        if (!sIsInitialized.compareAndSet(false, true)) {
+            throw new IllegalStateException("Cannot create two instances of MbmsGroupCallSession");
+        }
+        MbmsGroupCallSession session = new MbmsGroupCallSession(context, executor,
+                subscriptionId, callback);
+
+        final int result = session.bindAndInitialize();
+        if (result != MbmsErrors.SUCCESS) {
+            sIsInitialized.set(false);
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(result, null);
+                }
+            });
+            return null;
+        }
+        return session;
+    }
+
+    /**
+     * Create a new {@link MbmsGroupCallSession} using the system default data subscription ID.
+     * See {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+     */
+    public static MbmsGroupCallSession create(@NonNull Context context,
+            @NonNull Executor executor, @NonNull MbmsGroupCallSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
+    }
+
+    /**
+     * Terminates this instance. Also terminates
+     * any group calls spawned from this instance as if
+     * {@link GroupCall#close()} had been called on them. After this method returns,
+     * no further callbacks originating from the middleware will be enqueued on the provided
+     * instance of {@link MbmsGroupCallSessionCallback}, but callbacks that have already been
+     * enqueued will still be delivered.
+     *
+     * It is safe to call {@link #create(Context, Executor, int, MbmsGroupCallSessionCallback)} to
+     * obtain another instance of {@link MbmsGroupCallSession} immediately after this method
+     * returns.
+     *
+     * May throw an {@link IllegalStateException}
+     */
+    public void close() {
+        try {
+            IMbmsGroupCallService groupCallService = mService.get();
+            if (groupCallService == null) {
+                // Ignore and return, assume already disposed.
+                return;
+            }
+            groupCallService.dispose(mSubscriptionId);
+            for (GroupCall s : mKnownActiveGroupCalls) {
+                s.getCallback().stop();
+            }
+            mKnownActiveGroupCalls.clear();
+        } catch (RemoteException e) {
+            // Ignore for now
+        } finally {
+            mService.set(null);
+            sIsInitialized.set(false);
+            mInternalCallback.stop();
+        }
+    }
+
+    /**
+     * Starts the requested group call, reporting status to the indicated callback.
+     * Returns an object used to control that call.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * Asynchronous errors through the callback include any of the errors in
+     * {@link MbmsErrors.GeneralErrors}.
+     *
+     * @param executor The executor on which you wish to execute callbacks for this stream.
+     * @param tmgi The TMGI, an identifier for the group call you want to join.
+     * @param saiArray An array of SAIs for the group call that should be negotiated separately with
+     *                the carrier.
+     * @param frequencyArray An array of frequencies for the group call that should be negotiated
+     *                separately with the carrier.
+     * @param callback The callback that you want to receive information about the call on.
+     * @return An instance of {@link GroupCall} through which the call can be controlled.
+     *         May be {@code null} if an error occurred.
+     */
+    public @Nullable GroupCall startGroupCall(@NonNull Executor executor, long tmgi, int[] saiArray,
+            int[] frequencyArray, @NonNull GroupCallCallback callback) {
+        IMbmsGroupCallService groupCallService = mService.get();
+        if (groupCallService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalGroupCallCallback serviceCallback = new InternalGroupCallCallback(
+                callback, executor);
+
+        GroupCall serviceForApp = new GroupCall(mSubscriptionId,
+                groupCallService, this, tmgi, serviceCallback);
+        mKnownActiveGroupCalls.add(serviceForApp);
+
+        try {
+            int returnCode = groupCallService.startGroupCall(
+                    mSubscriptionId, tmgi, saiArray, frequencyArray, serviceCallback);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (returnCode != MbmsErrors.SUCCESS) {
+                mInternalCallback.onError(returnCode, null);
+                return null;
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sIsInitialized.set(false);
+            mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return null;
+        }
+
+        return serviceForApp;
+    }
+
+    /** @hide */
+    public void onGroupCallStopped(GroupCall service) {
+        mKnownActiveGroupCalls.remove(service);
+    }
+
+    private int bindAndInitialize() {
+        return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION,
+                new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        IMbmsGroupCallService groupCallService =
+                                IMbmsGroupCallService.Stub.asInterface(service);
+                        int result;
+                        try {
+                            result = groupCallService.initialize(mInternalCallback,
+                                    mSubscriptionId);
+                        } catch (RemoteException e) {
+                            Log.e(LOG_TAG, "Service died before initialization");
+                            mInternalCallback.onError(
+                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                                    e.toString());
+                            sIsInitialized.set(false);
+                            return;
+                        } catch (RuntimeException e) {
+                            Log.e(LOG_TAG, "Runtime exception during initialization");
+                            mInternalCallback.onError(
+                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                                    e.toString());
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        if (result == MbmsErrors.UNKNOWN) {
+                            // Unbind and throw an obvious error
+                            close();
+                            throw new IllegalStateException("Middleware must not return"
+                                    + " an unknown error code");
+                        }
+                        if (result != MbmsErrors.SUCCESS) {
+                            mInternalCallback.onError(result,
+                                    "Error returned during initialization");
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        try {
+                            groupCallService.asBinder().linkToDeath(mDeathRecipient, 0);
+                        } catch (RemoteException e) {
+                            mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                                    "Middleware lost during initialization");
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        mService.set(groupCallService);
+                    }
+
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        sIsInitialized.set(false);
+                        mService.set(null);
+                    }
+                });
+    }
+}
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 8e99518..79298fd 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -32,7 +32,12 @@
 /**
  * Represents the neighboring cell information, including
  * Received Signal Strength and Cell ID location.
+ *
+ * @deprecated This class should not be used by any app targeting
+ *     {@link Build.VERSION_CODES.Q Android Q} or higher. Instead callers should use
+ *     {@Link android.telephony.CellInfo CellInfo}.
  */
+@Deprecated
 public class NeighboringCellInfo implements Parcelable
 {
     /**
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index 7c7d7a0..202da68 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -16,11 +16,10 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.annotation.IntDef;
-import android.util.Log;
 
 import com.android.internal.telephony.ITelephony;
 
@@ -113,6 +112,8 @@
         }
         try {
             telephony.stopNetworkScan(mSubId, mScanId);
+        } catch (IllegalArgumentException ex) {
+            Rlog.d(TAG,  "stopNetworkScan - no active scan for ScanID=" + mScanId);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "stopNetworkScan  RemoteException", ex);
         } catch (RuntimeException ex) {
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index f2b73dc..7469186 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -24,6 +24,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.NetworkRegistrationState.Domain;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
@@ -1595,7 +1596,7 @@
     /**
      * Get all of the available network registration states.
      *
-     * @return List of registration states
+     * @return List of {@link NetworkRegistrationState}
      * @hide
      */
     @SystemApi
@@ -1606,14 +1607,30 @@
     }
 
     /**
-     * Get the network registration states with given transport type.
+     * Get the network registration states for the transport type.
      *
-     * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
-     * @return List of registration states.
+     * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+     * @return List of {@link NetworkRegistrationState}
+     * @hide
+     *
+     * @deprecated Use {@link #getNetworkRegistrationStatesFromTransportType(int)}
+     */
+    @Deprecated
+    @SystemApi
+    public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+        return getNetworkRegistrationStatesForTransportType(transportType);
+    }
+
+    /**
+     * Get the network registration states for the transport type.
+     *
+     * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+     * @return List of {@link NetworkRegistrationState}
      * @hide
      */
     @SystemApi
-    public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+    public List<NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(
+            int transportType) {
         List<NetworkRegistrationState> list = new ArrayList<>();
 
         synchronized (mNetworkRegistrationStates) {
@@ -1628,16 +1645,57 @@
     }
 
     /**
-     * Get the network registration states with given transport type and domain.
+     * Get the network registration states for the network domain.
      *
-     * @param domain The network domain. Must be {@link NetworkRegistrationState#DOMAIN_CS} or
-     * {@link NetworkRegistrationState#DOMAIN_PS}.
-     * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
-     * @return The matching NetworkRegistrationState.
+     * @param domain The network {@link NetworkRegistrationState.Domain domain}
+     * @return List of {@link NetworkRegistrationState}
      * @hide
      */
     @SystemApi
-    public NetworkRegistrationState getNetworkRegistrationStates(int domain, int transportType) {
+    public List<NetworkRegistrationState> getNetworkRegistrationStatesForDomain(
+            @Domain int domain) {
+        List<NetworkRegistrationState> list = new ArrayList<>();
+
+        synchronized (mNetworkRegistrationStates) {
+            for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+                if (networkRegistrationState.getDomain() == domain) {
+                    list.add(networkRegistrationState);
+                }
+            }
+        }
+
+        return list;
+    }
+
+    /**
+     * Get the network registration state for the transport type and network domain.
+     *
+     * @param domain The network {@link NetworkRegistrationState.Domain domain}
+     * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+     * @return The matching {@link NetworkRegistrationState}
+     * @hide
+     *
+     * @deprecated Use {@link #getNetworkRegistrationState(int, int)}
+     */
+    @Deprecated
+    @SystemApi
+    public NetworkRegistrationState getNetworkRegistrationStates(@Domain int domain,
+                                                                 int transportType) {
+        return getNetworkRegistrationState(domain, transportType);
+    }
+
+    /**
+     * Get the network registration state for the transport type and network domain.
+     *
+     * @param domain The network {@link NetworkRegistrationState.Domain domain}
+     * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+     * @return The matching {@link NetworkRegistrationState}
+     * @hide
+     *
+     */
+    @SystemApi
+    public NetworkRegistrationState getNetworkRegistrationState(@Domain int domain,
+                                                                int transportType) {
         synchronized (mNetworkRegistrationStates) {
             for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
                 if (networkRegistrationState.getTransportType() == transportType
@@ -1669,5 +1727,4 @@
             mNetworkRegistrationStates.add(regState);
         }
     }
-
 }
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index ec26622..22c1e58 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -33,11 +33,13 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A Parcelable class for Subscription Information.
@@ -552,11 +554,49 @@
         String cardIdToPrint = givePrintableIccid(mCardId);
         return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
                 + " displayName=" + mDisplayName + " carrierName=" + mCarrierName
-                + " nameSource=" + mNameSource + " iconTint=" + mIconTint
+                + " nameSource=" + mNameSource + " iconTint=" + mIconTint + " mNumber=" + mNumber
                 + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
-                + " mnc " + mMnc + " isEmbedded " + mIsEmbedded
+                + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
                 + " accessRules " + Arrays.toString(mAccessRules)
                 + " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
                 + " parentSubId=" + mParentSubId + "}";
     }
-}
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
+                mIsOpportunistic, mParentSubId, mIccId, mNumber, mMcc, mMnc, mCountryIso,
+                mCardId, mDisplayName, mCarrierName, mAccessRules);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) return false;
+        if (obj == this) return true;
+
+        SubscriptionInfo toCompare;
+        try {
+            toCompare = (SubscriptionInfo) obj;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        return mId == toCompare.mId
+                && mSimSlotIndex == toCompare.mSimSlotIndex
+                && mNameSource == toCompare.mNameSource
+                && mIconTint == toCompare.mIconTint
+                && mDataRoaming == toCompare.mDataRoaming
+                && mIsEmbedded == toCompare.mIsEmbedded
+                && mIsOpportunistic == toCompare.mIsOpportunistic
+                && mParentSubId == toCompare.mParentSubId
+                && Objects.equals(mIccId, toCompare.mIccId)
+                && Objects.equals(mNumber, toCompare.mNumber)
+                && Objects.equals(mMcc, toCompare.mMcc)
+                && Objects.equals(mMnc, toCompare.mMnc)
+                && Objects.equals(mCountryIso, toCompare.mCountryIso)
+                && Objects.equals(mCardId, toCompare.mCardId)
+                && TextUtils.equals(mDisplayName, toCompare.mDisplayName)
+                && TextUtils.equals(mCarrierName, toCompare.mCarrierName)
+                && Arrays.equals(mAccessRules, toCompare.mAccessRules);
+    }
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 45afde6..ad16901 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;
 
@@ -1821,6 +1822,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);
 
@@ -1832,6 +1846,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);
@@ -1839,6 +1858,19 @@
     }
 
     /**
+     * Checks if the supplied subscription ID corresponds to an active subscription.
+     *
+     * @param subscriptionId the subscription ID.
+     * @return {@code true} if the supplied subscription ID corresponds to an active subscription;
+     * {@code false} if it does not correspond to an active subscription; or throw a
+     * SecurityException if the caller hasn't got the right permission.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public boolean isActiveSubscriptionId(int subscriptionId) {
+        return isActiveSubId(subscriptionId);
+    }
+
+    /**
      * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription
      * and the SIM providing the subscription is present in a slot and in "LOADED" state.
      * @hide
@@ -1848,7 +1880,7 @@
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                return iSub.isActiveSubId(subId);
+                return iSub.isActiveSubId(subId, mContext.getOpPackageName());
             }
         } catch (RemoteException ex) {
         }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index dab0511..bba4868 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import static android.content.Context.TELECOM_SERVICE;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.IntDef;
@@ -67,6 +69,7 @@
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.TelephonyProperties;
@@ -134,6 +137,22 @@
         static final int NEVER_USE = 2;
     }
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"NETWORK_SELECTION_MODE_"},
+            value = {
+                    NETWORK_SELECTION_MODE_UNKNOWN,
+                    NETWORK_SELECTION_MODE_AUTO,
+                    NETWORK_SELECTION_MODE_MANUAL})
+    public @interface NetworkSelectionMode {}
+
+    /** @hide */
+    public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0;
+    /** @hide */
+    public static final int NETWORK_SELECTION_MODE_AUTO = 1;
+    /** @hide */
+    public static final int NETWORK_SELECTION_MODE_MANUAL = 2;
+
     /** The otaspMode passed to PhoneStateListener#onOtaspChanged */
     /** @hide */
     static public final int OTASP_UNINITIALIZED = 0;
@@ -147,7 +166,6 @@
     /** @hide */
     static public final int OTASP_SIM_UNPROVISIONED = 5;
 
-
     /** @hide */
     static public final int KEY_TYPE_EPDG = 1;
 
@@ -1482,23 +1500,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;
         }
     }
 
@@ -1559,6 +1578,7 @@
      *
      * @return List of NeighboringCellInfo or null if info unavailable.
      *
+     * @removed
      * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
      *             from NeighboringCellInfo, including LTE cell information.
      */
@@ -2071,10 +2091,37 @@
 
     /** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
     public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA;
+
+    /** @hide */
+    @IntDef({
+            NETWORK_TYPE_UNKNOWN,
+            NETWORK_TYPE_GPRS,
+            NETWORK_TYPE_EDGE,
+            NETWORK_TYPE_UMTS,
+            NETWORK_TYPE_CDMA,
+            NETWORK_TYPE_EVDO_0,
+            NETWORK_TYPE_EVDO_A,
+            NETWORK_TYPE_1xRTT,
+            NETWORK_TYPE_HSDPA,
+            NETWORK_TYPE_HSUPA,
+            NETWORK_TYPE_HSPA,
+            NETWORK_TYPE_IDEN,
+            NETWORK_TYPE_EVDO_B,
+            NETWORK_TYPE_LTE,
+            NETWORK_TYPE_EHRPD,
+            NETWORK_TYPE_HSPAP,
+            NETWORK_TYPE_GSM,
+            NETWORK_TYPE_TD_SCDMA,
+            NETWORK_TYPE_IWLAN,
+            NETWORK_TYPE_LTE_CA,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NetworkType{}
+
     /**
      * @return the NETWORK_TYPE_xxxx for current data connection.
      */
-    public int getNetworkType() {
+    public @NetworkType int getNetworkType() {
        try {
            ITelephony telephony = getITelephony();
            if (telephony != null) {
@@ -2119,24 +2166,24 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-   @UnsupportedAppUsage
-   public int getNetworkType(int subId) {
-       try {
-           ITelephony telephony = getITelephony();
-           if (telephony != null) {
-               return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName());
-           } else {
-               // This can happen when the ITelephony interface is not up yet.
-               return NETWORK_TYPE_UNKNOWN;
-           }
-       } catch(RemoteException ex) {
-           // This shouldn't happen in the normal case
-           return NETWORK_TYPE_UNKNOWN;
-       } catch (NullPointerException ex) {
-           // This could happen before phone restarts due to crashing
-           return NETWORK_TYPE_UNKNOWN;
-       }
-   }
+    @UnsupportedAppUsage
+    public int getNetworkType(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getNetworkTypeForSubscriber(subId, getOpPackageName());
+            } else {
+                // This can happen when the ITelephony interface is not up yet.
+                return NETWORK_TYPE_UNKNOWN;
+            }
+        } catch (RemoteException ex) {
+            // This shouldn't happen in the normal case
+            return NETWORK_TYPE_UNKNOWN;
+        } catch (NullPointerException ex) {
+            // This could happen before phone restarts due to crashing
+            return NETWORK_TYPE_UNKNOWN;
+        }
+    }
 
     /**
      * Returns a constant indicating the radio technology (network type)
@@ -2169,7 +2216,7 @@
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public int getDataNetworkType() {
+    public @NetworkType int getDataNetworkType() {
         return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
     }
 
@@ -2209,7 +2256,7 @@
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public int getVoiceNetworkType() {
+    public @NetworkType int getVoiceNetworkType() {
         return getVoiceNetworkType(getSubId());
     }
 
@@ -2866,7 +2913,7 @@
      * of time the mode may be unknown.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
      * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
@@ -4121,11 +4168,16 @@
     }
 
     /**
-     * Returns the IMS home network domain name that was loaded from the ISIM.
-     * @return the IMS domain name, or null if not present or not loaded
+     * Returns the IMS home network domain name that was loaded from the ISIM {@see #APPTYPE_ISIM}.
+     * @return the IMS domain name. Returns {@code null} if ISIM hasn't been loaded or IMS domain
+     * hasn't been loaded or isn't present on the ISIM.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getIsimDomain() {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
@@ -4353,7 +4405,7 @@
     * @hide
     */
     private ITelecomService getTelecomService() {
-        return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
+        return ITelecomService.Stub.asInterface(ServiceManager.getService(TELECOM_SERVICE));
     }
 
     private ITelephonyRegistry getTelephonyRegistry() {
@@ -5379,7 +5431,19 @@
         }
     }
 
-    // ICC SIM Application Types
+    /**
+     * UICC SIM Application Types
+     * @hide
+     */
+    @IntDef(prefix = { "APPTYPE_" }, value = {
+            APPTYPE_SIM,
+            APPTYPE_USIM,
+            APPTYPE_RUIM,
+            APPTYPE_CSIM,
+            APPTYPE_ISIM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UiccAppType{}
     /** UICC application type is SIM */
     public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
     /** UICC application type is USIM */
@@ -5390,6 +5454,7 @@
     public static final int APPTYPE_CSIM = PhoneConstants.APPTYPE_CSIM;
     /** UICC application type is ISIM */
     public static final int APPTYPE_ISIM = PhoneConstants.APPTYPE_ISIM;
+
     // authContext (parameter P2) when doing UICC challenge,
     // per 3GPP TS 31.102 (Section 7.1.2)
     /** Authentication type for UICC challenge is EAP SIM. See RFC 4186 for details. */
@@ -5641,23 +5706,6 @@
     }
 
     /**
-     * @return true if the IMS resolver is busy resolving a binding and should not be considered
-     * available, false if the IMS resolver is idle.
-     * @hide
-     */
-    public boolean isResolvingImsBinding() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                return telephony.isResolvingImsBinding();
-            }
-        } catch (RemoteException e) {
-            Rlog.e(TAG, "isResolvingImsBinding, RemoteException: " + e.getMessage());
-        }
-        return false;
-    }
-
-    /**
      * Set IMS registration state
      *
      * @param Registration state
@@ -5673,6 +5721,203 @@
         }
     }
 
+    /** @hide */
+    @IntDef(prefix = { "NETWORK_MODE_" }, value = {
+            NETWORK_MODE_WCDMA_PREF,
+            NETWORK_MODE_GSM_ONLY,
+            NETWORK_MODE_WCDMA_ONLY,
+            NETWORK_MODE_GSM_UMTS,
+            NETWORK_MODE_CDMA_EVDO,
+            NETWORK_MODE_CDMA_NO_EVDO,
+            NETWORK_MODE_EVDO_NO_CDMA,
+            NETWORK_MODE_GLOBAL,
+            NETWORK_MODE_LTE_CDMA_EVDO,
+            NETWORK_MODE_LTE_GSM_WCDMA,
+            NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_LTE_ONLY,
+            NETWORK_MODE_LTE_WCDMA,
+            NETWORK_MODE_TDSCDMA_ONLY,
+            NETWORK_MODE_TDSCDMA_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA,
+            NETWORK_MODE_TDSCDMA_GSM,
+            NETWORK_MODE_LTE_TDSCDMA_GSM,
+            NETWORK_MODE_TDSCDMA_GSM_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA,
+            NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PrefNetworkMode{}
+
+    /**
+     * network mode is GSM/WCDMA (WCDMA preferred).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF;
+
+    /**
+     * network mode is GSM only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY;
+
+    /**
+     * network mode is WCDMA only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+
+    /**
+     * network mode is GSM/WCDMA (auto mode, according to PRL).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS;
+
+    /**
+     * network mode is CDMA and EvDo (auto mode, according to PRL).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA;
+
+    /**
+     * network mode is CDMA only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+
+    /**
+     * network mode is EvDo only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+
+    /**
+     * network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL;
+
+    /**
+     * network mode is LTE, CDMA and EvDo.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
+
+    /**
+     * preferred network mode is LTE, GSM/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
+
+    /**
+     * network mode is LTE, CDMA, EvDo, GSM/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
+
+    /**
+     * network mode is LTE Only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY;
+
+    /**
+     * network mode is LTE/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_WCDMA = RILConstants.NETWORK_MODE_LTE_WCDMA;
+
+    /**
+     * network mode is TD-SCDMA only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_ONLY = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
+
+    /**
+     * network mode is TD-SCDMA and WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
+
+    /**
+     * network mode is TD-SCDMA and LTE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
+
+    /**
+     * network mode is TD-SCDMA and GSM.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_GSM = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
+
+    /**
+     * network mode is TD-SCDMA,GSM and LTE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA_GSM =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
+
+    /**
+     * network mode is TD-SCDMA, GSM/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
+
+    /**
+     * network mode is TD-SCDMA, WCDMA and LTE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
+
+    /**
+     * network mode is TD-SCDMA, GSM/WCDMA and LTE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
+
+    /**
+     * network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+
+    /**
+     * network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+
     /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
@@ -5681,11 +5926,12 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
-     * @return the preferred network type, defined in RILConstants.java.
+     * @return the preferred network type.
      * @hide
      */
-    @UnsupportedAppUsage
-    public int getPreferredNetworkType(int subId) {
+    @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
+    @SystemApi
+    public @PrefNetworkMode int getPreferredNetworkType(int subId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
@@ -5702,7 +5948,7 @@
      * Sets the network selection mode to automatic.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5727,7 +5973,7 @@
      * Perform a radio scan and return the list of available networks.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p> Note that this scan can take a long time (sometimes minutes) to happen.
      *
@@ -5806,7 +6052,7 @@
      * Ask the radio to connect to the input network and change selection mode to manual.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5816,25 +6062,75 @@
      * @param persistSelection whether the selection will persist until reboot. If true, only allows
      * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
      * normal network selection next time.
-     * @return true on success; false on any failure.
+     * @return {@code true} on success; {@code false} on any failure.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
+        return setNetworkSelectionModeManual(
+                new OperatorInfo(
+                        "" /* operatorAlphaLong */, "" /* operatorAlphaShort */, operatorNumeric),
+                persistSelection);
+    }
+
+    /**
+     * Ask the radio to connect to the input network and change selection mode to manual.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param operatorInfo included the PLMN id, long name, short name of the operator to attach to.
+     * @param persistSelection whether the selection will persist until reboot. If true, only allows
+     * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+     * normal network selection next time.
+     * @return {@code true} on success; {@code true} on any failure.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean setNetworkSelectionModeManual(
+            OperatorInfo operatorInfo, boolean persistSelection) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.setNetworkSelectionModeManual(
-                        getSubId(), operatorNumeric, persistSelection);
+                        getSubId(), operatorInfo, persistSelection);
             }
         } catch (RemoteException ex) {
             Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex);
-        } catch (NullPointerException ex) {
-            Rlog.e(TAG, "setNetworkSelectionModeManual NPE", ex);
         }
         return false;
     }
 
+   /**
+     * Get the network selection mode.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+
+     * @return the network selection mode.
+     *
+     * @hide
+     */
+    @NetworkSelectionMode
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public int getNetworkSelectionMode() {
+        int mode = NETWORK_SELECTION_MODE_UNKNOWN;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                mode = telephony.getNetworkSelectionMode(getSubId());
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getNetworkSelectionMode RemoteException", ex);
+        }
+        return mode;
+    }
+
     /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
@@ -6217,57 +6513,42 @@
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
+     * @removed Use {@link android.telecom.TelecomManager#endCall()} instead.
      * @hide
+     * @removed
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.CALL_PHONE)
     public boolean endCall() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.endCall();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#endCall", e);
-        }
         return false;
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
+     * @removed Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
      * @hide
+     * @removed
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void answerRingingCall() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.answerRingingCall();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#answerRingingCall", e);
-        }
+        // No-op
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#silenceRinger} instead
+     * @removed Use {@link android.telecom.TelecomManager#silenceRinger} instead
      * @hide
      */
     @Deprecated
     @SystemApi
     @SuppressLint("Doclava125")
     public void silenceRinger() {
-        try {
-            getTelecomService().silenceRinger(getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelecomService#silenceRinger", e);
-        }
+        // No-op
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
+     * @removed Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
     @Deprecated
@@ -6277,18 +6558,11 @@
             android.Manifest.permission.READ_PHONE_STATE
     })
     public boolean isOffhook() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.isOffhook(getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isOffhook", e);
-        }
         return false;
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
+     * @removed Use {@link android.telecom.TelecomManager#isRinging} instead
      * @hide
      */
     @Deprecated
@@ -6298,18 +6572,11 @@
             android.Manifest.permission.READ_PHONE_STATE
     })
     public boolean isRinging() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.isRinging(getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isRinging", e);
-        }
         return false;
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
+     * @removed Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
     @Deprecated
@@ -6319,13 +6586,6 @@
             android.Manifest.permission.READ_PHONE_STATE
     })
     public boolean isIdle() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.isIdle(getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isIdle", e);
-        }
         return true;
     }
 
@@ -6740,7 +7000,8 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                isDataRoamingEnabled = telephony.isDataRoamingEnabled(getSubId());
+                isDataRoamingEnabled = telephony.isDataRoamingEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#isDataRoamingEnabled", e);
@@ -6749,6 +7010,84 @@
     }
 
     /**
+     * Gets the roaming mode for CDMA phone.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * @return one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT}, {@link #CDMA_ROAMING_MODE_HOME},
+     * {@link #CDMA_ROAMING_MODE_AFFILIATED}, {@link #CDMA_ROAMING_MODE_ANY}.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public int getCdmaRoamingMode() {
+        int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                mode = telephony.getCdmaRoamingMode(getSubId());
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error calling ITelephony#getCdmaRoamingMode", ex);
+        }
+        return mode;
+    }
+
+    /**
+     * Sets the roaming mode for CDMA phone to the given mode {@code mode}.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     *
+     * @param mode should be one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT},
+     * {@link #CDMA_ROAMING_MODE_HOME}, {@link #CDMA_ROAMING_MODE_AFFILIATED},
+     * {@link #CDMA_ROAMING_MODE_ANY}.
+     *
+     * @return {@code true} if successed.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean setCdmaRoamingMode(int mode) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.setCdmaRoamingMode(getSubId(), mode);
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error calling ITelephony#setCdmaRoamingMode", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Sets the subscription mode for CDMA phone to the given mode {@code mode}.
+     *
+     * @param mode CDMA subscription mode
+     *
+     * @return {@code true} if successed.
+     *
+     * @see Phone#CDMA_SUBSCRIPTION_UNKNOWN
+     * @see Phone#CDMA_SUBSCRIPTION_RUIM_SIM
+     * @see Phone#CDMA_SUBSCRIPTION_NV
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean setCdmaSubscriptionMode(int mode) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.setCdmaSubscriptionMode(getSubId(), mode);
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error calling ITelephony#setCdmaSubscriptionMode", ex);
+        }
+        return false;
+    }
+
+    /**
      * Enables/Disables the data roaming on the subscription.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
@@ -6768,7 +7107,8 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                telephony.setDataRoamingEnabled(getSubId(), isEnabled);
+                telephony.setDataRoamingEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), isEnabled);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#setDataRoamingEnabled", e);
@@ -7561,7 +7901,7 @@
      * Returns the current {@link ServiceState} information.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -7733,26 +8073,23 @@
     }
 
     /**
-     * Return the application ID for the app type like {@link APPTYPE_CSIM}.
+     * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
+     * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
      *
-     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
-     *
-     * @param appType the uicc app type like {@link APPTYPE_CSIM}
-     * @return Application ID for specificied app type or null if no uicc or error.
+     * @param appType the uicc app type.
+     * @return Application ID for specified app type or {@code null} if no uicc or error.
      * @hide
      */
-    public String getAidForAppType(int appType) {
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public String getAidForAppType(@UiccAppType int appType) {
         return getAidForAppType(getSubId(), appType);
     }
 
     /**
-     * Return the application ID for the app type like {@link APPTYPE_CSIM}.
-     *
-     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
-     *
-     * @param subId the subscription ID that this request applies to.
-     * @param appType the uicc app type, like {@link APPTYPE_CSIM}
-     * @return Application ID for specificied app type or null if no uicc or error.
+     * same as {@link #getAidForAppType(int)}
      * @hide
      */
     public String getAidForAppType(int subId, int appType) {
@@ -8033,7 +8370,7 @@
      * Checks if phone is in emergency callback mode.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * @return true if phone is in emergency callback mode.
      * @hide
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 0e4a7ad..951bed4 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -31,6 +31,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
@@ -1205,7 +1206,8 @@
 
     /** @hide */
     public static int getMvnoTypeIntFromString(String mvnoType) {
-        Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoType);
+        String mvnoTypeString = TextUtils.isEmpty(mvnoType) ? mvnoType : mvnoType.toLowerCase();
+        Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoTypeString);
         return  mvnoTypeInt == null ? UNSPECIFIED_INT : mvnoTypeInt;
     }
 
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.aidl b/telephony/java/android/telephony/emergency/EmergencyNumber.aidl
new file mode 100644
index 0000000..bfb0a59
--- /dev/null
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.emergency;
+
+parcelable EmergencyNumber;
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
new file mode 100644
index 0000000..d6a08543
--- /dev/null
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.emergency;
+
+import android.annotation.IntDef;
+import android.hardware.radio.V1_3.EmergencyNumberSource;
+import android.hardware.radio.V1_3.EmergencyServiceCategory;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A parcelable class that wraps and retrieves the information of number, service category(s) and
+ * country code for a specific emergency number.
+ */
+public final class EmergencyNumber implements Parcelable {
+
+    private static final String LOG_TAG = "EmergencyNumber";
+
+    /**
+     * Defining Emergency Service Category as follows:
+     *  - General emergency call, all categories;
+     *  - Police;
+     *  - Ambulance;
+     *  - Fire Brigade;
+     *  - Marine Guard;
+     *  - Mountain Rescue;
+     *  - Manually Initiated eCall (MIeC);
+     *  - Automatically Initiated eCall (AIeC);
+     *
+     * Category UNSPECIFIED (General emergency call, all categories) indicates that no specific
+     * services are associated with this emergency number; if the emergency number is specified,
+     * it has one or more defined emergency service categories.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "EMERGENCY_SERVICE_CATEGORY_" }, value = {
+            EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+            EMERGENCY_SERVICE_CATEGORY_POLICE,
+            EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+            EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+            EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
+            EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE,
+            EMERGENCY_SERVICE_CATEGORY_MIEC,
+            EMERGENCY_SERVICE_CATEGORY_AIEC
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EmergencyServiceCategories {}
+
+    /**
+     * Emergency Service Category UNSPECIFIED (General emergency call, all categories) bit-field
+     * indicates that no specific services are associated with this emergency number; if the
+     * emergency number is specified, it has one or more defined emergency service categories.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED =
+            EmergencyServiceCategory.UNSPECIFIED;
+    /**
+     * Bit-field that indicates Emergency Service Category for Police.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = EmergencyServiceCategory.POLICE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Ambulance.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE =
+            EmergencyServiceCategory.AMBULANCE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Fire Brigade.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE =
+            EmergencyServiceCategory.FIRE_BRIGADE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Marine Guard.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD =
+            EmergencyServiceCategory.MARINE_GUARD;
+    /**
+     * Bit-field that indicates Emergency Service Category for Mountain Rescue.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE =
+            EmergencyServiceCategory.MOUNTAIN_RESCUE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Manually Initiated eCall (MIeC)
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = EmergencyServiceCategory.MIEC;
+    /**
+     * Bit-field that indicates Emergency Service Category for Automatically Initiated eCall (AIeC)
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = EmergencyServiceCategory.AIEC;
+
+    private static final Set<Integer> EMERGENCY_SERVICE_CATEGORY_SET;
+    static {
+        EMERGENCY_SERVICE_CATEGORY_SET = new HashSet<Integer>();
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_POLICE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MIEC);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AIEC);
+    }
+
+    /**
+     * The source to tell where the corresponding @1.3::EmergencyNumber comes from.
+     *
+     * The emergency number has one or more defined emergency number sources.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = {
+            EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+            EMERGENCY_NUMBER_SOURCE_SIM,
+            EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
+            EMERGENCY_NUMBER_SOURCE_DEFAULT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EmergencyNumberSources {}
+
+    /**
+     * Bit-field which indicates the number is from the network signaling.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING =
+            EmergencyNumberSource.NETWORK_SIGNALING;
+    /**
+     * Bit-field which indicates the number is from the sim.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM;
+    /** Bit-field which indicates the number is from the modem config. */
+    public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
+            EmergencyNumberSource.MODEM_CONFIG;
+    /**
+     * Bit-field which indicates the number is available as default.
+     *
+     * 112, 911 must always be available; additionally, 000, 08, 110, 999, 118 and 119 must be
+     * available when sim is not present.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = EmergencyNumberSource.DEFAULT;
+
+    private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
+    static {
+        EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
+    }
+
+    private final String mNumber;
+    private final String mCountryIso;
+    private final int mEmergencyServiceCategoryBitmask;
+    private final int mEmergencyNumberSourceBitmask;
+
+    /** @hide */
+    public EmergencyNumber(String number, String countryIso,
+                           int emergencyServiceCategories,
+                           int emergencyNumberSources) {
+        this.mNumber = number;
+        this.mCountryIso = countryIso;
+        this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
+        this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
+    }
+
+    /** @hide */
+    public EmergencyNumber(Parcel source) {
+        mNumber = source.readString();
+        mCountryIso = source.readString();
+        mEmergencyServiceCategoryBitmask = source.readInt();
+        mEmergencyNumberSourceBitmask = source.readInt();
+    }
+
+    /**
+     * Get the dialing number of the emergency number.
+     *
+     * The character in the number string is only the dial pad
+     * character('0'-'9', '*', or '#'). For example: 911.
+     *
+     * @return the dialing number.
+     */
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * Get the country code string (lowercase character) in ISO 3166 format of the emergency number.
+     *
+     * @return the country code string (lowercase character) in ISO 3166 format.
+     */
+    public String getCountryIso() {
+        return mCountryIso;
+    }
+
+    /**
+     * Returns the bitmask of emergency service categories {@link EmergencyServiceCategories} of
+     * the emergency number.
+     *
+     * @return bitmask of the emergency service categories {@link EmergencyServiceCategories}
+     */
+    public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() {
+        return mEmergencyServiceCategoryBitmask;
+    }
+
+    /**
+     * Returns the emergency service categories {@link EmergencyServiceCategories} of the emergency
+     * number.
+     *
+     * @return a list of the emergency service categories {@link EmergencyServiceCategories}
+     */
+    public List<Integer> getEmergencyServiceCategories() {
+        List<Integer> categories = new ArrayList<>();
+        if (serviceUnspecified()) {
+            categories.add(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
+            return categories;
+        }
+        for (Integer category : EMERGENCY_SERVICE_CATEGORY_SET) {
+            if (isInEmergencyServiceCategories(category)) {
+                categories.add(category);
+            }
+        }
+        return categories;
+    }
+
+    /**
+     * Checks if the emergency service category is unspecified for the emergency number
+     * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}.
+     *
+     * @return {@code true} if the emergency service category is unspecified for the emergency
+     * number {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
+     */
+    private boolean serviceUnspecified() {
+        return mEmergencyServiceCategoryBitmask == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+    }
+
+    /**
+     * Checks if the emergency number is in the specified emergency service category(s)
+     * {@link EmergencyServiceCategories}.
+     *
+     * @return {@code true} if the emergency number is in the specified emergency service
+     * category(s) {@link EmergencyServiceCategories}; {@code false} otherwise.
+     *
+     * @param categories - emergency service categories {@link EmergencyServiceCategories}
+     */
+    public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) {
+        if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
+            return serviceUnspecified();
+        }
+        return (mEmergencyServiceCategoryBitmask & categories) == categories;
+    }
+
+    /**
+     * Returns the bitmask of the sources {@link EmergencyNumberSources} of the emergency number.
+     *
+     * @return bitmask of the emergency number sources {@link EmergencyNumberSources}
+     */
+    public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() {
+        return mEmergencyNumberSourceBitmask;
+    }
+
+    /**
+     * Returns a list of {@link EmergencyNumberSources} of the emergency number.
+     *
+     * @return a list of {@link EmergencyNumberSources}
+     */
+    public List<Integer> getEmergencyNumberSources() {
+        List<Integer> sources = new ArrayList<>();
+        for (Integer source : EMERGENCY_NUMBER_SOURCE_SET) {
+            if ((mEmergencyNumberSourceBitmask & source) == source) {
+                sources.add(source);
+            }
+        }
+        return sources;
+    }
+
+    /**
+     * Checks if the emergency number is from the specified emergency number source(s)
+     * {@link EmergencyNumberSources}.
+     *
+     * @return {@code true} if the emergency number is from the specified emergency number
+     * source(s) {@link EmergencyNumberSources}; {@code false} otherwise.
+     *
+     * @param sources - {@link EmergencyNumberSources}
+     */
+    public boolean isFromSources(@EmergencyNumberSources int sources) {
+        return (mEmergencyNumberSourceBitmask & sources) == sources;
+    }
+
+    @Override
+    /** @hide */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mNumber);
+        dest.writeString(mCountryIso);
+        dest.writeInt(mEmergencyServiceCategoryBitmask);
+        dest.writeInt(mEmergencyNumberSourceBitmask);
+    }
+
+    @Override
+    /** @hide */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "EmergencyNumber = " + "[Number]" + mNumber + " / [CountryIso]" + mCountryIso
+                + " / [ServiceCategories]"
+                + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
+                + " / [Sources]" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!EmergencyNumber.class.isInstance(o)) {
+            return false;
+        }
+        return (o == this || toString().equals(o.toString()));
+    }
+
+    public static final Parcelable.Creator<EmergencyNumber> CREATOR =
+            new Parcelable.Creator<EmergencyNumber>() {
+        @Override
+        public EmergencyNumber createFromParcel(Parcel in) {
+            return new EmergencyNumber(in);
+        }
+
+        @Override
+        public EmergencyNumber[] newArray(int size) {
+            return new EmergencyNumber[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index f0d3c89..89ef339 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -117,10 +117,14 @@
      * @hide
      */
     public static final String EXTRA_CONFERENCE = "conference";
+
     /**
-     * @hide
+     * Boolean extra property set on an {@link ImsCallProfile} to indicate that this call is an
+     * emergency call.  The {@link ImsService} sets this on a call to indicate that the network has
+     * identified the call as an emergency call.
      */
-    public static final String EXTRA_E_CALL = "e_call";
+    public static final String EXTRA_EMERGENCY_CALL = "e_call";
+
     /**
      * @hide
      */
@@ -245,7 +249,8 @@
      * constants, the values passed for the {@link #EXTRA_CALL_RAT_TYPE} should be strings (e.g.
      * "14" vs (int) 14).
      * Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
-     *      updateWifiStateFromExtras(Bundle)} to determine whether to set the
+     *      updateImsCallRatFromExtras(Bundle)} to determine whether to set the
+     * {@link android.telecom.TelecomManager#EXTRA_CALL_NETWORK_TYPE} extra value and
      * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
      */
     public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index a20d4f5..df903cc2 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -16,22 +16,16 @@
 
 package android.telephony.ims;
 
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.ims.aidl.IImsCallSessionListener;
-
-import java.util.Objects;
-import java.util.concurrent.Executor;
-
-import android.telephony.ims.stub.ImsCallSessionImplBase;
 import android.util.Log;
 
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsVideoCallProvider;
 
+import java.util.Objects;
+
 /**
  * Provides the call initiation/termination, and media exchange between two IMS endpoints.
  * It directly communicates with IMS service which implements the IMS protocol behavior.
@@ -42,7 +36,8 @@
     private static final String TAG = "ImsCallSession";
 
     /**
-     * Defines IMS call session state. Please use {@link ImsCallSessionImplBase.State} definition.
+     * Defines IMS call session state. Please use
+     * {@link android.telephony.ims.stub.ImsCallSessionImplBase.State} definition.
      * This is kept around for capability reasons.
      */
     public static class State {
@@ -1027,9 +1022,9 @@
     }
 
     /**
-     * Sends RTT Upgrade request
+     * Sends RTT Upgrade or downgrade request
      *
-     * @param to   : expected profile
+     * @param to Profile with the RTT flag set to the desired value
      */
     public void sendRttModifyRequest(ImsCallProfile to) {
         if (mClosed) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 3138180..cecf2e2 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -21,12 +21,11 @@
 import android.net.Uri;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.util.Log;
 
-import android.telephony.ims.ImsReasonInfo;
-
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
@@ -81,13 +80,14 @@
      * Callback class for receiving Registration callback events.
      * @hide
      */
-    public static class Callback {
+    public static class Callback extends IImsRegistrationCallback.Stub {
         /**
          * Notifies the framework when the IMS Provider is connected to the IMS network.
          *
          * @param imsRadioTech the radio access technology. Valid values are defined in
          * {@link ImsRegistrationTech}.
          */
+        @Override
         public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
         }
 
@@ -97,6 +97,7 @@
          * @param imsRadioTech the radio access technology. Valid values are defined in
          * {@link ImsRegistrationTech}.
          */
+        @Override
         public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
         }
 
@@ -105,6 +106,7 @@
          *
          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
          */
+        @Override
         public void onDeregistered(ImsReasonInfo info) {
         }
 
@@ -115,6 +117,7 @@
          * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
          * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
          */
+        @Override
         public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
                 ImsReasonInfo info) {
         }
@@ -125,6 +128,7 @@
          * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
          *         subscription.
          */
+        @Override
         public void onSubscriberAssociatedUriChanged(Uri[] uris) {
 
         }
diff --git a/telephony/java/android/telephony/mbms/GroupCall.java b/telephony/java/android/telephony/mbms/GroupCall.java
new file mode 100644
index 0000000..9aca18e
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/GroupCall.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.os.RemoteException;
+import android.telephony.MbmsGroupCallSession;
+import android.telephony.mbms.vendor.IMbmsGroupCallService;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class used to represent a single MBMS group call. After a call has been started with
+ * {@link MbmsGroupCallSession#startGroupCall},
+ * this class is used to hold information about the call and control it.
+ */
+public class GroupCall implements AutoCloseable {
+    private static final String LOG_TAG = "MbmsGroupCall";
+
+    /**
+     * The state of a group call, reported via
+     * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+    public @interface GroupCallState {}
+    public static final int STATE_STOPPED = 1;
+    public static final int STATE_STARTED = 2;
+    public static final int STATE_STALLED = 3;
+
+    /**
+     * The reason for a call state change, reported via
+     * {@link GroupCallCallback#onGroupCallStateChanged(int, int)}
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "REASON_" },
+            value = {REASON_BY_USER_REQUEST, REASON_FREQUENCY_CONFLICT,
+                    REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
+                    REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
+    public @interface GroupCallStateChangeReason {}
+
+    /**
+     * Indicates that the middleware does not have a reason to provide for the state change.
+     */
+    public static final int REASON_NONE = 0;
+
+    /**
+     * State changed due to a call to {@link #close()} or
+     * {@link MbmsGroupCallSession#startGroupCall}
+     */
+    public static final int REASON_BY_USER_REQUEST = 1;
+
+    // 2 is unused to match up with streaming.
+
+    /**
+     * State changed due to a frequency conflict with another requested call.
+     */
+    public static final int REASON_FREQUENCY_CONFLICT = 3;
+
+    /**
+     * State changed due to the middleware running out of memory
+     */
+    public static final int REASON_OUT_OF_MEMORY = 4;
+
+    /**
+     * State changed due to the device leaving the home carrier's LTE network.
+     */
+    public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5;
+
+    /**
+     * State changed due to the device leaving the area where this call is being broadcast.
+     */
+    public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 6;
+
+    private final int mSubscriptionId;
+    private final long mTmgi;
+    private final MbmsGroupCallSession mParentSession;
+    private final InternalGroupCallCallback mCallback;
+    private IMbmsGroupCallService mService;
+
+    /**
+     * @hide
+     */
+    public GroupCall(int subscriptionId,
+            IMbmsGroupCallService service,
+            MbmsGroupCallSession session,
+            long tmgi,
+            InternalGroupCallCallback callback) {
+        mSubscriptionId = subscriptionId;
+        mParentSession = session;
+        mService = service;
+        mTmgi = tmgi;
+        mCallback = callback;
+    }
+
+    /**
+     * Retrieve the TMGI (Temporary Mobile Group Identity) corresponding to this call.
+     */
+    public long getTmgi() {
+        return mTmgi;
+    }
+
+    /**
+     * Send an update to the middleware when the SAI (Service Area Identifier) list and frequency
+     * information of the group call has * changed. Callers must obtain this information from the
+     * wireless carrier independently.
+     * @param saiArray New array of SAIs that the call is available on.
+     * @param frequencyArray New array of frequencies that the call is available on.
+     */
+    public void updateGroupCall(int[] saiArray, int[] frequencyArray) {
+        if (mService == null) {
+            throw new IllegalStateException("No group call service attached");
+        }
+
+        try {
+            mService.updateGroupCall(mSubscriptionId, mTmgi, saiArray, frequencyArray);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService = null;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        } finally {
+            mParentSession.onGroupCallStopped(this);
+        }
+    }
+
+    /**
+     * Stop this group call. Further operations on this object will fail with an
+     * {@link IllegalStateException}.
+     *
+     * May throw an {@link IllegalStateException}
+     */
+    @Override
+    public void close() {
+        if (mService == null) {
+            throw new IllegalStateException("No group call service attached");
+        }
+
+        try {
+            mService.stopGroupCall(mSubscriptionId, mTmgi);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService = null;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        } finally {
+            mParentSession.onGroupCallStopped(this);
+        }
+    }
+
+    /** @hide */
+    public InternalGroupCallCallback getCallback() {
+        return mCallback;
+    }
+
+    private void sendErrorToApp(int errorCode, String message) {
+        mCallback.onError(errorCode, message);
+    }
+}
+
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
new file mode 100644
index 0000000..001bb02
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A callback class for use when the application is in a group call. The middleware
+ * will provide updates on the status of the call via this callback.
+ */
+public class GroupCallCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
+    private @interface GroupCallError{}
+
+    /**
+     * Indicates broadcast signal strength is not available for this call.
+     *
+     * This may be due to the call no longer being available due to geography
+     * or timing (end of service)
+     */
+    public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+
+    /**
+     * Called by the middleware when it has detected an error condition in this group call. The
+     * possible error codes are listed in {@link MbmsErrors}.
+     * @param errorCode The error code.
+     * @param message A human-readable message generated by the middleware for debugging purposes.
+     */
+    public void onError(@GroupCallError int errorCode, @Nullable String message) {
+        // default implementation empty
+    }
+
+    /**
+     * Called to indicate this call has changed state.
+     *
+     * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
+     * and {@link GroupCall#STATE_STALLED}.
+     */
+    public void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+            @GroupCall.GroupCallStateChangeReason int reason) {
+        // default implementation empty
+    }
+
+    /**
+     * Broadcast Signal Strength updated.
+     *
+     * This signal strength is the BROADCAST signal strength which,
+     * depending on technology in play and it's deployment, may be
+     * stronger or weaker than the traditional UNICAST signal
+     * strength.  It a simple int from 0-4 for valid levels or
+     * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
+     * for this call due to timing, geography or popularity.
+     */
+    public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+        // default implementation empty
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
new file mode 100755
index 0000000..844b634
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+/**
+ * @hide
+ */
+oneway interface IGroupCallCallback {
+    void onError(int errorCode, String message);
+    void onGroupCallStateChanged(int state, int reason);
+    void onBroadcastSignalStrengthUpdated(int signalStrength);
+}
diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
new file mode 100755
index 0000000..1a1c7f8
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
@@ -0,0 +1,33 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony.mbms;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+oneway interface IMbmsGroupCallSessionCallback
+{
+    void onError(int errorCode, String message);
+
+    void onAvailableSaisUpdated(in List currentSai, in List availableSais);
+
+    void onServiceInterfaceAvailable(String interfaceName, int index);
+
+    void onMiddlewareReady();
+}
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
new file mode 100644
index 0000000..2910bb3
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallCallback.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.os.Binder;
+
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalGroupCallCallback extends IGroupCallCallback.Stub {
+    private final GroupCallCallback mAppCallback;
+    private final Executor mExecutor;
+    private volatile boolean mIsStopped = false;
+
+    public InternalGroupCallCallback(GroupCallCallback appCallback,
+            Executor executor) {
+        mAppCallback = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onError(final int errorCode, final String message) {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onGroupCallStateChanged(final int state, final int reason) {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onGroupCallStateChanged(state, reason);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    /** Prevents this callback from calling the app */
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
new file mode 100644
index 0000000..4c9cf4d
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalGroupCallSessionCallback.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.os.Binder;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** @hide */
+public class InternalGroupCallSessionCallback extends IMbmsGroupCallSessionCallback.Stub {
+    private final Executor mExecutor;
+    private final MbmsGroupCallSessionCallback mAppCallback;
+    private volatile boolean mIsStopped = false;
+
+    public InternalGroupCallSessionCallback(MbmsGroupCallSessionCallback appCallback,
+            Executor executor) {
+        mAppCallback = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onError(final int errorCode, final String message) {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onAvailableSaisUpdated(final List currentSais, final List availableSais) {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onAvailableSaisUpdated(currentSais, availableSais);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onServiceInterfaceAvailable(final String interfaceName, final int index) {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onServiceInterfaceAvailable(interfaceName, index);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onMiddlewareReady() {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMiddlewareReady();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    /** Prevents this callback from calling the app */
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
new file mode 100644
index 0000000..7da734e
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.telephony.MbmsGroupCallSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class that is used to receive information from the middleware on MBMS group-call
+ * services. An instance of this object should be passed into
+ * {@link MbmsGroupCallSession#create(Context, Executor, int, MbmsGroupCallSessionCallback)}.
+ */
+public class MbmsGroupCallSessionCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED}, prefix = { "ERROR_" })
+    private @interface GroupCallError{}
+
+    /**
+     * Called by the middleware when it has detected an error condition. The possible error codes
+     * are listed in {@link MbmsErrors}.
+     * @param errorCode The error code.
+     * @param message A human-readable message generated by the middleware for debugging purposes.
+     */
+    public void onError(@GroupCallError int errorCode, @Nullable String message) {
+    }
+
+    /**
+     * Indicates that the list of currently available SAIs has been updated. The app may use this
+     * information to filter the list of group calls when displaying available group calls to
+     * the user by matching the SAIs with a list of group calls separately negotiated with the
+     * carrier. The app may also report the aggregate list of SAIs to the group call application
+     * server so that a network entity can determine when, and where to activate the broadcast of
+     * particular group calls.
+     * @param currentSais The available SAIs on the current cell.
+     * @param availableSais A list of lists of available SAIS in neighboring cells, where each list
+     *                      contains the available SAIs in an individual cell.
+     */
+    public void onAvailableSaisUpdated(List<Integer> currentSais,
+            List<List<Integer>> availableSais) {
+    }
+
+    /**
+     * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
+     * via this callback may be used to establish a data-link interface with the modem before the
+     * middleware is ready.
+     * Note that this method may be called before {@link #onMiddlewareReady()}.
+     *
+     * @param interfaceName The interface name for the data link.
+     * @param index The index for the data link.
+     */
+    public void onServiceInterfaceAvailable(String interfaceName, int index) {
+    }
+
+    /**
+     * Called to indicate that the middleware has been initialized and is ready.
+     *
+     * Before this method is called, calling any method on an instance of
+     * {@link MbmsGroupCallSession} will result in an {@link IllegalStateException} or an error
+     * delivered via {@link #onError(int, String)} with error code
+     * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
+     */
+    public void onMiddlewareReady() {
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index 06b2120..95b4d37 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -23,6 +23,7 @@
 import android.content.pm.*;
 import android.content.pm.ServiceInfo;
 import android.telephony.MbmsDownloadSession;
+import android.telephony.MbmsGroupCallSession;
 import android.telephony.MbmsStreamingSession;
 import android.util.Log;
 
@@ -59,6 +60,9 @@
             case MbmsStreamingSession.MBMS_STREAMING_SERVICE_ACTION:
                 metaDataKey = MbmsStreamingSession.MBMS_STREAMING_SERVICE_OVERRIDE_METADATA;
                 break;
+            case MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_ACTION:
+                metaDataKey = MbmsGroupCallSession.MBMS_GROUP_CALL_SERVICE_OVERRIDE_METADATA;
+                break;
         }
         if (metaDataKey == null) {
             return null;
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
new file mode 100755
index 0000000..721256a
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
@@ -0,0 +1,39 @@
+/*
+** 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.
+*/
+
+package android.telephony.mbms.vendor;
+
+import android.net.Uri;
+import android.telephony.mbms.IMbmsGroupCallSessionCallback;
+import android.telephony.mbms.IGroupCallCallback;
+
+/**
+ * @hide
+ */
+interface IMbmsGroupCallService
+{
+    int initialize(IMbmsGroupCallSessionCallback callback, int subId);
+
+    void stopGroupCall(int subId, long tmgi);
+
+    void updateGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
+        in int[] frequencyArray);
+
+    int startGroupCall(int subscriptionId, long tmgi, in int[] saiArray,
+        in int[] frequencyArray, IGroupCallCallback callback);
+
+    void dispose(int subId);
+}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
new file mode 100644
index 0000000..3734ca7
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.mbms.vendor;
+
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.mbms.GroupCallCallback;
+import android.telephony.mbms.IGroupCallCallback;
+import android.telephony.mbms.IMbmsGroupCallSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsGroupCallSessionCallback;
+import android.telephony.mbms.vendor.IMbmsGroupCallService.Stub;
+
+import java.util.List;
+
+/**
+ * Base class for MBMS group-call services. The middleware should override this class to implement
+ * its {@link Service} for group calls
+ * Subclasses should call this class's {@link #onBind} method to obtain an {@link IBinder} if they
+ * override {@link #onBind}.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class MbmsGroupCallServiceBase extends Service {
+    private final IBinder mInterface = new Stub() {
+        @Override
+        public int initialize(final IMbmsGroupCallSessionCallback callback,
+                final int subscriptionId) throws RemoteException {
+            if (callback == null) {
+                throw new NullPointerException("Callback must not be null");
+            }
+
+            final int uid = Binder.getCallingUid();
+
+            int result = MbmsGroupCallServiceBase.this.initialize(
+                    new MbmsGroupCallSessionCallback() {
+                        @Override
+                        public void onError(final int errorCode, final String message) {
+                            try {
+                                if (errorCode == MbmsErrors.UNKNOWN) {
+                                    throw new IllegalArgumentException(
+                                            "Middleware cannot send an unknown error.");
+                                }
+                                callback.onError(errorCode, message);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        @Override
+                        public void onAvailableSaisUpdated(final List currentSais,
+                                final List availableSais) {
+                            try {
+                                callback.onAvailableSaisUpdated(currentSais, availableSais);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        @Override
+                        public void onServiceInterfaceAvailable(final String interfaceName,
+                                final int index) {
+                            try {
+                                callback.onServiceInterfaceAvailable(interfaceName, index);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        @Override
+                        public void onMiddlewareReady() {
+                            try {
+                                callback.onMiddlewareReady();
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+                    }, subscriptionId);
+
+            if (result == MbmsErrors.SUCCESS) {
+                callback.asBinder().linkToDeath(new DeathRecipient() {
+                    @Override
+                    public void binderDied() {
+                        onAppCallbackDied(uid, subscriptionId);
+                    }
+                }, 0);
+            }
+
+            return result;
+        }
+
+        @Override
+        public void stopGroupCall(int subId, long tmgi) {
+            MbmsGroupCallServiceBase.this.stopGroupCall(subId, tmgi);
+        }
+
+        @Override
+        public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
+                int[] frequencyArray) {
+            MbmsGroupCallServiceBase.this.updateGroupCall(
+                    subscriptionId, tmgi, saiArray, frequencyArray);
+        }
+
+        @Override
+        public int startGroupCall(final int subscriptionId, final long tmgi, final int[] saiArray,
+                final int[] frequencyArray, final IGroupCallCallback callback)
+                throws RemoteException {
+            if (callback == null) {
+                throw new NullPointerException("Callback must not be null");
+            }
+
+            final int uid = Binder.getCallingUid();
+
+            int result = MbmsGroupCallServiceBase.this.startGroupCall(
+                    subscriptionId, tmgi, saiArray, frequencyArray, new GroupCallCallback() {
+                        @Override
+                        public void onError(final int errorCode, final String message) {
+                            try {
+                                if (errorCode == MbmsErrors.UNKNOWN) {
+                                    throw new IllegalArgumentException(
+                                            "Middleware cannot send an unknown error.");
+                                }
+                                callback.onError(errorCode, message);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        public void onGroupCallStateChanged(int state, int reason) {
+                            try {
+                                callback.onGroupCallStateChanged(state, reason);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+
+                        public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+                            try {
+                                callback.onBroadcastSignalStrengthUpdated(signalStrength);
+                            } catch (RemoteException e) {
+                                onAppCallbackDied(uid, subscriptionId);
+                            }
+                        }
+                    });
+
+            if (result == MbmsErrors.SUCCESS) {
+                callback.asBinder().linkToDeath(new DeathRecipient() {
+                    @Override
+                    public void binderDied() {
+                        onAppCallbackDied(uid, subscriptionId);
+                    }
+                }, 0);
+            }
+
+            return result;
+        }
+
+        @Override
+        public void dispose(int subId) throws RemoteException {
+            MbmsGroupCallServiceBase.this.dispose(subId);
+        }
+    };
+
+    /**
+     * Initialize the group call service for this app and subscription ID, registering the callback.
+     *
+     * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
+     * will be intercepted and passed to the app as
+     * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+     *
+     * May return any value from {@link MbmsErrors.InitializationErrors}
+     * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+     * {@link IMbmsGroupCallSessionCallback#onError(int, String)}.
+     *
+     * @param callback The callback to use to communicate with the app.
+     * @param subscriptionId The subscription ID to use.
+     */
+    public int initialize(MbmsGroupCallSessionCallback callback, int subscriptionId)
+            throws RemoteException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Starts a particular group call. This method may perform asynchronous work. When
+     * the call is ready for consumption, the middleware should inform the app via
+     * {@link IGroupCallCallback#onGroupCallStateChanged(int, int)}.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     * @param tmgi The TMGI, an identifier for the group call.
+     * @param saiArray An array of SAIs for the group call.
+     * @param frequencyArray An array of frequencies for the group call.
+     * @param callback The callback object on which the app wishes to receive updates.
+     * @return Any error in {@link MbmsErrors.GeneralErrors}
+     */
+    public int startGroupCall(int subscriptionId, long tmgi, int[] saiArray, int[] frequencyArray,
+            GroupCallCallback callback) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Stop the group call identified by {@code tmgi}.
+     *
+     * The callback provided via {@link #startGroupCall} should no longer be
+     * used after this method has called by the app.
+     *
+     * May throw an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     * @param tmgi The TMGI for the call to stop.
+     */
+    public void stopGroupCall(int subscriptionId, long tmgi) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Called when the app receives new SAI and frequency information for the group call identified
+     * by {@code tmgi}.
+     * @param saiArray New array of SAIs that the call is available on.
+     * @param frequencyArray New array of frequencies that the call is available on.
+     */
+    public void updateGroupCall(int subscriptionId, long tmgi, int[] saiArray,
+            int[] frequencyArray) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Signals that the app wishes to dispose of the session identified by the
+     * {@code subscriptionId} argument and the caller's uid. No notification back to the
+     * app is required for this operation, and the corresponding callback provided via
+     * {@link #initialize} should no longer be used
+     * after this method has been called by the app.
+     *
+     * May throw an {@link IllegalStateException}
+     *
+     * @param subscriptionId The subscription id to use.
+     */
+    public void dispose(int subscriptionId) throws RemoteException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Indicates that the app identified by the given UID and subscription ID has died.
+     * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+     * @param subscriptionId The subscription ID the app is using.
+     */
+    public void onAppCallbackDied(int uid, int subscriptionId) {
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mInterface;
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 6521f0b..0ccd748 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -232,5 +232,5 @@
      */
     int getSimStateForSlotIndex(int slotIndex);
 
-    boolean isActiveSubId(int subId);
+    boolean isActiveSubId(int subId, String callingPackage);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b4f3487..627dfaa 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -75,116 +75,6 @@
     void call(String callingPackage, String number);
 
     /**
-     * End call if there is a call in progress, otherwise does nothing.
-     *
-     * @return whether it hung up
-     */
-    boolean endCall();
-
-    /**
-     * End call on particular subId or go to the Home screen
-     * @param subId user preferred subId.
-     * @return whether it hung up
-     */
-    boolean endCallForSubscriber(int subId);
-
-    /**
-     * Answer the currently-ringing call.
-     *
-     * If there's already a current active call, that call will be
-     * automatically put on hold.  If both lines are currently in use, the
-     * current active call will be ended.
-     *
-     * TODO: provide a flag to let the caller specify what policy to use
-     * if both lines are in use.  (The current behavior is hardwired to
-     * "answer incoming, end ongoing", which is how the CALL button
-     * is specced to behave.)
-     *
-     * TODO: this should be a oneway call (especially since it's called
-     * directly from the key queue thread).
-     */
-    void answerRingingCall();
-
-    /**
-     * Answer the currently-ringing call on particular subId .
-     *
-     * If there's already a current active call, that call will be
-     * automatically put on hold.  If both lines are currently in use, the
-     * current active call will be ended.
-     *
-     * TODO: provide a flag to let the caller specify what policy to use
-     * if both lines are in use.  (The current behavior is hardwired to
-     * "answer incoming, end ongoing", which is how the CALL button
-     * is specced to behave.)
-     *
-     * TODO: this should be a oneway call (especially since it's called
-     * directly from the key queue thread).
-     */
-    void answerRingingCallForSubscriber(int subId);
-
-    /**
-     * Silence the ringer if an incoming call is currently ringing.
-     * (If vibrating, stop the vibrator also.)
-     *
-     * It's safe to call this if the ringer has already been silenced, or
-     * even if there's no incoming call.  (If so, this method will do nothing.)
-     *
-     * TODO: this should be a oneway call too (see above).
-     *       (Actually *all* the methods here that return void can
-     *       probably be oneway.)
-     */
-    void silenceRinger();
-
-    /**
-     * Check if we are in either an active or holding call
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is OFFHOOK.
-     */
-    boolean isOffhook(String callingPackage);
-
-    /**
-     * Check if a particular subId has an active or holding call
-     *
-     * @param subId user preferred subId.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is OFFHOOK.
-     */
-    boolean isOffhookForSubscriber(int subId, String callingPackage);
-
-    /**
-     * Check if an incoming phone call is ringing or call waiting
-     * on a particular subId.
-     *
-     * @param subId user preferred subId.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is RINGING.
-     */
-    boolean isRingingForSubscriber(int subId, String callingPackage);
-
-    /**
-     * Check if an incoming phone call is ringing or call waiting.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is RINGING.
-     */
-    boolean isRinging(String callingPackage);
-
-    /**
-     * Check if the phone is idle.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is IDLE.
-     */
-    boolean isIdle(String callingPackage);
-
-    /**
-     * Check if the phone is idle on a particular subId.
-     *
-     * @param subId user preferred subId.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is IDLE.
-     */
-    boolean isIdleForSubscriber(int subId, String callingPackage);
-
-    /**
      * Check to see if the radio is on or not.
      * @param callingPackage the name of the package making the call.
      * @return returns true if the radio is on.
@@ -817,12 +707,6 @@
     IImsConfig getImsConfig(int slotId, int feature);
 
     /**
-     * @return true if the IMS resolver is busy resolving a binding and should not be considered
-     * available, false if the IMS resolver is idle.
-     */
-    boolean isResolvingImsBinding();
-
-    /**
     *  @return true if the ImsService to bind to for the slot id specified was set, false otherwise.
     */
     boolean setImsService(int slotId, boolean isCarrierImsService, String packageName);
@@ -871,14 +755,15 @@
      * Ask the radio to connect to the input network and change selection mode to manual.
      *
      * @param subId the id of the subscription.
-     * @param operatorNumeric the PLMN of the operator to attach to.
-     * @param persistSelection Whether the selection will persist until reboot. If true, only allows
+     * @param operatorInfo the operator inforamtion, included the PLMN, long name and short name of
+     * the operator to attach to.
+     * @param persistSelection whether the selection will persist until reboot. If true, only allows
      * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
      * normal network selection next time.
-     * @return true if the request suceeded.
+     * @return {@code true} on success; {@code true} on any failure.
      */
-    boolean setNetworkSelectionModeManual(int subId, in String operatorNumeric,
-            boolean persistSelection);
+    boolean setNetworkSelectionModeManual(
+            int subId, in OperatorInfo operatorInfo, boolean persisSelection);
 
     /**
      * Set the preferred network type.
@@ -1536,6 +1421,34 @@
     void setDataRoamingEnabled(int subId, boolean isEnabled);
 
     /**
+     * Gets the roaming mode for the CDMA phone with the subscription id {@code subId}.
+     *
+     * @param the subscription id.
+     * @return the roaming mode for CDMA phone.
+     */
+    int getCdmaRoamingMode(int subId);
+
+    /**
+     * Sets the roaming mode on the CDMA phone with the subscription {@code subId} to the given
+     * roaming mode {@code mode}.
+     *
+     * @param subId the subscription id.
+     * @param mode the roaming mode should be set.
+     * @return {@code true} if successed.
+     */
+    boolean setCdmaRoamingMode(int subId, int mode);
+
+    /**
+     * Sets the subscription mode for CDMA phone with the subscription {@code subId} to the given
+     * subscription mode {@code mode}.
+     *
+     * @param subId the subscription id.
+     * @param mode the subscription mode should be set.
+     * @return {@code true} if successed.
+     */
+    boolean setCdmaSubscriptionMode(int subId, int mode);
+
+    /**
      * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
      * plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config
      * (also any country or carrier overlays) to be loaded when using a test SIM with a call box.
@@ -1559,4 +1472,9 @@
      * @hide
      */
     int getNumberOfModemsWithSimultaneousDataConnections(int subId, String callingPackage);
+
+    /**
+     * Return the network selection mode on the subscription with id {@code subId}.
+     */
+     int getNetworkSelectionMode(int subId);
 }
diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
index d0245a0..a47e2b0 100644
--- a/telephony/java/com/android/internal/telephony/OperatorInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -21,7 +21,7 @@
 import android.os.Parcelable;
 
 /**
- * {@hide}
+ * @hide
  */
 public class OperatorInfo implements Parcelable {
     public enum State {
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index f9de776..21f3b92 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -176,6 +176,10 @@
 
     // FIXME: This is used to pass a subId via intents, we need to look at its usage, which is
     // FIXME: extensive, and see if this should be an array of all active subId's or ...?
+    /**
+     * @Deprecated use {@link android.telephony.SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}
+     * instead.
+     */
     public static final String SUBSCRIPTION_KEY  = "subscription";
 
     public static final String SUB_SETTING  = "subSettings";
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index 6bf22a0..8015b07 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -33,6 +33,7 @@
 import com.android.internal.telephony.SmsConstants;
 
 import java.io.UnsupportedEncodingException;
+import java.util.Locale;
 
 /**
  * Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is
@@ -44,16 +45,34 @@
      * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
      */
     private static final String[] LANGUAGE_CODES_GROUP_0 = {
-            "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
-            "pl", null
+            Locale.GERMAN.getLanguage(),        // German
+            Locale.ENGLISH.getLanguage(),       // English
+            Locale.ITALIAN.getLanguage(),       // Italian
+            Locale.FRENCH.getLanguage(),        // French
+            new Locale("es").getLanguage(),     // Spanish
+            new Locale("nl").getLanguage(),     // Dutch
+            new Locale("sv").getLanguage(),     // Swedish
+            new Locale("da").getLanguage(),     // Danish
+            new Locale("pt").getLanguage(),     // Portuguese
+            new Locale("fi").getLanguage(),     // Finnish
+            new Locale("nb").getLanguage(),     // Norwegian
+            new Locale("el").getLanguage(),     // Greek
+            new Locale("tr").getLanguage(),     // Turkish
+            new Locale("hu").getLanguage(),     // Hungarian
+            new Locale("pl").getLanguage(),     // Polish
+            null
     };
 
     /**
      * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
      */
     private static final String[] LANGUAGE_CODES_GROUP_2 = {
-            "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
-            null, null
+            new Locale("cs").getLanguage(),     // Czech
+            new Locale("he").getLanguage(),     // Hebrew
+            new Locale("ar").getLanguage(),     // Arabic
+            new Locale("ru").getLanguage(),     // Russian
+            new Locale("is").getLanguage(),     // Icelandic
+            null, null, null, null, null, null, null, null, null, null, null
     };
 
     private static final char CARRIAGE_RETURN = 0x0d;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
index 0fabc2f..541ca8d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
@@ -118,71 +118,103 @@
     public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE
             = 0x111E; // 4382
 
-    /** CMAS Message Identifier for Presidential Level alerts for additional languages
-     *  for additional languages. */
+    /**
+     * CMAS Message Identifier for Presidential Level alerts for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL_LANGUAGE
             = 0x111F; // 4383
 
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed
-     *  for additional languages. */
+    /**
+     * CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed
+     * for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE
             = 0x1120; // 4384
 
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely
-     *  for additional languages. */
+    /**
+     * CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely
+     *  for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE
             = 0x1121; // 4385
 
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed
-     *  for additional languages. */
+    /**
+     * CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed
+     * for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE
             = 0x1122; // 4386
 
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely
-     *  for additional languages. */
+    /**
+     * CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely
+     * for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE
             = 0x1123; // 4387
 
-    /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed
-     *  for additional languages. */
+    /**
+     * CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed
+     * for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE
             = 0x1124; // 4388
 
-    /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely
-     *  for additional languages.*/
+    /**
+     * CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely
+     * for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE
             = 0x1125; // 4389
 
-    /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed
-     *  for additional languages. */
+    /**
+     * CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed
+     * for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE
             = 0x1126; // 4390
 
-    /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely
-     *  for additional languages.*/
+    /**
+     * CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely
+     * for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE
             = 0x1127; // 4391
 
-    /** CMAS Message Identifier for Child Abduction Emergency (Amber Alert)
-     *  for additional languages. */
+    /**
+     * CMAS Message Identifier for Child Abduction Emergency (Amber Alert)
+     * for additional languages.
+     */
     public static final int MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY_LANGUAGE
             = 0x1128; // 4392
 
-    /** CMAS Message Identifier for the Required Monthly Test
-     *  for additional languages. */
+    /** CMAS Message Identifier for the Required Monthly Test  for additional languages. */
     public static final int MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST_LANGUAGE
             = 0x1129; // 4393
 
-    /** CMAS Message Identifier for CMAS Exercise
-     *  for additional languages. */
+    /** CMAS Message Identifier for CMAS Exercise for additional languages. */
     public static final int MESSAGE_ID_CMAS_ALERT_EXERCISE_LANGUAGE
             = 0x112A; // 4394
 
-    /** CMAS Message Identifier for operator defined use
-     *  for additional languages. */
+    /** CMAS Message Identifier for operator defined use for additional languages. */
     public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE_LANGUAGE
             = 0x112B; // 4395
 
+    /** CMAS Message Identifier for CMAS Public Safety Alerts. */
+    public static final int MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY
+            = 0x112C; // 4396
+
+    /** CMAS Message Identifier for CMAS Public Safety Alerts for additional languages. */
+    public static final int MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY_LANGUAGE
+            = 0x112D; // 4397
+
+    /** CMAS Message Identifier for CMAS State/Local Test. */
+    public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST
+            = 0x112E; // 4398
+
+    /** CMAS Message Identifier for CMAS State/Local Test for additional languages. */
+    public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST_LANGUAGE
+            = 0x112F; // 4399
+
     /** End of CMAS Message Identifier range (including future extensions). */
     public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER
             = 0x112F; // 4399
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/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 6ce66f0..639ed7d 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -245,8 +245,14 @@
                     mIterationCycle = false;
                     // In the "applaunch.txt" file, trail launches is referenced using
                     // "TRIAL_LAUNCH"
-                    String appPkgName = mNameToIntent.get(launch.getApp())
-                        .getComponent().getPackageName();
+                    Intent startIntent = mNameToIntent.get(launch.getApp());
+                    if (startIntent == null) {
+                        Log.w(TAG, "App does not exist: " + launch.getApp());
+                        mResult.putString(mNameToResultKey.get(launch.getApp()),
+                            "App does not exist");
+                        continue;
+                    }
+                    String appPkgName = startIntent.getComponent().getPackageName();
                     if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
                         assertTrue(String.format("Not able to compile the app : %s", appPkgName),
                               compileApp(VERIFY_FILTER, appPkgName));
diff --git a/tests/DexLoggerIntegrationTests/AndroidManifest.xml b/tests/DexLoggerIntegrationTests/AndroidManifest.xml
index a847e8f..a9f01ed 100644
--- a/tests/DexLoggerIntegrationTests/AndroidManifest.xml
+++ b/tests/DexLoggerIntegrationTests/AndroidManifest.xml
@@ -17,10 +17,10 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.frameworks.dexloggertest">
 
-    <!-- Tests feature introduced in P (27) -->
+    <!-- Tests feature introduced in P (28) -->
     <uses-sdk
-        android:minSdkVersion="27"
-        android:targetSdkVersion="27" />
+        android:minSdkVersion="28"
+        android:targetSdkVersion="28" />
 
     <uses-permission android:name="android.permission.READ_LOGS" />
 
diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
index fe65ecc..c225e17 100644
--- a/tests/NetworkSecurityConfigTest/Android.mk
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -7,7 +7,6 @@
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner \
-    bouncycastle \
     conscrypt \
     android.test.base \
 
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index e529b93..750e2fb 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -38,6 +38,7 @@
     libbacktrace \
     libbase \
     libbinder \
+    libbinderthreadstate \
     libc++ \
     libcrypto \
     libcutils \
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index ba1a2ea..6dae3f1 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -44,6 +44,7 @@
     <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.NETWORK_STACK" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/net/java/android/net/dhcp/DhcpPacketTest.java b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
index 312b3d1..a592809 100644
--- a/tests/net/java/android/net/dhcp/DhcpPacketTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.annotation.Nullable;
 import android.net.DhcpResults;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
@@ -37,6 +38,7 @@
 import java.io.ByteArrayOutputStream;
 import java.net.Inet4Address;
 import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -56,6 +58,8 @@
     private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
     private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
             SERVER_ADDR, PREFIX_LENGTH);
+    private static final String HOSTNAME = "testhostname";
+    private static final short MTU = 1500;
     // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
     // doesn't use == instead of equals when comparing addresses.
     private static final Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0");
@@ -960,7 +964,8 @@
         assertTrue(msg, Arrays.equals(expected, actual));
     }
 
-    public void checkBuildOfferPacket(int leaseTimeSecs) throws Exception {
+    public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
+            throws Exception {
         final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
         final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
         final int transactionId = 0xdeadbeef;
@@ -971,7 +976,8 @@
                 CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
                 BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
                 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
-                SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, false /* metered */);
+                SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
+                false /* metered */, MTU);
 
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         // BOOTP headers
@@ -1027,12 +1033,22 @@
         // Nameserver
         bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
         bos.write(SERVER_ADDR.getAddress());
+        // Hostname
+        if (hostname != null) {
+            bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
+            bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
+        }
+        // MTU
+        bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
+        bos.write(shortToByteArray(MTU));
         // End options.
         bos.write(0xff);
 
-        final byte[] expected = bos.toByteArray();
-        assertTrue((expected.length & 1) == 0);
+        if ((bos.size() & 1) != 0) {
+            bos.write(0x00);
+        }
 
+        final byte[] expected = bos.toByteArray();
         final byte[] actual = new byte[packet.limit()];
         packet.get(actual);
         final String msg = "Expected:\n  " + HexDump.dumpHexString(expected) +
@@ -1042,13 +1058,18 @@
 
     @Test
     public void testOfferPacket() throws Exception {
-        checkBuildOfferPacket(3600);
-        checkBuildOfferPacket(Integer.MAX_VALUE);
-        checkBuildOfferPacket(0x80000000);
-        checkBuildOfferPacket(INFINITE_LEASE);
+        checkBuildOfferPacket(3600, HOSTNAME);
+        checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
+        checkBuildOfferPacket(0x80000000, HOSTNAME);
+        checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
+        checkBuildOfferPacket(3600, null);
     }
 
     private static byte[] intToByteArray(int val) {
         return ByteBuffer.allocate(4).putInt(val).array();
     }
+
+    private static byte[] shortToByteArray(short val) {
+        return ByteBuffer.allocate(2).putShort(val).array();
+    }
 }
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java
index 45a50d9..df34c73 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java
@@ -17,6 +17,7 @@
 package android.net.dhcp;
 
 import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
+import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
 import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
 import static android.net.dhcp.DhcpPacket.INADDR_ANY;
 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
@@ -87,6 +88,7 @@
             Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
     private static final long TEST_LEASE_TIME_SECS = 3600L;
     private static final int TEST_MTU = 1500;
+    private static final String TEST_HOSTNAME = "testhostname";
 
     private static final int TEST_TRANSACTION_ID = 123;
     private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 };
@@ -96,7 +98,10 @@
     private static final long TEST_CLOCK_TIME = 1234L;
     private static final int TEST_LEASE_EXPTIME_SECS = 3600;
     private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC,
-            TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS*1000L + TEST_CLOCK_TIME, null /* hostname */);
+            TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
+            null /* hostname */);
+    private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC,
+            TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, TEST_HOSTNAME);
 
     @NonNull @Mock
     private Dependencies mDeps;
@@ -217,15 +222,17 @@
     public void testRequest_Selecting_Ack() throws Exception {
         when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
                 eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */,
-                eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */))
-                .thenReturn(TEST_LEASE);
+                eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME)))
+                .thenReturn(TEST_LEASE_WITH_HOSTNAME);
 
         final DhcpRequestPacket request = makeRequestSelectingPacket();
+        request.mHostName = TEST_HOSTNAME;
+        request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
         mServer.processPacket(request, DHCP_CLIENT);
 
         assertResponseSentTo(TEST_CLIENT_ADDR);
         final DhcpAckPacket packet = assertAck(getPacket());
-        assertMatchesTestLease(packet);
+        assertMatchesTestLease(packet, TEST_HOSTNAME);
     }
 
     @Test
@@ -270,14 +277,18 @@
      *  - other request states (init-reboot/renewing/rebinding)
      */
 
-    private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
+    private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
         assertMatchesClient(packet);
         assertFalse(packet.hasExplicitClientId());
         assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier);
         assertEquals(TEST_CLIENT_ADDR, packet.mYourIp);
         assertNotNull(packet.mLeaseTime);
         assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime);
-        assertNull(packet.mHostName);
+        assertEquals(hostname, packet.mHostName);
+    }
+
+    private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
+        assertMatchesTestLease(packet, null);
     }
 
     private void assertMatchesClient(@NonNull DhcpPacket packet) {
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/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
new file mode 100644
index 0000000..122edba
--- /dev/null
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -0,0 +1,327 @@
+/*
+ * 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.netlink;
+
+import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static android.os.Process.INVALID_UID;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_RCVTIMEO;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.netlink.StructNlMsgHdr;
+import android.os.Process;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.SmallTest;
+import android.support.test.InstrumentationRegistry;
+import android.system.Os;
+import android.system.StructTimeval;
+import android.util.Log;
+import java.io.FileDescriptor;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import libcore.util.HexEncoding;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InetDiagSocketTest {
+    private final String TAG = "InetDiagSocketTest";
+    private ConnectivityManager mCm;
+    private Context mContext;
+    private final static int SOCKET_TIMEOUT_MS = 100;
+
+    @Before
+    public void setUp() throws Exception {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = instrumentation.getTargetContext();
+        mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+
+    private class Connection {
+        public int socketDomain;
+        public int socketType;
+        public InetAddress localAddress;
+        public InetAddress remoteAddress;
+        public InetAddress localhostAddress;
+        public InetSocketAddress local;
+        public InetSocketAddress remote;
+        public int protocol;
+        public FileDescriptor localFd;
+        public FileDescriptor remoteFd;
+
+        public FileDescriptor createSocket() throws Exception {
+            return Os.socket(socketDomain, socketType, protocol);
+        }
+
+        public Connection(String to, String from) throws Exception {
+            remoteAddress = InetAddress.getByName(to);
+            if (from != null) {
+                localAddress = InetAddress.getByName(from);
+            } else {
+                localAddress = (remoteAddress instanceof Inet4Address) ?
+                        Inet4Address.getByName("localhost") : Inet6Address.getByName("::");
+            }
+            if ((localAddress instanceof Inet4Address) && (remoteAddress instanceof Inet4Address)) {
+                socketDomain = AF_INET;
+                localhostAddress = Inet4Address.getByName("localhost");
+            } else {
+                socketDomain = AF_INET6;
+                localhostAddress = Inet6Address.getByName("::");
+            }
+        }
+
+        public void close() throws Exception {
+            Os.close(localFd);
+        }
+    }
+
+    private class TcpConnection extends Connection {
+        public TcpConnection(String to, String from) throws Exception {
+            super(to, from);
+            protocol = IPPROTO_TCP;
+            socketType = SOCK_STREAM;
+
+            remoteFd = createSocket();
+            Os.bind(remoteFd, remoteAddress, 0);
+            Os.listen(remoteFd, 10);
+            int remotePort = ((InetSocketAddress) Os.getsockname(remoteFd)).getPort();
+
+            localFd = createSocket();
+            Os.bind(localFd, localAddress, 0);
+            Os.connect(localFd, remoteAddress, remotePort);
+
+            local = (InetSocketAddress) Os.getsockname(localFd);
+            remote = (InetSocketAddress) Os.getpeername(localFd);
+        }
+
+        public void close() throws Exception {
+            super.close();
+            Os.close(remoteFd);
+        }
+    }
+    private class UdpConnection extends Connection {
+        public UdpConnection(String to, String from) throws Exception {
+            super(to, from);
+            protocol = IPPROTO_UDP;
+            socketType = SOCK_DGRAM;
+
+            remoteFd = null;
+            localFd = createSocket();
+            Os.bind(localFd, localAddress, 0);
+
+            Os.connect(localFd, remoteAddress, 7);
+            local = (InetSocketAddress) Os.getsockname(localFd);
+            remote = new InetSocketAddress(remoteAddress, 7);
+        }
+    }
+
+    private void checkConnectionOwnerUid(int protocol, InetSocketAddress local,
+                                         InetSocketAddress remote, boolean expectSuccess) {
+        final int expectedUid = expectSuccess ? Process.myUid() : INVALID_UID;
+        final int uid = mCm.getConnectionOwnerUid(protocol, local, remote);
+        assertEquals(expectedUid, uid);
+    }
+
+    private int findLikelyFreeUdpPort(UdpConnection conn) throws Exception {
+        UdpConnection udp = new UdpConnection(conn.remoteAddress.getHostAddress(),
+                conn.localAddress.getHostAddress());
+        final int localPort = udp.local.getPort();
+        udp.close();
+        return localPort;
+    }
+
+    public void checkGetConnectionOwnerUid(String to, String from) throws Exception {
+        /**
+         * For TCP connections, create a test connection and verify that this
+         * {protocol, local, remote} socket result in receiving a valid UID.
+         */
+        TcpConnection tcp = new TcpConnection(to, from);
+        checkConnectionOwnerUid(tcp.protocol, tcp.local, tcp.remote, true);
+        checkConnectionOwnerUid(IPPROTO_UDP, tcp.local, tcp.remote, false);
+        checkConnectionOwnerUid(tcp.protocol, new InetSocketAddress(0), tcp.remote, false);
+        checkConnectionOwnerUid(tcp.protocol, tcp.local, new InetSocketAddress(0), false);
+        tcp.close();
+
+        /**
+         * For UDP connections, either a complete match {protocol, local, remote} or a
+         * partial match {protocol, local} should return a valid UID.
+         */
+        UdpConnection udp = new UdpConnection(to,from);
+        checkConnectionOwnerUid(udp.protocol, udp.local, udp.remote, true);
+        checkConnectionOwnerUid(udp.protocol, udp.local, new InetSocketAddress(0), true);
+        checkConnectionOwnerUid(IPPROTO_TCP, udp.local, udp.remote, false);
+        checkConnectionOwnerUid(udp.protocol, new InetSocketAddress(findLikelyFreeUdpPort(udp)),
+                udp.remote, false);
+        udp.close();
+    }
+
+    @Test
+    public void testGetConnectionOwnerUid() throws Exception {
+        checkGetConnectionOwnerUid("::", null);
+        checkGetConnectionOwnerUid("::", "::");
+        checkGetConnectionOwnerUid("0.0.0.0", null);
+        checkGetConnectionOwnerUid("0.0.0.0", "0.0.0.0");
+        checkGetConnectionOwnerUid("127.0.0.1", null);
+        checkGetConnectionOwnerUid("127.0.0.1", "127.0.0.2");
+        checkGetConnectionOwnerUid("::1", null);
+        checkGetConnectionOwnerUid("::1", "::1");
+    }
+
+    // Hexadecimal representation of InetDiagReqV2 request.
+    private static final String INET_DIAG_REQ_V2_UDP_INET4_HEX =
+            // struct nlmsghdr
+            "48000000" +     // length = 72
+            "1400" +         // type = SOCK_DIAG_BY_FAMILY
+            "0103" +         // flags = NLM_F_REQUEST | NLM_F_DUMP
+            "00000000" +     // seqno
+            "00000000" +     // pid (0 == kernel)
+            // struct inet_diag_req_v2
+            "02" +           // family = AF_INET
+            "11" +           // protcol = IPPROTO_UDP
+            "00" +           // idiag_ext
+            "00" +           // pad
+            "ffffffff" +     // idiag_states
+            // inet_diag_sockid
+            "a5de" +         // idiag_sport = 42462
+            "b971" +         // idiag_dport = 47473
+            "0a006402000000000000000000000000" + // idiag_src = 10.0.100.2
+            "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8
+            "00000000" +     // idiag_if
+            "ffffffffffffffff"; // idiag_cookie = INET_DIAG_NOCOOKIE
+    private static final byte[] INET_DIAG_REQ_V2_UDP_INET4_BYTES =
+            HexEncoding.decode(INET_DIAG_REQ_V2_UDP_INET4_HEX.toCharArray(), false);
+
+    @Test
+    public void testInetDiagReqV2UdpInet4() throws Exception {
+        InetSocketAddress local = new InetSocketAddress(InetAddress.getByName("10.0.100.2"),
+                42462);
+        InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.8.8"),
+                47473);
+        final byte[] msg = InetDiagMessage.InetDiagReqV2(IPPROTO_UDP, local, remote, AF_INET,
+                (short) (NLM_F_REQUEST | NLM_F_DUMP));
+        assertArrayEquals(INET_DIAG_REQ_V2_UDP_INET4_BYTES, msg);
+    }
+
+    // Hexadecimal representation of InetDiagReqV2 request.
+    private static final String INET_DIAG_REQ_V2_TCP_INET6_HEX =
+            // struct nlmsghdr
+            "48000000" +     // length = 72
+            "1400" +         // type = SOCK_DIAG_BY_FAMILY
+            "0100" +         // flags = NLM_F_REQUEST
+            "00000000" +     // seqno
+            "00000000" +     // pid (0 == kernel)
+            // struct inet_diag_req_v2
+            "0a" +           // family = AF_INET6
+            "06" +           // protcol = IPPROTO_TCP
+            "00" +           // idiag_ext
+            "00" +           // pad
+            "ffffffff" +     // idiag_states
+                // inet_diag_sockid
+                "a5de" +         // idiag_sport = 42462
+                "b971" +         // idiag_dport = 47473
+                "fe8000000000000086c9b2fffe6aed4b" + // idiag_src = fe80::86c9:b2ff:fe6a:ed4b
+                "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8
+                "00000000" +     // idiag_if
+                "ffffffffffffffff"; // idiag_cookie = INET_DIAG_NOCOOKIE
+    private static final byte[] INET_DIAG_REQ_V2_TCP_INET6_BYTES =
+            HexEncoding.decode(INET_DIAG_REQ_V2_TCP_INET6_HEX.toCharArray(), false);
+
+    @Test
+    public void testInetDiagReqV2TcpInet6() throws Exception {
+        InetSocketAddress local = new InetSocketAddress(
+                InetAddress.getByName("fe80::86c9:b2ff:fe6a:ed4b"), 42462);
+        InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.8.8"),
+                47473);
+        byte[] msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, remote, AF_INET6,
+                NLM_F_REQUEST);
+
+        assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_BYTES, msg);
+    }
+
+    // Hexadecimal representation of InetDiagReqV2 request.
+    private static final String INET_DIAG_MSG_HEX =
+            // struct nlmsghdr
+            "58000000" +     // length = 88
+            "1400" +         // type = SOCK_DIAG_BY_FAMILY
+            "0200" +         // flags = NLM_F_MULTI
+            "00000000" +     // seqno
+            "f5220000" +     // pid (0 == kernel)
+            // struct inet_diag_msg
+            "0a" +           // family = AF_INET6
+            "01" +           // idiag_state
+            "00" +           // idiag_timer
+            "00" +           // idiag_retrans
+                // inet_diag_sockid
+                "a817" +     // idiag_sport = 43031
+                "960f" +     // idiag_dport = 38415
+                "fe8000000000000086c9b2fffe6aed4b" + // idiag_src = fe80::86c9:b2ff:fe6a:ed4b
+                "00000000000000000000ffff08080808" + // idiag_dst = 8.8.8.8
+                "00000000" + // idiag_if
+                "ffffffffffffffff" + // idiag_cookie = INET_DIAG_NOCOOKIE
+            "00000000" +     // idiag_expires
+            "00000000" +     // idiag_rqueue
+            "00000000" +     // idiag_wqueue
+            "a3270000" +     // idiag_uid
+            "A57E1900";      // idiag_inode
+    private static final byte[] INET_DIAG_MSG_BYTES =
+            HexEncoding.decode(INET_DIAG_MSG_HEX.toCharArray(), false);
+
+    @Test
+    public void testParseInetDiagResponse() throws Exception {
+        final ByteBuffer byteBuffer = ByteBuffer.wrap(INET_DIAG_MSG_BYTES);
+        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+        final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
+        assertNotNull(msg);
+
+        assertTrue(msg instanceof InetDiagMessage);
+        final InetDiagMessage inetDiagMsg = (InetDiagMessage) msg;
+        assertEquals(10147, inetDiagMsg.mStructInetDiagMsg.idiag_uid);
+
+        final StructNlMsgHdr hdr = inetDiagMsg.getHeader();
+        assertNotNull(hdr);
+        assertEquals(NetlinkConstants.SOCK_DIAG_BY_FAMILY, hdr.nlmsg_type);
+        assertEquals(StructNlMsgHdr.NLM_F_MULTI, hdr.nlmsg_flags);
+        assertEquals(0, hdr.nlmsg_seq);
+        assertEquals(8949, hdr.nlmsg_pid);
+    }
+}
diff --git a/tests/net/java/android/net/util/SharedLogTest.java b/tests/net/java/android/net/util/SharedLogTest.java
index d46facf..8604860 100644
--- a/tests/net/java/android/net/util/SharedLogTest.java
+++ b/tests/net/java/android/net/util/SharedLogTest.java
@@ -44,6 +44,8 @@
         final SharedLog logLevel2a = logTop.forSubComponent("twoA");
         final SharedLog logLevel2b = logTop.forSubComponent("twoB");
         logLevel2b.e("2b or not 2b");
+        logLevel2b.e("No exception", null);
+        logLevel2b.e("Wait, here's one", new Exception("Test"));
         logLevel2a.w("second post?");
 
         final SharedLog logLevel3 = logLevel2a.forSubComponent("three");
@@ -54,6 +56,9 @@
         final String[] expected = {
             " - MARK first post!",
             " - [twoB] ERROR 2b or not 2b",
+            " - [twoB] ERROR No exception",
+            // No stacktrace in shared log, only in logcat
+            " - [twoB] ERROR Wait, here's one: Test",
             " - [twoA] WARN second post?",
             " - still logging",
             " - [twoA.three] 3 >> 2",
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e3db7e8..1c77fcc 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -79,6 +79,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -248,7 +249,7 @@
         @Spy private Resources mResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
 
-        MockContext(Context base) {
+        MockContext(Context base, ContentProvider settingsProvider) {
             super(base);
 
             mResources = spy(base.getResources());
@@ -260,7 +261,7 @@
                     });
 
             mContentResolver = new MockContentResolver();
-            mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+            mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
         }
 
         @Override
@@ -1048,7 +1049,9 @@
             Looper.prepare();
         }
 
-        mServiceContext = new MockContext(InstrumentationRegistry.getContext());
+        FakeSettingsProvider.clearSettingsProvider();
+        mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
+                new FakeSettingsProvider());
         LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
         LocalServices.addService(
                 NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
@@ -1067,13 +1070,13 @@
 
         // Ensure that the default setting for Captive Portals is used for most tests
         setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
-        setMobileDataAlwaysOn(false);
+        setAlwaysOnNetworks(false);
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
     }
 
     @After
     public void tearDown() throws Exception {
-        setMobileDataAlwaysOn(false);
+        setAlwaysOnNetworks(false);
         if (mCellNetworkAgent != null) {
             mCellNetworkAgent.disconnect();
             mCellNetworkAgent = null;
@@ -1086,6 +1089,7 @@
             mEthernetNetworkAgent.disconnect();
             mEthernetNetworkAgent = null;
         }
+        FakeSettingsProvider.clearSettingsProvider();
     }
 
     private static int transportToLegacyType(int transport) {
@@ -2023,7 +2027,7 @@
 
     @Test
     public void testNetworkGoesIntoBackgroundAfterLinger() {
-        setMobileDataAlwaysOn(true);
+        setAlwaysOnNetworks(true);
         NetworkRequest request = new NetworkRequest.Builder()
                 .clearCapabilities()
                 .build();
@@ -2768,10 +2772,10 @@
         Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode);
     }
 
-    private void setMobileDataAlwaysOn(boolean enable) {
+    private void setAlwaysOnNetworks(boolean enable) {
         ContentResolver cr = mServiceContext.getContentResolver();
         Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0);
-        mService.updateMobileDataAlwaysOn();
+        mService.updateAlwaysOnNetworks();
         waitForIdle();
     }
 
@@ -2793,7 +2797,7 @@
     public void testBackgroundNetworks() throws Exception {
         // Create a background request. We can't do this ourselves because ConnectivityService
         // doesn't have an API for it. So just turn on mobile data always on.
-        setMobileDataAlwaysOn(true);
+        setAlwaysOnNetworks(true);
         final NetworkRequest request = new NetworkRequest.Builder().build();
         final NetworkRequest fgRequest = new NetworkRequest.Builder()
                 .addCapability(NET_CAPABILITY_FOREGROUND).build();
@@ -2991,7 +2995,7 @@
 
         // Turn on mobile data always on. The factory starts looking again.
         testFactory.expectAddRequests(1);
-        setMobileDataAlwaysOn(true);
+        setAlwaysOnNetworks(true);
         testFactory.waitForNetworkRequests(2);
         assertTrue(testFactory.getMyStartRequested());
 
@@ -3011,7 +3015,7 @@
 
         // Turn off mobile data always on and expect the request to disappear...
         testFactory.expectRemoveRequests(1);
-        setMobileDataAlwaysOn(false);
+        setAlwaysOnNetworks(false);
         testFactory.waitForNetworkRequests(1);
 
         // ...  and cell data to be torn down.
@@ -4532,4 +4536,78 @@
         mCellNetworkAgent.disconnect();
         mCm.unregisterNetworkCallback(networkCallback);
     }
+
+    @Test
+    public void testDataActivityTracking() throws RemoteException {
+        final TestNetworkCallback networkCallback = new TestNetworkCallback();
+        final NetworkRequest networkRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName(MOBILE_IFNAME);
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        reset(mNetworkManagementService);
+        mCellNetworkAgent.connect(true);
+        networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+                eq(ConnectivityManager.TYPE_MOBILE));
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        final LinkProperties wifiLp = new LinkProperties();
+        wifiLp.setInterfaceName(WIFI_IFNAME);
+        mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+
+        // Network switch
+        reset(mNetworkManagementService);
+        mWiFiNetworkAgent.connect(true);
+        networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
+                eq(ConnectivityManager.TYPE_WIFI));
+        verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME));
+
+        // Disconnect wifi and switch back to cell
+        reset(mNetworkManagementService);
+        mWiFiNetworkAgent.disconnect();
+        networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        assertNoCallbacks(networkCallback);
+        verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+        verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+                eq(ConnectivityManager.TYPE_MOBILE));
+
+        // reconnect wifi
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        wifiLp.setInterfaceName(WIFI_IFNAME);
+        mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+        mWiFiNetworkAgent.connect(true);
+        networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+
+        // Disconnect cell
+        reset(mNetworkManagementService);
+        mCellNetworkAgent.disconnect();
+        networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        // LOST callback is triggered earlier than removing idle timer. Broadcast should also be
+        // sent as network being switched. Ensure rule removal for cell will not be triggered
+        // unexpectedly before network being removed.
+        waitForIdle();
+        verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME));
+        verify(mNetworkManagementService, times(1)).removeNetwork(
+                eq(mCellNetworkAgent.getNetwork().netId));
+
+        // Disconnect wifi
+        ConditionVariable cv = waitForConnectivityBroadcasts(1);
+        reset(mNetworkManagementService);
+        mWiFiNetworkAgent.disconnect();
+        waitFor(cv);
+        verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+
+        // Clean up
+        mCm.unregisterNetworkCallback(networkCallback);
+    }
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 99a5a69..9b919abf 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
@@ -34,8 +36,10 @@
 import android.net.IpSecConfig;
 import android.net.IpSecManager;
 import android.net.IpSecSpiResponse;
+import android.net.IpSecTransform;
 import android.net.IpSecTransformResponse;
 import android.net.IpSecTunnelInterfaceResponse;
+import android.net.IpSecUdpEncapResponse;
 import android.net.LinkAddress;
 import android.net.Network;
 import android.net.NetworkUtils;
@@ -62,16 +66,17 @@
 
     private static final int TEST_SPI = 0xD1201D;
 
-    private final String mDestinationAddr;
     private final String mSourceAddr;
+    private final String mDestinationAddr;
     private final LinkAddress mLocalInnerAddress;
+    private final int mFamily;
 
     @Parameterized.Parameters
     public static Collection ipSecConfigs() {
         return Arrays.asList(
                 new Object[][] {
-                {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"},
-                {"2601::2", "2601::10", "2001:db8::1/64"}
+                {"1.2.3.4", "8.8.4.4", "10.0.1.1/24", AF_INET},
+                {"2601::2", "2601::10", "2001:db8::1/64", AF_INET6}
         });
     }
 
@@ -129,12 +134,14 @@
             new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
     private static final IpSecAlgorithm AEAD_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+    private static final int REMOTE_ENCAP_PORT = 4500;
 
     public IpSecServiceParameterizedTest(
-            String sourceAddr, String destAddr, String localInnerAddr) {
+            String sourceAddr, String destAddr, String localInnerAddr, int family) {
         mSourceAddr = sourceAddr;
         mDestinationAddr = destAddr;
         mLocalInnerAddress = new LinkAddress(localInnerAddr);
+        mFamily = family;
     }
 
     @Before
@@ -157,6 +164,8 @@
             .thenReturn(AppOpsManager.MODE_IGNORED);
     }
 
+    //TODO: Add a test to verify SPI.
+
     @Test
     public void testIpSecServiceReserveSpi() throws Exception {
         when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
@@ -257,6 +266,47 @@
         config.setAuthentication(AUTH_ALGO);
     }
 
+    private void addEncapSocketToIpSecConfig(int resourceId, IpSecConfig config) throws Exception {
+        config.setEncapType(IpSecTransform.ENCAP_ESPINUDP);
+        config.setEncapSocketResourceId(resourceId);
+        config.setEncapRemotePort(REMOTE_ENCAP_PORT);
+    }
+
+    private void verifyTransformNetdCalledForCreatingSA(
+            IpSecConfig config, IpSecTransformResponse resp) throws Exception {
+        verifyTransformNetdCalledForCreatingSA(config, resp, 0);
+    }
+
+    private void verifyTransformNetdCalledForCreatingSA(
+            IpSecConfig config, IpSecTransformResponse resp, int encapSocketPort) throws Exception {
+        IpSecAlgorithm auth = config.getAuthentication();
+        IpSecAlgorithm crypt = config.getEncryption();
+        IpSecAlgorithm authCrypt = config.getAuthenticatedEncryption();
+
+        verify(mMockNetd, times(1))
+                .ipSecAddSecurityAssociation(
+                        eq(mUid),
+                        eq(config.getMode()),
+                        eq(config.getSourceAddress()),
+                        eq(config.getDestinationAddress()),
+                        eq((config.getNetwork() != null) ? config.getNetwork().netId : 0),
+                        eq(TEST_SPI),
+                        eq(0),
+                        eq(0),
+                        eq((auth != null) ? auth.getName() : ""),
+                        eq((auth != null) ? auth.getKey() : new byte[] {}),
+                        eq((auth != null) ? auth.getTruncationLengthBits() : 0),
+                        eq((crypt != null) ? crypt.getName() : ""),
+                        eq((crypt != null) ? crypt.getKey() : new byte[] {}),
+                        eq((crypt != null) ? crypt.getTruncationLengthBits() : 0),
+                        eq((authCrypt != null) ? authCrypt.getName() : ""),
+                        eq((authCrypt != null) ? authCrypt.getKey() : new byte[] {}),
+                        eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0),
+                        eq(config.getEncapType()),
+                        eq(encapSocketPort),
+                        eq(config.getEncapRemotePort()));
+    }
+
     @Test
     public void testCreateTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
@@ -267,28 +317,7 @@
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(mUid),
-                        anyInt(),
-                        anyString(),
-                        anyString(),
-                        anyInt(),
-                        eq(TEST_SPI),
-                        anyInt(),
-                        anyInt(),
-                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
-                        eq(AUTH_KEY),
-                        anyInt(),
-                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
-                        eq(CRYPT_KEY),
-                        anyInt(),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
     }
 
     @Test
@@ -302,28 +331,59 @@
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(mUid),
-                        anyInt(),
-                        anyString(),
-                        anyString(),
-                        anyInt(),
-                        eq(TEST_SPI),
-                        anyInt(),
-                        anyInt(),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
-                        eq(AEAD_KEY),
-                        anyInt(),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+    }
+
+    @Test
+    public void testCreateTransportModeTransformWithEncap() throws Exception {
+        IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TRANSPORT);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+        addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
+
+        if (mFamily == AF_INET) {
+            IpSecTransformResponse createTransformResp =
+                    mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+            assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+            verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
+        } else {
+            try {
+                IpSecTransformResponse createTransformResp =
+                        mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+                fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
+            } catch (IllegalArgumentException expected) {
+            }
+        }
+    }
+
+    @Test
+    public void testCreateTunnelModeTransformWithEncap() throws Exception {
+        IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+        addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
+
+        if (mFamily == AF_INET) {
+            IpSecTransformResponse createTransformResp =
+                    mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+            assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+            verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
+        } else {
+            try {
+                IpSecTransformResponse createTransformResp =
+                        mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+                fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
+            } catch (IllegalArgumentException expected) {
+            }
+        }
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 0d3b8e4..a6ed9f2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -33,6 +33,7 @@
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -75,6 +76,9 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+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.NetworkConstants;
@@ -84,6 +88,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.INetworkManagementService;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
@@ -99,10 +104,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;
 
@@ -147,6 +150,7 @@
     @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
     @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
     @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+    @Mock private DhcpServer mDhcpServer;
     @Mock private INetd mNetd;
 
     private final MockTetheringDependencies mTetheringDependencies =
@@ -190,7 +194,7 @@
 
     public class MockTetheringDependencies extends TetheringDependencies {
         StateMachine upstreamNetworkMonitorMasterSM;
-        ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+        ArrayList<IpServer> ipv6CoordinatorNotifyList;
         int isTetheringSupportedCalls;
 
         public void reset() {
@@ -213,29 +217,41 @@
 
         @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
+                public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+                        DhcpServingParams params, SharedLog log) {
+                    return mDhcpServer;
+                }
+            };
         }
 
         @Override
@@ -328,6 +344,7 @@
         mServiceContext = new MockContext(mContext);
         mContentResolver = new MockContentResolver(mServiceContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
         mIntents = new Vector<>();
         mBroadcastReceiver = new BroadcastReceiver() {
             @Override
@@ -338,12 +355,16 @@
         mServiceContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(ACTION_TETHER_STATE_CHANGED));
         mTetheringDependencies.reset();
-        mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
-                                   mLooper.getLooper(), mSystemProperties,
-                                   mTetheringDependencies);
+        mTethering = makeTethering();
         verify(mNMService).registerTetheringStatsProvider(any(), anyString());
     }
 
+    private Tethering makeTethering() {
+        return new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
+                mLooper.getLooper(), mSystemProperties,
+                mTetheringDependencies);
+    }
+
     @After
     public void tearDown() {
         mServiceContext.unregisterReceiver(mBroadcastReceiver);
@@ -458,9 +479,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 +578,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);
@@ -592,6 +613,18 @@
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
+        verify(mDhcpServer, times(1)).start();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_IPv4LegacyDhcp() {
+        Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+        mTethering = makeTethering();
+        final NetworkState upstreamState = buildMobileIPv4UpstreamState();
+        runUsbTethering(upstreamState);
+        sendIPv6TetherUpdates(upstreamState);
+
+        verify(mDhcpServer, never()).start();
     }
 
     @Test
@@ -615,6 +648,7 @@
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mRouterAdvertisementDaemon, times(1)).start();
+        verify(mDhcpServer, times(1)).start();
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -628,6 +662,7 @@
 
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mDhcpServer, times(1)).start();
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
                 TEST_XLAT_MOBILE_IFNAME);
@@ -644,6 +679,7 @@
         runUsbTethering(upstreamState);
 
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mDhcpServer, times(1)).start();
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
 
         // Then 464xlat comes up
@@ -666,6 +702,8 @@
         // Forwarding was not re-added for v6 (still times(1))
         verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        // DHCP not restarted on downstream (still times(1))
+        verify(mDhcpServer, times(1)).start();
     }
 
     @Test
@@ -812,7 +850,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/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index bb31230..5217784 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -225,13 +225,4 @@
         final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
         assertFalse(cfg.enableLegacyDhcpServer);
     }
-
-    @Test
-    public void testNewDhcpServerDefault() {
-        Settings.Global.putString(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, null);
-
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
-        // TODO: change to false when new server is promoted to default
-        assertTrue(cfg.enableLegacyDhcpServer);
-    }
 }
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index f719552..b46a503 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -53,11 +53,11 @@
   ConfigDescription();
   ConfigDescription(const android::ResTable_config& o);  // NOLINT(implicit)
   ConfigDescription(const ConfigDescription& o);
-  ConfigDescription(ConfigDescription&& o);
+  ConfigDescription(ConfigDescription&& o) noexcept;
 
   ConfigDescription& operator=(const android::ResTable_config& o);
   ConfigDescription& operator=(const ConfigDescription& o);
-  ConfigDescription& operator=(ConfigDescription&& o);
+  ConfigDescription& operator=(ConfigDescription&& o) noexcept;
 
   ConfigDescription CopyWithoutSdkVersion() const;
 
@@ -124,7 +124,7 @@
   *static_cast<android::ResTable_config*>(this) = o;
 }
 
-inline ConfigDescription::ConfigDescription(ConfigDescription&& o) {
+inline ConfigDescription::ConfigDescription(ConfigDescription&& o) noexcept {
   *this = o;
 }
 
@@ -141,7 +141,7 @@
   return *this;
 }
 
-inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) {
+inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) noexcept {
   *this = o;
   return *this;
 }
diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp
index c0eaa8e..7872738 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -41,46 +41,46 @@
   ASSERT_FALSE(in.HadError());
   EXPECT_THAT(in.ByteCount(), Eq(0u));
 
-  const char* buffer;
+  const void* buffer;
   size_t size;
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)) << in.GetError();
+  ASSERT_TRUE(in.Next(&buffer, &size)) << in.GetError();
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(in.ByteCount(), Eq(10u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("this is a "));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("this is a "));
 
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_TRUE(in.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(in.ByteCount(), Eq(20u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("cool strin"));
 
   in.BackUp(5u);
   EXPECT_THAT(in.ByteCount(), Eq(15u));
 
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_TRUE(in.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(5u));
   ASSERT_THAT(buffer, NotNull());
   ASSERT_THAT(in.ByteCount(), Eq(20u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("strin"));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("strin"));
 
   // Backup 1 more than possible. Should clamp.
   in.BackUp(11u);
   EXPECT_THAT(in.ByteCount(), Eq(10u));
 
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_TRUE(in.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   ASSERT_THAT(in.ByteCount(), Eq(20u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("cool strin"));
 
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_TRUE(in.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(1u));
   ASSERT_THAT(buffer, NotNull());
   ASSERT_THAT(in.ByteCount(), Eq(21u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("g"));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("g"));
 
-  EXPECT_FALSE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  EXPECT_FALSE(in.Next(&buffer, &size));
   EXPECT_FALSE(in.HadError());
 }
 
@@ -93,25 +93,25 @@
   ASSERT_FALSE(out.HadError());
   EXPECT_THAT(out.ByteCount(), Eq(0u));
 
-  char* buffer;
+  void* buffer;
   size_t size;
-  ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+  ASSERT_TRUE(out.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(out.ByteCount(), Eq(10u));
-  memcpy(buffer, input.c_str(), size);
+  memcpy(reinterpret_cast<char*>(buffer), input.c_str(), size);
 
-  ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+  ASSERT_TRUE(out.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(out.ByteCount(), Eq(20u));
-  memcpy(buffer, input.c_str() + 10u, size);
+  memcpy(reinterpret_cast<char*>(buffer), input.c_str() + 10u, size);
 
-  ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+  ASSERT_TRUE(out.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(out.ByteCount(), Eq(30u));
-  buffer[0] = input[20u];
+  reinterpret_cast<char*>(buffer)[0] = input[20u];
   out.BackUp(size - 1);
   EXPECT_THAT(out.ByteCount(), Eq(21u));
 
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index be67c9c..10e504e 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -26,21 +26,20 @@
 #include "util/Maybe.h"
 #include "xml/XmlDom.h"
 
-using ::android::StringPiece;
 using ::aapt::text::IsJavaIdentifier;
 
 namespace aapt {
 
-static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
+static Maybe<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
                                                 const std::string& value) {
-  StringPiece result = value;
+  std::string result = value;
   size_t pos = value.rfind('.');
   if (pos != std::string::npos) {
     result = result.substr(pos + 1);
   }
 
   // Normalize only the java identifier, leave the original value unchanged.
-  if (result.contains("-")) {
+  if (result.find("-") != std::string::npos) {
     result = JavaClassGenerator::TransformToFieldName(result);
   }
 
@@ -64,7 +63,7 @@
     return false;
   }
 
-  Maybe<StringPiece> result =
+  Maybe<std::string> result =
       ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value);
   if (!result) {
     return false;
diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h
index 3045255..d4b3abc 100644
--- a/tools/aapt2/util/BigBuffer.h
+++ b/tools/aapt2/util/BigBuffer.h
@@ -68,7 +68,7 @@
    */
   explicit BigBuffer(size_t block_size);
 
-  BigBuffer(BigBuffer&& rhs);
+  BigBuffer(BigBuffer&& rhs) noexcept;
 
   /**
    * Number of occupied bytes in all the allocated blocks.
@@ -136,7 +136,7 @@
 inline BigBuffer::BigBuffer(size_t block_size)
     : block_size_(block_size), size_(0) {}
 
-inline BigBuffer::BigBuffer(BigBuffer&& rhs)
+inline BigBuffer::BigBuffer(BigBuffer&& rhs) noexcept
     : block_size_(rhs.block_size_),
       size_(rhs.size_),
       blocks_(std::move(rhs.blocks_)) {}
diff --git a/tools/aapt2/util/ImmutableMap.h b/tools/aapt2/util/ImmutableMap.h
index 59858e4..1727b18 100644
--- a/tools/aapt2/util/ImmutableMap.h
+++ b/tools/aapt2/util/ImmutableMap.h
@@ -32,8 +32,8 @@
   using const_iterator =
       typename std::vector<std::pair<TKey, TValue>>::const_iterator;
 
-  ImmutableMap(ImmutableMap&&) = default;
-  ImmutableMap& operator=(ImmutableMap&&) = default;
+  ImmutableMap(ImmutableMap&&) noexcept = default;
+  ImmutableMap& operator=(ImmutableMap&&) noexcept = default;
 
   static ImmutableMap<TKey, TValue> CreatePreSorted(
       std::initializer_list<std::pair<TKey, TValue>> list) {
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index 9a82418..031276c 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -46,7 +46,7 @@
   template <typename U>
   Maybe(const Maybe<U>& rhs);  // NOLINT(implicit)
 
-  Maybe(Maybe&& rhs);
+  Maybe(Maybe&& rhs) noexcept;
 
   template <typename U>
   Maybe(Maybe<U>&& rhs);  // NOLINT(implicit)
@@ -56,7 +56,7 @@
   template <typename U>
   Maybe& operator=(const Maybe<U>& rhs);
 
-  Maybe& operator=(Maybe&& rhs);
+  Maybe& operator=(Maybe&& rhs) noexcept;
 
   template <typename U>
   Maybe& operator=(Maybe<U>&& rhs);
@@ -134,7 +134,7 @@
 }
 
 template <typename T>
-Maybe<T>::Maybe(Maybe&& rhs) : nothing_(rhs.nothing_) {
+Maybe<T>::Maybe(Maybe&& rhs) noexcept : nothing_(rhs.nothing_) {
   if (!rhs.nothing_) {
     rhs.nothing_ = true;
 
@@ -192,7 +192,7 @@
 }
 
 template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
+inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) noexcept {
   // Delegate to the actual assignment.
   return move(std::forward<Maybe<T>>(rhs));
 }
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index 6c46e67..fdc800b 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -212,8 +212,8 @@
     move_from_files(args.input_greylists, uncategorized, light_greylist)
     move_from_files(args.input_blacklists, uncategorized, blacklist)
 
-    # Iterate over all uncategorized members and move serialization API to light greylist.
-    move_serialization(uncategorized, light_greylist)
+    # Iterate over all uncategorized members and move serialization API to whitelist.
+    move_serialization(uncategorized, whitelist)
 
     # Extract package names of members from whitelist and light greylist, which
     # are assumed to have been finalized at this point. Assign all uncategorized
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
index 8f79318..4716241 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py
@@ -85,5 +85,23 @@
         self.assertEqual(
             dst, set([ "Lfoo/bar/ClassA;->abc()J", "Lfoo/bar/ClassA;->def()J" ]))
 
+    def test_move_serialization(self):
+        # All the entries should be moved apart from the last one
+        src = set([ "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)V",
+                    "Lfoo/bar/ClassA;->readObjectNoData()V",
+                    "Lfoo/bar/ClassA;->readResolve()Ljava/lang/Object;",
+                    "Lfoo/bar/ClassA;->serialVersionUID:J",
+                    "Lfoo/bar/ClassA;->serialPersistentFields:[Ljava/io/ObjectStreamField;",
+                    "Lfoo/bar/ClassA;->writeObject(Ljava/io/ObjectOutputStream;)V",
+                    "Lfoo/bar/ClassA;->writeReplace()Ljava/lang/Object;",
+                    # Should not be moved as signature does not match
+                    "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)I"])
+        expectedToMove = len(src) - 1
+        dst = set()
+        packages = set([ "Lfoo/bar/" ])
+        move_serialization(src, dst)
+        self.assertEqual(len(src), 1)
+        self.assertEqual(len(dst), expectedToMove)
+
 if __name__ == '__main__':
     unittest.main()
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[*]}") )
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 3689a8f..4b5677a 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -140,7 +140,7 @@
     size_t base = 0;
     size_t found;
     while (true) {
-        found = args.find_first_of(" ", base);
+        found = args.find_first_of(' ', base);
         if (found != base) {
             string arg = args.substr(base, found - base);
             printf(" \"%s\",", arg.c_str());