Merge "Also pin vdex file for camera"
diff --git a/Android.bp b/Android.bp
index 9ba4004..6fc0aa9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,6 +15,7 @@
 subdirs = [
     "core/jni",
     "libs/*",
+    "media/*",
     "native/android",
     "native/graphics/jni",
 ]
diff --git a/Android.mk b/Android.mk
index 02caa0a..2571afb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -553,7 +553,6 @@
         frameworks/base/telephony/java/android/telephony/mbms/FileInfo.aidl \
         frameworks/base/telephony/java/android/telephony/mbms/FileServiceInfo.aidl \
         frameworks/base/telephony/java/android/telephony/mbms/ServiceInfo.aidl \
-	frameworks/base/telephony/java/android/telephony/mbms/StreamingService.aidl \
         frameworks/base/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl \
 	frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
 	frameworks/base/telephony/java/android/telephony/SubscriptionInfo.aidl \
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 3135b30..b12ff72 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,7 +16,6 @@
 
 package android.bluetooth;
 
-import android.content.Context;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -927,6 +926,31 @@
     }
 
     /**
+     * Discovers a service by UUID. This is exposed only for passing PTS tests.
+     * It should never be used by real applications. The service is not searched
+     * for characteristics and descriptors, or returned in any callback.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @return true, if the remote service discovery has been started
+     * @hide
+     */
+    public boolean discoverServiceByUuid(UUID uuid) {
+        if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
+        if (mService == null || mClientIf == 0) return false;
+
+        mServices.clear();
+
+        try {
+            mService.discoverServiceByUuid(mClientIf, mDevice.getAddress(), new ParcelUuid(uuid));
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Returns a list of GATT services offered by the remote device.
      *
      * <p>This function requires that service discovery has been completed
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 63bd942..4ff5976 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -76,6 +76,7 @@
     void clientReadPhy(in int clientIf, in String address);
     void refreshDevice(in int clientIf, in String address);
     void discoverServices(in int clientIf, in String address);
+    void discoverServiceByUuid(in int clientIf, in String address, in ParcelUuid uuid);
     void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq);
     void readUsingCharacteristicUuid(in int clientIf, in String address, in ParcelUuid uuid,
                            in int startHandle, in int endHandle, in int authReq);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6320134..234bef7 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -44,7 +44,6 @@
 import android.telephony.SubscriptionManager;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.SparseArray;
 import android.util.SparseIntArray;
 
 import com.android.internal.telephony.ITelephony;
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 0d62054..b72e783 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -53,7 +53,7 @@
 
     private final boolean mIsOwner;
     private final long mMemoryAddr;
-    private int mFd;
+    private int mFd = -1;
 
     /**
      * Creates a new instance.
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 527582b..91429a6 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -717,14 +717,6 @@
     public static void applyInvokeWithSystemProperty(Arguments args) {
         if (args.invokeWith == null && args.niceName != null) {
             String property = "wrap." + args.niceName;
-            if (property.length() > 31) {
-                // Properties with a trailing "." are illegal.
-                if (property.charAt(30) != '.') {
-                    property = property.substring(0, 31);
-                } else {
-                    property = property.substring(0, 30);
-                }
-            }
             args.invokeWith = SystemProperties.get(property);
             if (args.invokeWith != null && args.invokeWith.length() == 0) {
                 args.invokeWith = null;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 7d5f8ee..c8784f8 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -271,7 +271,15 @@
     ],
 
     local_include_dirs: ["android/graphics"],
-    export_include_dirs: ["include"],
-    // AndroidRuntime.h depends on nativehelper/jni.h
-    export_shared_lib_headers: ["libnativehelper"],
+    export_include_dirs: [
+        ".",
+        "include",
+    ],
+    export_shared_lib_headers: [
+        // AndroidRuntime.h depends on nativehelper/jni.h
+        "libnativehelper",
+
+        // GraphicsJNI.h includes hwui headers
+        "libhwui",
+    ],
 }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 6000fb5..d73e7dd 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -27,6 +27,7 @@
 #include <fcntl.h>
 #include <grp.h>
 #include <inttypes.h>
+#include <malloc.h>
 #include <mntent.h>
 #include <paths.h>
 #include <signal.h>
@@ -519,6 +520,9 @@
     // The child process.
     gMallocLeakZygoteChild = 1;
 
+    // Set the jemalloc decay time to 1.
+    mallopt(M_DECAY_TIME, 1);
+
     // Clean up any descriptors which must be closed immediately
     DetachDescriptors(env, fdsToClose);
 
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index faf04b1..e69a2cc 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -17,7 +17,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
     frameworks-base-testutils \
-    mockito-target
+    mockito-target \
+    legacy-android-tests
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
@@ -27,4 +28,4 @@
 
 include $(BUILD_PACKAGE)
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index 0835cad..8efda2a 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -20,9 +20,11 @@
 # =============================
 # This contains the junit.framework and android.test classes that were in
 # Android API level 25 excluding those from android.test.runner.
+# Also contains the com.android.internal.util.Predicate[s] classes.
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
 LOCAL_MODULE := legacy-test
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
@@ -30,20 +32,35 @@
 include $(BUILD_JAVA_LIBRARY)
 
 # Build the legacy-android-test library
-# =============================
+# =====================================
 # This contains the android.test classes that were in Android API level 25,
 # including those from android.test.runner.
+# Also contains the com.android.internal.util.Predicate[s] classes.
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src/android) \
-    $(call all-java-files-under, ../test-runner/src/android)
+    $(call all-java-files-under, ../test-runner/src/android) \
+    $(call all-java-files-under, src/com)
 LOCAL_MODULE := legacy-android-test
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVA_LIBRARIES := core-oj core-libart framework junit
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
+# Build the legacy-android-tests library
+# ======================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, tests)
+LOCAL_MODULE := legacy-android-tests
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework junit
+LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
 ifeq ($(HOST_OS),linux)
 # Build the legacy-performance-test-hostdex library
 # =================================================
diff --git a/core/java/com/android/internal/util/Predicate.java b/legacy-test/src/com/android/internal/util/Predicate.java
similarity index 100%
rename from core/java/com/android/internal/util/Predicate.java
rename to legacy-test/src/com/android/internal/util/Predicate.java
diff --git a/core/java/com/android/internal/util/Predicates.java b/legacy-test/src/com/android/internal/util/Predicates.java
similarity index 99%
rename from core/java/com/android/internal/util/Predicates.java
rename to legacy-test/src/com/android/internal/util/Predicates.java
index c006564..fe1ff15 100644
--- a/core/java/com/android/internal/util/Predicates.java
+++ b/legacy-test/src/com/android/internal/util/Predicates.java
@@ -21,6 +21,8 @@
 /**
  * Predicates contains static methods for creating the standard set of
  * {@code Predicate} objects.
+ *
+ * @hide
  */
 public class Predicates {
 
diff --git a/core/tests/utiltests/src/com/android/internal/util/PredicatesTest.java b/legacy-test/tests/com/android/internal/util/PredicatesTest.java
similarity index 100%
rename from core/tests/utiltests/src/com/android/internal/util/PredicatesTest.java
rename to legacy-test/tests/com/android/internal/util/PredicatesTest.java
diff --git a/media/java/Android.bp b/media/java/Android.bp
new file mode 100644
index 0000000..0810699
--- /dev/null
+++ b/media/java/Android.bp
@@ -0,0 +1,4 @@
+filegroup {
+    name: "IMidiDeviceServer.aidl",
+    srcs: ["android/media/midi/IMidiDeviceServer.aidl"],
+}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
new file mode 100644
index 0000000..d5e2101
--- /dev/null
+++ b/media/jni/Android.bp
@@ -0,0 +1,78 @@
+cc_library_shared {
+    name: "libmedia_jni",
+
+    srcs: [
+        "android_media_ExifInterface.cpp",
+        "android_media_ImageWriter.cpp",
+        "android_media_ImageReader.cpp",
+        "android_media_MediaCrypto.cpp",
+        "android_media_MediaCodec.cpp",
+        "android_media_MediaCodecList.cpp",
+        "android_media_MediaDataSource.cpp",
+        "android_media_MediaDrm.cpp",
+        "android_media_MediaExtractor.cpp",
+        "android_media_MediaHTTPConnection.cpp",
+        "android_media_MediaMetadataRetriever.cpp",
+        "android_media_MediaMuxer.cpp",
+        "android_media_MediaPlayer.cpp",
+        "android_media_MediaProfiles.cpp",
+        "android_media_MediaRecorder.cpp",
+        "android_media_MediaScanner.cpp",
+        "android_media_MediaSync.cpp",
+        "android_media_ResampleInputStream.cpp",
+        "android_media_SyncParams.cpp",
+        "android_media_Utils.cpp",
+        "android_mtp_MtpDatabase.cpp",
+        "android_mtp_MtpDevice.cpp",
+        "android_mtp_MtpServer.cpp",
+    ],
+
+    shared_libs: [
+        "libandroid_runtime",
+        "libnativehelper",
+        "libutils",
+        "libbinder",
+        "libmedia",
+        "libmediadrm",
+        "libskia",
+        "libui",
+        "liblog",
+        "libcutils",
+        "libgui",
+        "libstagefright",
+        "libstagefright_foundation",
+        "libcamera_client",
+        "libmtp",
+        "libusbhost",
+        "libexif",
+        "libpiex",
+        "libandroidfw",
+    ],
+
+    header_libs: ["libhardware_headers"],
+
+    include_dirs: [
+        "frameworks/base/core/jni",
+        "frameworks/native/include/media/openmax",
+        "system/media/camera/include",
+    ],
+
+    export_include_dirs: ["."],
+
+    export_shared_lib_headers: [
+        "libpiex",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
+
+subdirs = [
+    "audioeffect",
+    "soundpool",
+]
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
deleted file mode 100644
index 8640565..0000000
--- a/media/jni/Android.mk
+++ /dev/null
@@ -1,74 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    android_media_ExifInterface.cpp \
-    android_media_ImageWriter.cpp \
-    android_media_ImageReader.cpp \
-    android_media_MediaCrypto.cpp \
-    android_media_MediaCodec.cpp \
-    android_media_MediaCodecList.cpp \
-    android_media_MediaDataSource.cpp \
-    android_media_MediaDrm.cpp \
-    android_media_MediaExtractor.cpp \
-    android_media_MediaHTTPConnection.cpp \
-    android_media_MediaMetadataRetriever.cpp \
-    android_media_MediaMuxer.cpp \
-    android_media_MediaPlayer.cpp \
-    android_media_MediaProfiles.cpp \
-    android_media_MediaRecorder.cpp \
-    android_media_MediaScanner.cpp \
-    android_media_MediaSync.cpp \
-    android_media_ResampleInputStream.cpp \
-    android_media_SyncParams.cpp \
-    android_media_Utils.cpp \
-    android_mtp_MtpDatabase.cpp \
-    android_mtp_MtpDevice.cpp \
-    android_mtp_MtpServer.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
-    libandroid_runtime \
-    libnativehelper \
-    libutils \
-    libbinder \
-    libmedia \
-    libmediadrm \
-    libskia \
-    libui \
-    liblog \
-    libcutils \
-    libgui \
-    libstagefright \
-    libstagefright_foundation \
-    libcamera_client \
-    libmtp \
-    libusbhost \
-    libexif \
-    libpiex \
-    libandroidfw
-
-LOCAL_STATIC_LIBRARIES := \
-
-LOCAL_C_INCLUDES += \
-    external/libexif/ \
-    external/piex/ \
-    external/tremor/Tremor \
-    frameworks/base/core/jni \
-    frameworks/base/libs/hwui \
-    frameworks/av/media/libmedia \
-    frameworks/av/media/libstagefright \
-    frameworks/av/media/mtp \
-    frameworks/native/include/media/openmax \
-    $(call include-path-for, libhardware)/hardware \
-    $(PV_INCLUDES) \
-    $(JNI_H_INCLUDE)
-
-LOCAL_CFLAGS += -Wall -Werror -Wno-error=deprecated-declarations -Wunused -Wunreachable-code
-
-LOCAL_MODULE:= libmedia_jni
-
-include $(BUILD_SHARED_LIBRARY)
-
-# build libsoundpool.so
-# build libaudioeffect_jni.so
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 724fc02..c655b7c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -26,7 +26,6 @@
 
 #include <gui/BufferItemConsumer.h>
 #include <gui/Surface.h>
-#include <camera3.h>
 
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index d5d9fc9..56df32f 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -25,7 +25,6 @@
 #include <gui/Surface.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
-#include <camera3.h>
 #include <jni.h>
 #include <JNIHelp.h>
 
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index c62d930..458d847 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -17,6 +17,7 @@
 // #define LOG_NDEBUG 0
 #define LOG_TAG "AndroidMediaUtils"
 
+#include <hardware/camera3.h>
 #include <utils/Log.h>
 #include "android_media_Utils.h"
 
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index 39c1554..af2f2d7 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -21,7 +21,6 @@
 #include "src/piex.h"
 
 #include <android_runtime/AndroidRuntime.h>
-#include <camera3.h>
 #include <gui/CpuConsumer.h>
 #include <jni.h>
 #include <JNIHelp.h>
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
new file mode 100644
index 0000000..8ac139d
--- /dev/null
+++ b/media/jni/audioeffect/Android.bp
@@ -0,0 +1,29 @@
+cc_library_shared {
+    name: "libaudioeffect_jni",
+
+    srcs: [
+        "android_media_AudioEffect.cpp",
+        "android_media_Visualizer.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libcutils",
+        "libutils",
+        "libandroid_runtime",
+        "libnativehelper",
+        "libmedia",
+        "libaudioclient",
+    ],
+
+    header_libs: [
+        "libaudioeffects",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk
deleted file mode 100644
index 8bd8857..0000000
--- a/media/jni/audioeffect/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    android_media_AudioEffect.cpp \
-    android_media_Visualizer.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    liblog \
-    libcutils \
-    libutils \
-    libandroid_runtime \
-    libnativehelper \
-    libmedia \
-    libaudioclient \
-
-LOCAL_C_INCLUDES := \
-    $(call include-path-for, audio-effects)
-
-LOCAL_MODULE:= libaudioeffect_jni
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp
new file mode 100644
index 0000000..35b7b01
--- /dev/null
+++ b/media/jni/soundpool/Android.bp
@@ -0,0 +1,28 @@
+cc_library_shared {
+    name: "libsoundpool",
+
+    srcs: [
+        "android_media_SoundPool.cpp",
+        "SoundPool.cpp",
+        "SoundPoolThread.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libcutils",
+        "libutils",
+        "libandroid_runtime",
+        "libnativehelper",
+        "libaudioclient",
+        "libmediandk",
+        "libbinder",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
deleted file mode 100644
index 509a59b..0000000
--- a/media/jni/soundpool/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    android_media_SoundPool.cpp \
-    SoundPool.cpp \
-    SoundPoolThread.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    liblog \
-    libcutils \
-    libutils \
-    libandroid_runtime \
-    libnativehelper \
-    libaudioclient \
-    libmediandk \
-    libbinder
-
-LOCAL_MODULE:= libsoundpool
-
-LOCAL_CFLAGS += -Wall -Werror -Wno-error=deprecated-declarations -Wunused -Wunreachable-code
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 6394c64..34465e9 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -122,6 +122,11 @@
         WebSettings webSettings = myWebView.getSettings();
         webSettings.setJavaScriptEnabled(true);
         webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
+        webSettings.setUseWideViewPort(true);
+        webSettings.setLoadWithOverviewMode(true);
+        webSettings.setSupportZoom(true);
+        webSettings.setBuiltInZoomControls(true);
+        webSettings.setDisplayZoomControls(false);
         mWebViewClient = new MyWebViewClient();
         myWebView.setWebViewClient(mWebViewClient);
         myWebView.setWebChromeClient(new MyWebChromeClient());
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 689c979..f9b0d2f 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -18,10 +18,18 @@
 LOCAL_AIDL_INCLUDES += \
     system/netd/server/binder
 
-LOCAL_JAVA_LIBRARIES := services.net
-LOCAL_STATIC_JAVA_LIBRARIES := tzdata_shared2 tzdata_update2
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
 
+LOCAL_JAVA_LIBRARIES := \
+    services.net \
+    android.hidl.manager-V1.0-java \
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    tzdata_shared2 \
+    tzdata_update2 \
+    android.hidl.base-V1.0-java-static \
+    android.hardware.tetheroffload.control-V1.0-java-static \
+
 ifneq ($(INCREMENTAL_BUILDS),)
     LOCAL_PROGUARD_ENABLED := disabled
     LOCAL_JACK_ENABLED := incremental
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 5a3c7d0..ba1befd 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1699,7 +1699,7 @@
                     mHandler.removeMessages(MESSAGE_USER_SWITCHED);
 
                     /* disable and enable BT when detect a user switch */
-                    if (mEnable && mBluetooth != null) {
+                    if (mBluetooth != null && isEnabled()) {
                         try {
                             mBluetoothLock.readLock().lock();
                             if (mBluetooth != null) {
@@ -1768,6 +1768,8 @@
                         mState = BluetoothAdapter.STATE_OFF;
                         // enable
                         addActiveLog(REASON_USER_SWITCH, true);
+                        // mEnable flag could have been reset on disableBLE. Reenable it.
+                        mEnable = true;
                         handleEnable(mQuietEnable);
                     } else if (mBinding || mBluetooth != null) {
                         Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4f7b834..19badbd 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -145,6 +145,7 @@
 import com.android.server.connectivity.PacManager;
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.Tethering;
+import com.android.server.connectivity.tethering.TetheringDependencies;
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.net.LockdownVpnTracker;
@@ -182,6 +183,9 @@
         implements PendingIntent.OnFinished {
     private static final String TAG = ConnectivityService.class.getSimpleName();
 
+    public static final String DIAG_ARG = "--diag";
+    public static final String SHORT_ARG = "--short";
+
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
@@ -487,7 +491,7 @@
             new ArrayDeque<ValidationLog>(MAX_VALIDATION_LOGS);
 
     private void addValidationLogs(ReadOnlyLocalLog log, Network network, String networkExtraInfo) {
-        synchronized(mValidationLogs) {
+        synchronized (mValidationLogs) {
             while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) {
                 mValidationLogs.removeLast();
             }
@@ -802,8 +806,7 @@
         mTestMode = mSystemProperties.get("cm.test.mode").equals("true")
                 && mSystemProperties.get("ro.build.type").equals("eng");
 
-        mTethering = new Tethering(mContext, mNetd, statsService, mPolicyManager,
-                                   IoThread.get().getLooper(), new MockableSystemProperties());
+        mTethering = makeTethering();
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
 
@@ -853,6 +856,14 @@
         mMultinetworkPolicyTracker.start();
     }
 
+    private Tethering makeTethering() {
+        // TODO: Move other elements into @Overridden getters.
+        final TetheringDependencies deps = new TetheringDependencies();
+        return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
+                IoThread.get().getLooper(), new MockableSystemProperties(),
+                deps);
+    }
+
     private NetworkRequest createInternetRequestForTransport(
             int transportType, NetworkRequest.Type type) {
         NetworkCapabilities netCap = new NetworkCapabilities();
@@ -1671,7 +1682,7 @@
     }
 
     private void sendStickyBroadcast(Intent intent) {
-        synchronized(this) {
+        synchronized (this) {
             if (!mSystemReady) {
                 mInitialBroadcast = new Intent(intent);
             }
@@ -1712,7 +1723,7 @@
     void systemReady() {
         loadGlobalProxy();
 
-        synchronized(this) {
+        synchronized (this) {
             mSystemReady = true;
             if (mInitialBroadcast != null) {
                 mContext.sendStickyBroadcastAsUser(mInitialBroadcast, UserHandle.ALL);
@@ -1953,7 +1964,7 @@
             return;
         }
 
-        if (argsContain(args, "--diag")) {
+        if (argsContain(args, DIAG_ARG)) {
             dumpNetworkDiagnostics(pw);
             return;
         }
@@ -2045,7 +2056,7 @@
         pw.println();
         dumpAvoidBadWifiSettings(pw);
 
-        if (argsContain(args, "--short") == false) {
+        if (argsContain(args, SHORT_ARG) == false) {
             pw.println();
             synchronized (mValidationLogs) {
                 pw.println("mValidationLogs (most recent first):");
@@ -3494,7 +3505,7 @@
         enforceCrossUserPermission(userId);
         throwIfLockdownEnabled();
 
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             Vpn vpn = mVpns.get(userId);
             if (vpn != null) {
                 return vpn.prepare(oldPackage, newPackage);
@@ -3521,7 +3532,7 @@
     public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) {
         enforceCrossUserPermission(userId);
 
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             Vpn vpn = mVpns.get(userId);
             if (vpn != null) {
                 vpn.setPackageAuthorization(packageName, authorized);
@@ -3540,7 +3551,7 @@
     public ParcelFileDescriptor establishVpn(VpnConfig config) {
         throwIfLockdownEnabled();
         int user = UserHandle.getUserId(Binder.getCallingUid());
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             return mVpns.get(user).establish(config);
         }
     }
@@ -3557,7 +3568,7 @@
             throw new IllegalStateException("Missing active network connection");
         }
         int user = UserHandle.getUserId(Binder.getCallingUid());
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
         }
     }
@@ -3571,7 +3582,7 @@
     public LegacyVpnInfo getLegacyVpnInfo(int userId) {
         enforceCrossUserPermission(userId);
 
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             return mVpns.get(userId).getLegacyVpnInfo();
         }
     }
@@ -3587,7 +3598,7 @@
             return new VpnInfo[0];
         }
 
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             List<VpnInfo> infoList = new ArrayList<>();
             for (int i = 0; i < mVpns.size(); i++) {
                 VpnInfo info = createVpnInfo(mVpns.valueAt(i));
@@ -3635,7 +3646,7 @@
     @Override
     public VpnConfig getVpnConfig(int userId) {
         enforceCrossUserPermission(userId);
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             Vpn vpn = mVpns.get(userId);
             if (vpn != null) {
                 return vpn.getVpnConfig();
@@ -3669,7 +3680,7 @@
                 return true;
             }
             int user = UserHandle.getUserId(Binder.getCallingUid());
-            synchronized(mVpns) {
+            synchronized (mVpns) {
                 Vpn vpn = mVpns.get(user);
                 if (vpn == null) {
                     Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
@@ -3904,7 +3915,7 @@
     }
 
     private void onUserStart(int userId) {
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             Vpn userVpn = mVpns.get(userId);
             if (userVpn != null) {
                 loge("Starting user already has a VPN");
@@ -3919,7 +3930,7 @@
     }
 
     private void onUserStop(int userId) {
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             Vpn userVpn = mVpns.get(userId);
             if (userVpn == null) {
                 loge("Stopped user has no VPN");
@@ -3931,7 +3942,7 @@
     }
 
     private void onUserAdded(int userId) {
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
@@ -3941,7 +3952,7 @@
     }
 
     private void onUserRemoved(int userId) {
-        synchronized(mVpns) {
+        synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index b64c65d..9f7437e 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -26,6 +26,8 @@
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -73,6 +75,7 @@
     private long mNitzTimeSetTime = NOT_SET;
     // TODO: Have a way to look up the timezone we are in
     private long mNitzZoneSetTime = NOT_SET;
+    private Network mDefaultNetwork = null;
 
     private Context mContext;
     private TrustedTime mTime;
@@ -82,6 +85,8 @@
     private AlarmManager mAlarmManager;
     private PendingIntent mPendingPollIntent;
     private SettingsObserver mSettingsObserver;
+    private ConnectivityManager mCM;
+    private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
     // The last time that we successfully fetched the NTP time.
     private long mLastNtpFetchTime = NOT_SET;
     private final PowerManager.WakeLock mWakeLock;
@@ -103,6 +108,7 @@
         mContext = context;
         mTime = NtpTrustedTime.getInstance(context);
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         Intent pollIntent = new Intent(ACTION_POLL, null);
         mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
 
@@ -123,13 +129,12 @@
     public void systemRunning() {
         registerForTelephonyIntents();
         registerForAlarms();
-        registerForConnectivityIntents();
 
         HandlerThread thread = new HandlerThread(TAG);
         thread.start();
         mHandler = new MyHandler(thread.getLooper());
-        // Check the network time on the new thread
-        mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+        mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
+        mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
 
         mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
         mSettingsObserver.observe(mContext);
@@ -152,15 +157,10 @@
             }, new IntentFilter(ACTION_POLL));
     }
 
-    private void registerForConnectivityIntents() {
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        mContext.registerReceiver(mConnectivityReceiver, intentFilter);
-    }
-
     private void onPollNetworkTime(int event) {
-        // If Automatic time is not set, don't bother.
-        if (!isAutomaticTimeRequested()) return;
+        // If Automatic time is not set, don't bother. Similarly, if we don't
+        // have any default network, don't bother.
+        if (!isAutomaticTimeRequested() || mDefaultNetwork == null) return;
         mWakeLock.acquire();
         try {
             onPollNetworkTimeUnderWakeLock(event);
@@ -262,22 +262,6 @@
         }
     };
 
-    /** Receiver for ConnectivityManager events */
-    private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
-                if (DBG) Log.d(TAG, "Received CONNECTIVITY_ACTION ");
-                // Don't bother checking if we have connectivity, NtpTrustedTime does that for us.
-                Message message = mHandler.obtainMessage(EVENT_NETWORK_CHANGED);
-                // Send with a short delay to make sure the network is ready for use
-                mHandler.sendMessageDelayed(message, NETWORK_CHANGE_EVENT_DELAY_MS);
-            }
-        }
-    };
-
     /** Handler to do the network accesses on */
     private class MyHandler extends Handler {
 
@@ -297,6 +281,21 @@
         }
     }
 
+    private class NetworkTimeUpdateCallback extends NetworkCallback {
+        @Override
+        public void onAvailable(Network network) {
+            Log.d(TAG, String.format("New default network %s; checking time.", network));
+            mDefaultNetwork = network;
+            // Running on mHandler so invoke directly.
+            onPollNetworkTime(EVENT_NETWORK_CHANGED);
+        }
+
+        @Override
+        public void onLost(Network network) {
+            if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
+        }
+    }
+
     /** Observer to watch for changes to the AUTO_TIME setting */
     private static class SettingsObserver extends ContentObserver {
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f45beb6..1cada64 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -154,10 +154,6 @@
 
     private int[] mDataConnectionState;
 
-    private boolean[] mDataConnectionPossible;
-
-    private String[] mDataConnectionReason;
-
     private String[] mDataConnectionApn;
 
     private ArrayList<String>[] mConnectedApns;
@@ -307,8 +303,6 @@
         mDataActivationState = new int[numPhones];
         mSignalStrength = new SignalStrength[numPhones];
         mMessageWaiting = new boolean[numPhones];
-        mDataConnectionPossible = new boolean[numPhones];
-        mDataConnectionReason = new String[numPhones];
         mDataConnectionApn = new String[numPhones];
         mCallForwarding = new boolean[numPhones];
         mCellLocation = new Bundle[numPhones];
@@ -326,8 +320,6 @@
             mSignalStrength[i] =  new SignalStrength();
             mMessageWaiting[i] =  false;
             mCallForwarding[i] =  false;
-            mDataConnectionPossible[i] = false;
-            mDataConnectionReason[i] =  "";
             mDataConnectionApn[i] =  "";
             mCellLocation[i] = new Bundle();
             mCellInfo.add(i, null);
@@ -1081,16 +1073,16 @@
         }
     }
 
-    public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
+    public void notifyDataConnection(int state, boolean isDataAllowed,
             String reason, String apn, String apnType, LinkProperties linkProperties,
             NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
         notifyDataConnectionForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, state,
-            isDataConnectivityPossible,reason, apn, apnType, linkProperties,
+            isDataAllowed,reason, apn, apnType, linkProperties,
             networkCapabilities, networkType, roaming);
     }
 
     public void notifyDataConnectionForSubscriber(int subId, int state,
-            boolean isDataConnectivityPossible, String reason, String apn, String apnType,
+            boolean isDataAllowed, String reason, String apn, String apnType,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int networkType, boolean roaming) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
@@ -1098,7 +1090,7 @@
         }
         if (VDBG) {
             log("notifyDataConnectionForSubscriber: subId=" + subId
-                + " state=" + state + " isDataConnectivityPossible=" + isDataConnectivityPossible
+                + " state=" + state + " isDataAllowed=" + isDataAllowed
                 + " reason='" + reason
                 + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
                 + " mRecords.size()=" + mRecords.size());
@@ -1126,8 +1118,6 @@
                         }
                     }
                 }
-                mDataConnectionPossible[phoneId] = isDataConnectivityPossible;
-                mDataConnectionReason[phoneId] = reason;
                 mDataConnectionLinkProperties[phoneId] = linkProperties;
                 mDataConnectionNetworkCapabilities[phoneId] = networkCapabilities;
                 if (mDataConnectionNetworkType[phoneId] != networkType) {
@@ -1171,7 +1161,7 @@
             }
             handleRemoveListLocked();
         }
-        broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
+        broadcastDataConnectionStateChanged(state, isDataAllowed, reason, apn,
                 apnType, linkProperties, networkCapabilities, roaming, subId);
         broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason,
                 linkProperties, "");
@@ -1412,8 +1402,6 @@
                 pw.println("  mCallForwarding=" + mCallForwarding[i]);
                 pw.println("  mDataActivity=" + mDataActivity[i]);
                 pw.println("  mDataConnectionState=" + mDataConnectionState[i]);
-                pw.println("  mDataConnectionPossible=" + mDataConnectionPossible[i]);
-                pw.println("  mDataConnectionReason=" + mDataConnectionReason[i]);
                 pw.println("  mDataConnectionApn=" + mDataConnectionApn[i]);
                 pw.println("  mDataConnectionLinkProperties=" + mDataConnectionLinkProperties[i]);
                 pw.println("  mDataConnectionNetworkCapabilities=" +
@@ -1521,7 +1509,7 @@
     }
 
     private void broadcastDataConnectionStateChanged(int state,
-            boolean isDataConnectivityPossible,
+            boolean isDataAllowed,
             String reason, String apn, String apnType, LinkProperties linkProperties,
             NetworkCapabilities networkCapabilities, boolean roaming, int subId) {
         // Note: not reporting to the battery stats service here, because the
@@ -1530,7 +1518,7 @@
         Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
         intent.putExtra(PhoneConstants.STATE_KEY,
                 PhoneConstantConversions.convertDataState(state).toString());
-        if (!isDataConnectivityPossible) {
+        if (!isDataAllowed) {
             intent.putExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY, true);
         }
         if (reason != null) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 4dcf425..b41bcec 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -967,14 +967,18 @@
                 return result;
             }
         }
-        // Otherwise wait until https probe completes and use its result.
+        // Otherwise wait until http and https probes completes and use their results.
         try {
+            httpProbe.join();
+            if (httpProbe.result().isPortal()) {
+                return httpProbe.result();
+            }
             httpsProbe.join();
+            return httpsProbe.result();
         } catch (InterruptedException e) {
-            validationLog("Error: https probe wait interrupted!");
+            validationLog("Error: http or https probe wait interrupted!");
             return CaptivePortalProbeResult.FAILED;
         }
-        return httpsProbe.result();
     }
 
     private URL makeURL(String url) {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 4dae364..3c46226 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -20,6 +20,7 @@
 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static com.android.server.ConnectivityService.SHORT_ARG;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -47,6 +48,7 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.util.SharedLog;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
 import android.os.Bundle;
@@ -62,7 +64,6 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.LocalLog;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -81,6 +82,7 @@
 import com.android.server.connectivity.tethering.SimChangeListener;
 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.UpstreamNetworkMonitor;
 import com.android.server.net.BaseNetworkObserver;
 
@@ -145,9 +147,7 @@
         }
     }
 
-    private final static int MAX_LOG_RECORDS = 500;
-
-    private final LocalLog mLocalLog = new LocalLog(MAX_LOG_RECORDS);
+    private final SharedLog mLog = new SharedLog(TAG);
 
     // used to synchronize public access to members
     private final Object mPublicSync;
@@ -178,8 +178,9 @@
 
     public Tethering(Context context, INetworkManagementService nmService,
             INetworkStatsService statsService, INetworkPolicyManager policyManager,
-            Looper looper, MockableSystemProperties systemProperties) {
-        mLocalLog.log("CONSTRUCTED");
+            Looper looper, MockableSystemProperties systemProperties,
+            TetheringDependencies deps) {
+        mLog.mark("constructed");
         mContext = context;
         mNMService = nmService;
         mStatsService = statsService;
@@ -194,9 +195,10 @@
         mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
         mTetherMasterSM.start();
 
-        mOffloadController = new OffloadController(mTetherMasterSM.getHandler());
+        mOffloadController = new OffloadController(mTetherMasterSM.getHandler(),
+                deps.getOffloadHardwareInterface(), mLog);
         mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
-                mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
+                mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK, mLog);
         mForwardedDownstreams = new HashSet<>();
         mSimChange = new SimChangeListener(
                 mContext, mTetherMasterSM.getHandler(), () -> reevaluateSimCardProvisioning());
@@ -1128,7 +1130,7 @@
             addState(mSetDnsForwardersErrorState);
 
             mNotifyList = new ArrayList<>();
-            mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList);
+            mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
             setInitialState(mInitialState);
         }
 
@@ -1175,7 +1177,7 @@
                 try {
                     mNMService.setIpForwardingEnabled(true);
                 } catch (Exception e) {
-                    mLocalLog.log("ERROR " + e);
+                    mLog.e(e);
                     transitionTo(mSetIpForwardingEnabledErrorState);
                     return false;
                 }
@@ -1188,12 +1190,12 @@
                         mNMService.stopTethering();
                         mNMService.startTethering(cfg.dhcpRanges);
                     } catch (Exception ee) {
-                        mLocalLog.log("ERROR " + ee);
+                        mLog.e(ee);
                         transitionTo(mStartTetheringErrorState);
                         return false;
                     }
                 }
-                mLocalLog.log("SET master tether settings: ON");
+                mLog.log("SET master tether settings: ON");
                 return true;
             }
 
@@ -1201,19 +1203,19 @@
                 try {
                     mNMService.stopTethering();
                 } catch (Exception e) {
-                    mLocalLog.log("ERROR " + e);
+                    mLog.e(e);
                     transitionTo(mStopTetheringErrorState);
                     return false;
                 }
                 try {
                     mNMService.setIpForwardingEnabled(false);
                 } catch (Exception e) {
-                    mLocalLog.log("ERROR " + e);
+                    mLog.e(e);
                     transitionTo(mSetIpForwardingDisabledErrorState);
                     return false;
                 }
                 transitionTo(mInitialState);
-                mLocalLog.log("SET master tether settings: OFF");
+                mLog.log("SET master tether settings: OFF");
                 return true;
             }
 
@@ -1339,13 +1341,13 @@
                 }
                 try {
                     mNMService.setDnsForwarders(network, dnsServers);
-                    mLocalLog.log(String.format(
-                            "SET DNS forwarders: network=%s dnsServers=[%s]",
+                    mLog.log(String.format(
+                            "SET DNS forwarders: network=%s dnsServers=%s",
                             network, Arrays.toString(dnsServers)));
                 } catch (Exception e) {
                     // TODO: Investigate how this can fail and what exactly
                     // happens if/when such failures occur.
-                    mLocalLog.log("ERROR setting DNS forwarders failed, " + e);
+                    mLog.e("setting DNS forwarders failed, " + e);
                     transitionTo(mSetDnsForwardersErrorState);
                 }
             }
@@ -1474,7 +1476,8 @@
                         handleInterfaceServingStateInactive(who);
 
                         if (mNotifyList.isEmpty()) {
-                            // transitions appropriately
+                            // This transitions us out of TetherModeAliveState,
+                            // either to InitialState or an error state.
                             turnOffMasterTetherSettings();
                             break;
                         }
@@ -1706,12 +1709,23 @@
 
         pw.println("Log:");
         pw.increaseIndent();
-        mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
+        if (argsContain(args, SHORT_ARG)) {
+            pw.println("<log removed for brevity>");
+        } else {
+            mLog.dump(fd, pw, args);
+        }
         pw.decreaseIndent();
 
         pw.decreaseIndent();
     }
 
+    private static boolean argsContain(String[] args, String target) {
+        for (String arg : args) {
+            if (arg.equals(target)) return true;
+        }
+        return false;
+    }
+
     @Override
     public void notifyInterfaceStateChange(String iface, TetherInterfaceStateMachine who,
                                            int state, int error) {
@@ -1725,8 +1739,7 @@
             }
         }
 
-        mLocalLog.log(String.format("OBSERVED iface=%s state=%s error=%s",
-                iface, state, error));
+        mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error));
 
         try {
             // Notify that we're tethering (or not) this interface.
@@ -1764,8 +1777,8 @@
     private void trackNewTetherableInterface(String iface, int interfaceType) {
         TetherState tetherState;
         tetherState = new TetherState(new TetherInterfaceStateMachine(iface, mLooper,
-                interfaceType, mNMService, mStatsService, this,
-                new IPv6TetheringInterfaceServices(iface, mNMService)));
+                interfaceType, mLog, mNMService, mStatsService, this,
+                new IPv6TetheringInterfaceServices(iface, mNMService, mLog)));
         mTetherStates.put(iface, tetherState);
         tetherState.stateMachine.start();
     }
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 2485654..518f6c1 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -25,6 +25,7 @@
 import android.net.NetworkState;
 import android.net.RouteInfo;
 import android.net.util.NetworkConstants;
+import android.net.util.SharedLog;
 import android.util.Log;
 
 import java.net.Inet6Address;
@@ -64,6 +65,7 @@
     }
 
     private final ArrayList<TetherInterfaceStateMachine> 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
     // be parceled out on a "first come, first served" basis and a /64 used by
@@ -74,8 +76,10 @@
     private short mNextSubnetId;
     private NetworkState mUpstreamNetworkState;
 
-    public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) {
+    public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList,
+                                    SharedLog log) {
         mNotifyList = notifyList;
+        mLog = log.forSubComponent(TAG);
         mActiveDownstreams = new LinkedList<>();
         mUniqueLocalPrefix = generateUniqueLocalPrefix();
         mNextSubnetId = 0;
@@ -115,7 +119,7 @@
         if (VDBG) {
             Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
         }
-        if (!canTetherIPv6(ns)) {
+        if (!canTetherIPv6(ns, mLog)) {
             stopIPv6TetheringOnAllInterfaces();
             setUpstreamNetworkState(null);
             return;
@@ -150,9 +154,7 @@
                     null);
         }
 
-        if (DBG) {
-            Log.d(TAG, "setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
-        }
+        mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
     }
 
     private void updateIPv6TetheringInterfaces() {
@@ -206,7 +208,7 @@
         return null;
     }
 
-    private static boolean canTetherIPv6(NetworkState ns) {
+    private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) {
         // Broadly speaking:
         //
         //     [1] does the upstream have an IPv6 default route?
@@ -260,13 +262,11 @@
 
         final boolean outcome = canTether && supportedConfiguration;
 
-        if (VDBG) {
-            if (ns == null) {
-                Log.d(TAG, "No available upstream.");
-            } else {
-                Log.d(TAG, String.format("IPv6 tethering is %s for upstream: %s",
-                        (outcome ? "available" : "not available"), toDebugString(ns)));
-            }
+        if (ns == null) {
+            sharedLog.log("No available upstream.");
+        } else {
+            sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s",
+                    (outcome ? "available" : "not available"), toDebugString(ns)));
         }
 
         return outcome;
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
index c6a7925..adf4af8 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -28,10 +28,10 @@
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
 import android.net.util.NetdService;
+import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.ServiceSpecificException;
 import android.os.RemoteException;
-import android.util.Log;
 import android.util.Slog;
 
 import java.net.Inet6Address;
@@ -54,6 +54,7 @@
 
     private final String mIfName;
     private final INetworkManagementService mNMService;
+    private final SharedLog mLog;
 
     private NetworkInterface mNetworkInterface;
     private byte[] mHwAddr;
@@ -61,9 +62,11 @@
     private RouterAdvertisementDaemon mRaDaemon;
     private RaParams mLastRaParams;
 
-    public IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) {
+    public IPv6TetheringInterfaceServices(
+            String ifname, INetworkManagementService nms, SharedLog log) {
         mIfName = ifname;
         mNMService = nms;
+        mLog = log.forSubComponent(mIfName);
     }
 
     public boolean start() {
@@ -72,12 +75,12 @@
         try {
             mNetworkInterface = NetworkInterface.getByName(mIfName);
         } catch (SocketException e) {
-            Log.e(TAG, "Error looking up NetworkInterfaces for " + mIfName, e);
+            mLog.e("Error looking up NetworkInterfaces: " + e);
             stop();
             return false;
         }
         if (mNetworkInterface == null) {
-            Log.e(TAG, "Failed to find NetworkInterface for " + mIfName);
+            mLog.e("Failed to find NetworkInterface");
             stop();
             return false;
         }
@@ -85,7 +88,7 @@
         try {
             mHwAddr = mNetworkInterface.getHardwareAddress();
         } catch (SocketException e) {
-            Log.e(TAG, "Failed to find hardware address for " + mIfName, e);
+            mLog.e("Failed to find hardware address: " + e);
             stop();
             return false;
         }
@@ -161,11 +164,11 @@
             try {
                 final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
                 if (removalFailures > 0) {
-                    Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.",
+                    mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
                             removalFailures));
                 }
             } catch (RemoteException e) {
-                Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
+                mLog.e("Failed to remove IPv6 routes from local table: " + e);
             }
         }
 
@@ -195,7 +198,7 @@
                     // error (EEXIST is silently ignored).
                     mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded);
                 } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
+                    mLog.e("Failed to add IPv6 routes to local table: " + e);
                 }
             }
         }
@@ -206,7 +209,7 @@
         final INetd netd = NetdService.getInstance();
         if (netd == null) {
             if (newDnses != null) newDnses.clear();
-            Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses");
+            mLog.e("No netd service instance available; not setting local IPv6 addresses");
             return;
         }
 
@@ -217,7 +220,7 @@
                 try {
                     netd.interfaceDelAddress(mIfName, dnsString, RFC7421_PREFIX_LENGTH);
                 } catch (ServiceSpecificException | RemoteException e) {
-                    Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
+                    mLog.e("Failed to remove local dns IP " + dnsString + ": " + e);
                 }
             }
         }
@@ -234,7 +237,7 @@
                 try {
                     netd.interfaceAddAddress(mIfName, dnsString, RFC7421_PREFIX_LENGTH);
                 } catch (ServiceSpecificException | RemoteException e) {
-                    Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
+                    mLog.e("Failed to add local dns IP " + dnsString + ": " + e);
                     newDnses.remove(dns);
                 }
             }
@@ -243,7 +246,7 @@
         try {
             netd.tetherApplyDnsInterfaces();
         } catch (ServiceSpecificException | RemoteException e) {
-            Log.e(TAG, "Failed to update local DNS caching server");
+            mLog.e("Failed to update local DNS caching server");
             if (newDnses != null) newDnses.clear();
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 220e751..ec7ab5b 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -18,10 +18,11 @@
 
 import android.net.LinkProperties;
 import android.os.Handler;
-import android.util.Log;
+import android.net.util.SharedLog;
 
 /**
- * A wrapper around hardware offload interface.
+ * A class to encapsulate the business logic of programming the tethering
+ * hardware offload interface.
  *
  * @hide
  */
@@ -29,25 +30,50 @@
     private static final String TAG = OffloadController.class.getSimpleName();
 
     private final Handler mHandler;
+    private final OffloadHardwareInterface mHwInterface;
+    private final SharedLog mLog;
+    private boolean mConfigInitialized;
+    private boolean mControlInitialized;
     private LinkProperties mUpstreamLinkProperties;
 
-    public OffloadController(Handler h) {
+    public OffloadController(Handler h, OffloadHardwareInterface hwi, SharedLog log) {
         mHandler = h;
+        mHwInterface = hwi;
+        mLog = log.forSubComponent(TAG);
     }
 
     public void start() {
-        // TODO: initOffload() and configure callbacks to be handled on our
-        // preferred Handler.
-        Log.d(TAG, "tethering offload not supported");
+        if (started()) return;
+
+        if (!mConfigInitialized) {
+            mConfigInitialized = mHwInterface.initOffloadConfig();
+            if (!mConfigInitialized) {
+                mLog.i("tethering offload config not supported");
+                return;
+            }
+        }
+
+        // TODO: Create and register ITetheringOffloadCallback.
+        mControlInitialized = mHwInterface.initOffloadControl();
     }
 
     public void stop() {
-        // TODO: stopOffload().
         mUpstreamLinkProperties = null;
+        mHwInterface.stopOffloadControl();
+        mControlInitialized = false;
+        mConfigInitialized = false;
     }
 
     public void setUpstreamLinkProperties(LinkProperties lp) {
+        if (!started()) return;
+
         // TODO: setUpstreamParameters().
         mUpstreamLinkProperties = lp;
     }
+
+    // TODO: public void addDownStream(...)
+
+    private boolean started() {
+        return mConfigInitialized && mControlInitialized;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
new file mode 100644
index 0000000..87fc491
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
+import android.hardware.tetheroffload.control.V1_0.IOffloadControl.stopOffloadCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+
+/**
+ * Capture tethering dependencies, for injection.
+ *
+ * @hide
+ */
+public class OffloadHardwareInterface {
+    private static final String TAG = OffloadHardwareInterface.class.getSimpleName();
+
+    private static native boolean configOffload();
+
+    private IOffloadControl mOffloadControl;
+
+    public OffloadHardwareInterface() {}
+
+    public boolean initOffloadConfig() {
+        return configOffload();
+    }
+
+    // TODO: Extend this to take a TetheringControlCallback for registration.
+    public boolean initOffloadControl() {
+        if (mOffloadControl == null) {
+            try {
+                mOffloadControl = IOffloadControl.getService();
+            } catch (RemoteException e) {
+                Log.d(TAG, "tethering offload control not supported: " + e);
+                return false;
+            }
+        }
+
+        // TODO: call mOffloadControl.initOffload(...callback...);
+
+        return true;
+    }
+
+    public void stopOffloadControl() {
+        if (mOffloadControl == null) return;
+
+        try {
+            final stopOffloadCallback cb = new stopOffloadCallback() {
+                @Override
+                public void onValues(boolean success, String errMsg) {
+                    if (success) return;
+
+                    Log.e(TAG, "stopOffload failed: " + errMsg);
+                }
+            };
+            mOffloadControl.stopOffload(cb);
+        } catch (RemoteException e) {
+            Log.d(TAG, "failed to stopOffload: " + e);
+        }
+        mOffloadControl = null;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index d3cfd87..4a1d405 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -22,6 +22,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
+import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
@@ -82,6 +83,7 @@
     private final State mTetheredState;
     private final State mUnavailableState;
 
+    private final SharedLog mLog;
     private final INetworkManagementService mNMService;
     private final INetworkStatsService mStatsService;
     private final IControlsTethering mTetherController;
@@ -93,10 +95,12 @@
     private int mLastError;
     private String mMyUpstreamIfaceName;  // may change over time
 
-    public TetherInterfaceStateMachine(String ifaceName, Looper looper, int interfaceType,
-                    INetworkManagementService nMService, INetworkStatsService statsService,
-                    IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc) {
+    public TetherInterfaceStateMachine(
+            String ifaceName, Looper looper, int interfaceType, SharedLog log,
+            INetworkManagementService nMService, INetworkStatsService statsService,
+            IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc) {
         super(ifaceName, looper);
+        mLog = log.forSubComponent(ifaceName);
         mNMService = nMService;
         mStatsService = statsService;
         mTetherController = tetherController;
@@ -162,7 +166,7 @@
                 mNMService.setInterfaceConfig(mIfaceName, ifcg);
             }
         } catch (Exception e) {
-            Log.e(TAG, "Error configuring interface " + mIfaceName, e);
+            mLog.e("Error configuring interface " + e);
             return false;
         }
 
@@ -203,7 +207,7 @@
                             transitionTo(mTetheredState);
                             break;
                         default:
-                            Log.e(TAG, "Invalid tethering interface serving state specified.");
+                            mLog.e("Invalid tethering interface serving state specified.");
                     }
                     break;
                 case CMD_INTERFACE_DOWN:
@@ -232,13 +236,13 @@
             try {
                 mNMService.tetherInterface(mIfaceName);
             } catch (Exception e) {
-                Log.e(TAG, "Error Tethering: " + e.toString());
+                mLog.e("Error Tethering: " + e);
                 mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
                 return;
             }
 
             if (!mIPv6TetherSvc.start()) {
-                Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices");
+                mLog.e("Failed to start IPv6TetheringInterfaceServices");
                 // TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
                 return;
             }
@@ -255,7 +259,7 @@
                 mNMService.untetherInterface(mIfaceName);
             } catch (Exception e) {
                 mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
-                Log.e(TAG, "Failed to untether interface: " + e.toString());
+                mLog.e("Failed to untether interface: " + e);
             }
 
             configureIfaceIp(false);
@@ -316,7 +320,7 @@
             maybeLogMessage(this, message.what);
             switch (message.what) {
                 case CMD_TETHER_REQUESTED:
-                    Log.e(TAG, "CMD_TETHER_REQUESTED while in local hotspot mode.");
+                    mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode.");
                     break;
                 case CMD_TETHER_CONNECTION_CHANGED:
                     // Ignored in local hotspot state.
@@ -389,7 +393,7 @@
             boolean retValue = true;
             switch (message.what) {
                 case CMD_TETHER_REQUESTED:
-                    Log.e(TAG, "CMD_TETHER_REQUESTED while already tethering.");
+                    mLog.e("CMD_TETHER_REQUESTED while already tethering.");
                     break;
                 case CMD_TETHER_CONNECTION_CHANGED:
                     String newUpstreamIfaceName = (String)(message.obj);
@@ -406,7 +410,7 @@
                             mNMService.startInterfaceForwarding(mIfaceName,
                                     newUpstreamIfaceName);
                         } catch (Exception e) {
-                            Log.e(TAG, "Exception enabling Nat: " + e.toString());
+                            mLog.e("Exception enabling NAT: " + e);
                             cleanupUpstreamInterface(newUpstreamIfaceName);
                             mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
                             transitionTo(mInitialState);
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 d38beb3..44c61f0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.content.Context.TELEPHONY_SERVICE;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
@@ -47,9 +48,9 @@
 public class TetheringConfiguration {
     private static final String TAG = TetheringConfiguration.class.getSimpleName();
 
-    private static final int DUN_NOT_REQUIRED = 0;
-    private static final int DUN_REQUIRED = 1;
-    private static final int DUN_UNSPECIFIED = 2;
+    public static final int DUN_NOT_REQUIRED = 0;
+    public static final int DUN_REQUIRED = 1;
+    public static final int DUN_UNSPECIFIED = 2;
 
     // USB is  192.168.42.1 and 255.255.255.0
     // Wifi is 192.168.43.1 and 255.255.255.0
@@ -81,8 +82,9 @@
         tetherableBluetoothRegexs = ctx.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_bluetooth_regexs);
 
-        isDunRequired = checkDunRequired(ctx);
-        preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, isDunRequired);
+        final int dunCheck = checkDunRequired(ctx);
+        preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
+        isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
 
         dhcpRanges = getDhcpRanges(ctx);
         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
@@ -138,14 +140,12 @@
         pw.println();
     }
 
-    private static boolean checkDunRequired(Context ctx) {
-        final TelephonyManager tm = ctx.getSystemService(TelephonyManager.class);
-        final int secureSetting =
-                (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED;
-        return (secureSetting == DUN_REQUIRED);
+    private static int checkDunRequired(Context ctx) {
+        final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
+        return (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED;
     }
 
-    private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, boolean requiresDun) {
+    private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
         final int ifaceTypes[] = ctx.getResources().getIntArray(
                 com.android.internal.R.array.config_tether_upstream_types);
         final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
@@ -153,10 +153,10 @@
             switch (i) {
                 case TYPE_MOBILE:
                 case TYPE_MOBILE_HIPRI:
-                    if (requiresDun) continue;
+                    if (dunCheck == DUN_REQUIRED) continue;
                     break;
                 case TYPE_MOBILE_DUN:
-                    if (!requiresDun) continue;
+                    if (dunCheck == DUN_NOT_REQUIRED) continue;
                     break;
             }
             upstreamIfaceTypes.add(i);
@@ -166,7 +166,7 @@
         // of the value of |requiresDun|, cell data of one form or another is
         // *always* an upstream, regardless of the upstream interface types
         // specified by configuration resources.
-        if (requiresDun) {
+        if (dunCheck == DUN_REQUIRED) {
             if (!upstreamIfaceTypes.contains(TYPE_MOBILE_DUN)) {
                 upstreamIfaceTypes.add(TYPE_MOBILE_DUN);
             }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
new file mode 100644
index 0000000..be2cf08
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+
+/**
+ * Capture tethering dependencies, for injection.
+ *
+ * @hide
+ */
+public class TetheringDependencies {
+    public OffloadHardwareInterface getOffloadHardwareInterface() {
+        return new OffloadHardwareInterface();
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 97a2d5e..be71490 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -29,6 +29,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
+import android.net.util.SharedLog;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -73,6 +74,7 @@
     private static final int CALLBACK_MOBILE_REQUEST = 3;
 
     private final Context mContext;
+    private final SharedLog mLog;
     private final StateMachine mTarget;
     private final Handler mHandler;
     private final int mWhat;
@@ -84,16 +86,18 @@
     private boolean mDunRequired;
     private Network mCurrentDefault;
 
-    public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
+    public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what, SharedLog log) {
         mContext = ctx;
         mTarget = tgt;
         mHandler = mTarget.getHandler();
         mWhat = what;
+        mLog = log.forSubComponent(TAG);
     }
 
     @VisibleForTesting
-    public UpstreamNetworkMonitor(StateMachine tgt, int what, ConnectivityManager cm) {
-        this(null, tgt, what);
+    public UpstreamNetworkMonitor(
+            StateMachine tgt, int what, ConnectivityManager cm, SharedLog log) {
+        this(null, tgt, what, log);
         mCM = cm;
     }
 
@@ -136,7 +140,7 @@
 
     public void registerMobileNetworkRequest() {
         if (mMobileNetworkCallback != null) {
-            Log.e(TAG, "registerMobileNetworkRequest() already registered");
+            mLog.e("registerMobileNetworkRequest() already registered");
             return;
         }
 
@@ -156,7 +160,7 @@
         // TODO: Change the timeout from 0 (no onUnavailable callback) to some
         // moderate callback timeout. This might be useful for updating some UI.
         // Additionally, we log a message to aid in any subsequent debugging.
-        Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
+        mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest);
 
         cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
     }
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 0dbfa56..10ef1be 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -16,6 +16,7 @@
     $(LOCAL_REL_DIR)/com_android_server_am_ActivityManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp \
     $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_HardwarePropertiesManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \
@@ -58,6 +59,9 @@
     liblog \
     libhardware \
     libhardware_legacy \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
     libkeystore_binder \
     libnativehelper \
     libutils \
@@ -73,4 +77,5 @@
     libEGL \
     libGLESv2 \
     libnetutils \
+    android.hardware.tetheroffload.config@1.0 \
 
diff --git a/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
new file mode 100644
index 0000000..241ccf6
--- /dev/null
+++ b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <hidl/HidlSupport.h>
+#include <jni.h>
+#include <JNIHelp.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netlink.h>
+#include <sys/socket.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
+
+#define LOG_TAG "OffloadHardwareInterface"
+#include <utils/Log.h>
+
+namespace android {
+
+using hardware::hidl_handle;
+using hardware::hidl_string;
+using hardware::tetheroffload::config::V1_0::IOffloadConfig;
+
+namespace {
+
+inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
+    return reinterpret_cast<const sockaddr *>(nladdr);
+}
+
+int conntrackSocket(unsigned groups) {
+    base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
+    if (s.get() < 0) return -errno;
+
+    const struct sockaddr_nl bind_addr = {
+        .nl_family = AF_NETLINK,
+        .nl_pad = 0,
+        .nl_pid = 0,
+        .nl_groups = groups,
+    };
+    if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
+        return -errno;
+    }
+
+    const struct sockaddr_nl kernel_addr = {
+        .nl_family = AF_NETLINK,
+        .nl_pad = 0,
+        .nl_pid = 0,
+        .nl_groups = groups,
+    };
+    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
+        return -errno;
+    }
+
+    return s.release();
+}
+
+// Return a hidl_handle that owns the file descriptor owned by fd, and will
+// auto-close it (otherwise there would be double-close problems).
+//
+// Rely upon the compiler to eliminate the constexprs used for clarity.
+hidl_handle&& handleFromFileDescriptor(base::unique_fd fd) {
+    hidl_handle h;
+
+    NATIVE_HANDLE_DECLARE_STORAGE(storage, 0, 0);
+    static constexpr int kNumFds = 1;
+    static constexpr int kNumInts = 0;
+    native_handle_t *nh = native_handle_init(storage, kNumFds, kNumInts);
+    nh->data[0] = fd.release();
+
+    static constexpr bool kTakeOwnership = true;
+    h.setTo(nh, kTakeOwnership);
+
+    return std::move(h);
+}
+
+}  // namespace
+
+static jboolean android_server_connectivity_tethering_OffloadHardwareInterface_configOffload(
+        JNIEnv* /* env */) {
+    sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
+    if (configInterface.get() == nullptr) {
+        ALOGD("Could not find IOffloadConfig service.");
+        return false;
+    }
+
+    // Per the IConfigOffload definition:
+    //
+    // fd1   A file descriptor bound to the following netlink groups
+    //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
+    //
+    // fd2   A file descriptor bound to the following netlink groups
+    //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
+    base::unique_fd
+            fd1(conntrackSocket(NFNLGRP_CONNTRACK_NEW | NFNLGRP_CONNTRACK_DESTROY)),
+            fd2(conntrackSocket(NFNLGRP_CONNTRACK_UPDATE | NFNLGRP_CONNTRACK_DESTROY));
+    if (fd1.get() < 0 || fd2.get() < 0) {
+        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
+        return false;
+    }
+
+    hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
+                h2(handleFromFileDescriptor(std::move(fd2)));
+
+    bool rval;
+    hidl_string msg;
+    configInterface->setHandles(h1, h2,
+            [&rval, &msg](bool success, const hidl_string& errMsg) {
+                rval = success;
+                msg = errMsg;
+            });
+    if (!rval) {
+        ALOGE("IOffloadConfig::setHandles() error: %s", msg.c_str());
+    }
+
+    return rval;
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gMethods[] = {
+    /* name, signature, funcPtr */
+    { "configOffload", "()Z",
+      (void*) android_server_connectivity_tethering_OffloadHardwareInterface_configOffload },
+};
+
+int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv* env) {
+    return jniRegisterNativeMethods(env,
+            "com/android/server/connectivity/tethering/OffloadHardwareInterface",
+            gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index d3341e5..5d7291a 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -40,6 +40,7 @@
 int register_android_server_location_GnssLocationProvider(JNIEnv* env);
 int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
+int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
 int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
 int register_android_server_tv_TvUinputBridge(JNIEnv* env);
 int register_android_server_tv_TvInputHal(JNIEnv* env);
@@ -78,6 +79,7 @@
     register_android_server_location_GnssLocationProvider(env);
     register_android_server_location_FlpHardwareProvider(env);
     register_android_server_connectivity_Vpn(env);
+    register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
     register_android_server_AssetAtlasService(env);
     register_android_server_ConsumerIrService(env);
     register_android_server_BatteryStatsService(env);
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
new file mode 100644
index 0000000..343d237
--- /dev/null
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.StringJoiner;
+
+
+/**
+ * Class to centralize logging functionality for tethering.
+ *
+ * All access to class methods other than dump() must be on the same thread.
+ *
+ * @hide
+ */
+public class SharedLog {
+    private final static int DEFAULT_MAX_RECORDS = 500;
+    private final static String COMPONENT_DELIMITER = ".";
+
+    private enum Category {
+        NONE,
+        ERROR,
+        MARK,
+        WARN,
+    };
+
+    private final LocalLog mLocalLog;
+    // The tag to use for output to the system log. This is not output to the
+    // LocalLog because that would be redundant.
+    private final String mTag;
+    // The component (or subcomponent) of a system that is sharing this log.
+    // This can grow in depth if components call forSubComponent() to obtain
+    // their SharedLog instance. The tag is not included in the component for
+    // brevity.
+    private final String mComponent;
+
+    public SharedLog(String tag) {
+        this(DEFAULT_MAX_RECORDS, tag);
+    }
+
+    public SharedLog(int maxRecords, String tag) {
+        this(new LocalLog(maxRecords), tag, tag);
+    }
+
+    private SharedLog(LocalLog localLog, String tag, String component) {
+        mLocalLog = localLog;
+        mTag = tag;
+        mComponent = component;
+    }
+
+    public SharedLog forSubComponent(String component) {
+        if (!isRootLogInstance()) {
+            component = mComponent + COMPONENT_DELIMITER + component;
+        }
+        return new SharedLog(mLocalLog, mTag, component);
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
+    }
+
+    //////
+    // Methods that both log an entry and emit it to the system log.
+    //////
+
+    public void e(Exception e) {
+        Log.e(mTag, record(Category.ERROR, e.toString()));
+    }
+
+    public void e(String msg) {
+        Log.e(mTag, record(Category.ERROR, msg));
+    }
+
+    public void i(String msg) {
+        Log.i(mTag, record(Category.NONE, msg));
+    }
+
+    public void w(String msg) {
+        Log.w(mTag, record(Category.WARN, msg));
+    }
+
+    //////
+    // Methods that only log an entry (and do NOT emit to the system log).
+    //////
+
+    public void log(String msg) {
+        record(Category.NONE, msg);
+    }
+
+    public void mark(String msg) {
+        record(Category.MARK, msg);
+    }
+
+    private String record(Category category, String msg) {
+        final String entry = logLine(category, msg);
+        mLocalLog.log(entry);
+        return entry;
+    }
+
+    private String logLine(Category category, String msg) {
+        final StringJoiner sj = new StringJoiner(" ");
+        if (!isRootLogInstance()) sj.add("[" + mComponent + "]");
+        if (category != Category.NONE) sj.add(category.toString());
+        return sj.add(msg).toString();
+    }
+
+    // Check whether this SharedLog instance is nominally the top level in
+    // a potential hierarchy of shared logs (the root of a tree),
+    // or is a subcomponent within the hierarchy.
+    private boolean isRootLogInstance() {
+        return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag);
+    }
+}
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index fc406ee..251d5bb 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -22,13 +22,12 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.telephony.mbms.IMbmsStreamingManagerCallback;
-import android.telephony.mbms.IStreamingServiceCallback;
 import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.MbmsStreamingManagerCallback;
 import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.StreamingServiceCallback;
 import android.telephony.mbms.StreamingServiceInfo;
 import android.telephony.mbms.vendor.IMbmsStreamingService;
 import android.util.Log;
@@ -78,14 +77,14 @@
     };
     private List<ServiceListener> mServiceListeners = new LinkedList<>();
 
-    private IMbmsStreamingManagerCallback mCallbackToApp;
+    private MbmsStreamingManagerCallback mCallbackToApp;
     private final String mAppName;
 
     private final Context mContext;
     private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
 
     /** @hide */
-    private MbmsStreamingManager(Context context, IMbmsStreamingManagerCallback listener,
+    private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback listener,
                     String streamingAppName, int subscriptionId) {
         mContext = context;
         mAppName = streamingAppName;
@@ -96,18 +95,18 @@
     /**
      * Create a new MbmsStreamingManager using the given subscription ID.
      *
-     * Note that this call will bind a remote service and that may take a bit.  This
-     * may throw an {@link MbmsException}, indicating errors that may happen during
-     * the initialization or binding process.
+     * Note that this call will bind a remote service. You may not call this method on your app's
+     * main thread. This may throw an {@link MbmsException}, indicating errors that may happen
+     * during the initialization or binding process.
      *
-     * @param context
-     * @param listener
-     * @param streamingAppName
-     * @param subscriptionId
-     * @return
+     * @param context The {@link Context} to use.
+     * @param listener A callback object on which you wish to receive results of asynchronous
+     *                 operations.
+     * @param streamingAppName The name of the streaming app, as specified by the carrier.
+     * @param subscriptionId The subscription ID to use.
      */
     public static MbmsStreamingManager create(Context context,
-            IMbmsStreamingManagerCallback listener, String streamingAppName, int subscriptionId)
+            MbmsStreamingManagerCallback listener, String streamingAppName, int subscriptionId)
             throws MbmsException {
         MbmsStreamingManager manager = new MbmsStreamingManager(context, listener,
                 streamingAppName, subscriptionId);
@@ -117,12 +116,10 @@
 
     /**
      * Create a new MbmsStreamingManager using the system default data subscription ID.
-     *
-     * Note that this call will bind a remote service and that may take a bit.  This
-     * may throw an IllegalArgumentException or RemoteException.
+     * See {@link #create(Context, MbmsStreamingManagerCallback, String, int)}.
      */
     public static MbmsStreamingManager create(Context context,
-            IMbmsStreamingManagerCallback listener, String streamingAppName)
+            MbmsStreamingManagerCallback listener, String streamingAppName)
             throws MbmsException {
         int subId = SubscriptionManager.getDefaultSubscriptionId();
         MbmsStreamingManager manager = new MbmsStreamingManager(context, listener,
@@ -156,53 +153,63 @@
      *
      * Multiple calls replace the list of serviceClasses of interest.
      *
-     * May throw an IllegalArgumentException or RemoteException.
+     * This may throw an {@link MbmsException} containing one of the following errors:
+     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION}
+     * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
      *
-     * Synchronous responses include
-     * <li>SUCCESS</li>
-     * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
-     *
-     * Asynchronous errors through the listener include any of the errors except
-     * <li>ERROR_MSDC_UNABLE_TO_)START_SERVICE</li>
-     * <li>ERROR_MSDC_INVALID_SERVICE_ID</li>
-     * <li>ERROR_MSDC_END_OF_SESSION</li>
+     * Asynchronous error codes via the {@link MbmsStreamingManagerCallback#error(int, String)}
+     * callback can include any of the errors except:
+     * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}
+     * {@link MbmsException#ERROR_END_OF_SESSION}
      */
-    public int getStreamingServices(List<String> classList) {
-        return 0;
+    public void getStreamingServices(List<String> classList) throws MbmsException {
+        if (mService == null) {
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+        }
+        try {
+            int returnCode = mService.getStreamingServices(mAppName, mSubscriptionId, classList);
+            if (returnCode != MbmsException.SUCCESS) {
+                throw new MbmsException(returnCode);
+            }
+        } catch (RemoteException e) {
+            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
+        }
     }
 
     /**
      * Starts streaming a requested service, reporting status to the indicated listener.
-     * Returns an object used to control that stream.
+     * Returns an object used to control that stream. The stream may not be ready for consumption
+     * immediately upon return from this method -- wait until the streaming state has been
+     * reported via {@link android.telephony.mbms.StreamingServiceCallback#streamStateChanged(int)}.
      *
-     * May throw an IllegalArgumentException or RemoteException.
+     * May throw an {@link MbmsException} containing any of the following error codes:
+     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION}
+     * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+     *
+     * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      *
      * Asynchronous errors through the listener include any of the errors
      */
     public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
-            IStreamingServiceCallback listener) {
-        return null;
-    }
+            StreamingServiceCallback listener) throws MbmsException {
+        if (mService == null) {
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+        }
 
-    /**
-     * Lists all the services currently being streamed to the device by this application
-     * on this given subId.  Results are returned asynchronously through the previously
-     * registered callback.
-     *
-     * May throw a RemoteException.
-     *
-     * The return value is a success/error-code with the following possible values:
-     * <li>SUCCESS</li>
-     * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
-     *
-     * Asynchronous errors through the listener include any of the errors except
-     * <li>ERROR_UNABLED_TO_START_SERVICE</li>
-     * <li>ERROR_MSDC_INVALID_SERVICE_ID</li>
-     * <li>ERROR_MSDC_END_OF_SESSION</li>
-     *
-     */
-    public int getActiveStreamingServices() {
-        return 0;
+        try {
+            int returnCode = mService.startStreaming(
+                    mAppName, mSubscriptionId, serviceInfo.getServiceId(), listener);
+            if (returnCode != MbmsException.SUCCESS) {
+                throw new MbmsException(returnCode);
+            }
+        } catch (RemoteException e) {
+            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
+        }
+
+        return new StreamingService(
+                mAppName, mSubscriptionId, mService, serviceInfo, listener);
     }
 
     private void bindAndInitialize() throws MbmsException {
@@ -262,7 +269,7 @@
             } catch (RemoteException e) {
                 mService = null;
                 Log.e(LOG_TAG, "Service died before initialization");
-                throw new MbmsException(MbmsException.ERROR_INITIALIZATION_REMOTE_EXCEPTION);
+                throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
             }
         }
     }
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
new file mode 100644
index 0000000..0cb4cff
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.telephony.ITelephony;
+
+/**
+ * Allows applications to request the system to perform a network scan.
+ *
+ * The caller of {@link #requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} will
+ * receive a NetworkScan which contains the callback method to stop the scan requested.
+ * @hide
+ */
+public class NetworkScan {
+
+    public static final String TAG = "NetworkScan";
+
+    public static final int SUCCESS = 0;
+    public static final int ERROR_INVALID_SCAN = 1;
+    public static final int ERROR_UNSUPPORTED = 2;
+    public static final int ERROR_INTERRUPTED = 3;
+    public static final int ERROR_CANCELLED = 4;
+
+    private final int mScanId;
+    private final int mSubId;
+
+    /**
+     * Stops the network scan
+     *
+     * This is the callback method to stop an ongoing scan. When user requests a new scan,
+     * a NetworkScan object will be returned, and the user can stop the scan by calling this
+     * method.
+     */
+    public void stop() throws RemoteException {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.stopNetworkScan(mSubId, mScanId);
+            } else {
+                throw new RemoteException("Failed to get the ITelephony instance.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "stopNetworkScan  RemoteException", ex);
+            throw new RemoteException("Failed to stop the network scan with id " + mScanId);
+        }
+    }
+
+    /**
+     * Creates a new NetworkScan with scanId
+     *
+     * @param scanId The id of the scan
+     * @param subId the id of the subscription
+     * @hide
+     */
+    public NetworkScan(int scanId, int subId) {
+        mScanId = scanId;
+        mSubId = subId;
+    }
+
+    private ITelephony getITelephony() {
+        return ITelephony.Stub.asInterface(
+            ServiceManager.getService(Context.TELEPHONY_SERVICE));
+    }
+}
diff --git a/telephony/java/android/telephony/NetworkScanRequest.aidl b/telephony/java/android/telephony/NetworkScanRequest.aidl
new file mode 100644
index 0000000..5addb1c
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkScanRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable NetworkScanRequest;
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
new file mode 100644
index 0000000..0a542a7
--- /dev/null
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Defines a request to peform a network scan.
+ *
+ * This class defines whether the network scan will be performed only once or periodically until
+ * cancelled, when the scan is performed periodically, the time interval is not controlled by the
+ * user but defined by the modem vendor.
+ * @hide
+ */
+public final class NetworkScanRequest implements Parcelable {
+
+    /** Performs the scan only once */
+    public static final int SCAN_TYPE_ONE_SHOT = 0;
+    /**
+     * Performs the scan periodically until cancelled
+     *
+     * The modem will start new scans periodically, and the interval between two scans is usually
+     * multiple minutes.
+     * */
+    public static final int SCAN_TYPE_PERIODIC = 1;
+
+    /** Defines the type of the scan. */
+    public int scanType;
+
+    /** Describes the radio access technologies with bands or channels that need to be scanned. */
+    public RadioAccessSpecifier[] specifiers;
+
+    /**
+     * Creates a new NetworkScanRequest with scanType and network specifiers
+     *
+     * @param scanType The type of the scan
+     * @param specifiers the radio network with bands / channels to be scanned
+     */
+    public NetworkScanRequest(int scanType, RadioAccessSpecifier[] specifiers) {
+        this.scanType = scanType;
+        this.specifiers = specifiers;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(scanType);
+        dest.writeParcelableArray(specifiers, flags);
+    }
+
+    private NetworkScanRequest(Parcel in) {
+        scanType = in.readInt();
+        specifiers = (RadioAccessSpecifier[]) in.readParcelableArray(
+                Object.class.getClassLoader(),
+                RadioAccessSpecifier.class);
+    }
+
+    @Override
+    public boolean equals (Object o) {
+        NetworkScanRequest nsr;
+
+        try {
+            nsr = (NetworkScanRequest) o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return (scanType == nsr.scanType
+                && Arrays.equals(specifiers, nsr.specifiers));
+    }
+
+    @Override
+    public int hashCode () {
+        return ((scanType * 31)
+                + (Arrays.hashCode(specifiers)) * 37);
+    }
+
+    public static final Creator<NetworkScanRequest> CREATOR =
+            new Creator<NetworkScanRequest>() {
+                @Override
+                public NetworkScanRequest createFromParcel(Parcel in) {
+                    return new NetworkScanRequest(in);
+                }
+
+                @Override
+                public NetworkScanRequest[] newArray(int size) {
+                    return new NetworkScanRequest[size];
+                }
+            };
+}
diff --git a/telephony/java/android/telephony/RadioAccessSpecifier.aidl b/telephony/java/android/telephony/RadioAccessSpecifier.aidl
new file mode 100644
index 0000000..7e09e0b
--- /dev/null
+++ b/telephony/java/android/telephony/RadioAccessSpecifier.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+parcelable RadioAccessSpecifier;
diff --git a/telephony/java/android/telephony/RadioAccessSpecifier.java b/telephony/java/android/telephony/RadioAccessSpecifier.java
new file mode 100644
index 0000000..33ce8b4
--- /dev/null
+++ b/telephony/java/android/telephony/RadioAccessSpecifier.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Describes a particular radio access network to be scanned.
+ *
+ * The scan can be performed on either bands or channels for a specific radio access network type.
+ * @hide
+ */
+public final class RadioAccessSpecifier implements Parcelable {
+
+    /**
+     * The radio access network that needs to be scanned
+     *
+     * See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
+     */
+    public int radioAccessNetwork;
+
+    /**
+     * The frequency bands that need to be scanned
+     *
+     * bands must be used together with radioAccessNetwork
+     *
+     * See {@link RadioNetworkConstants} for details.
+     */
+    public int[] bands;
+
+    /**
+     * The frequency channels that need to be scanned
+     *
+     * channels must be used together with radioAccessNetwork
+     *
+     * See {@link RadioNetworkConstants.RadioAccessNetworks} for details.
+     */
+    public int[] channels;
+
+    /**
+    * Creates a new RadioAccessSpecifier with radio network, bands and channels
+    *
+    * The user must specify the radio network type, and at least specify either of frequency
+    * bands or channels.
+    *
+    * @param ran The type of the radio access network
+    * @param bands the frequency bands to be scanned
+    * @param channels the frequency bands to be scanned
+    */
+    public RadioAccessSpecifier(int ran, int[] bands, int[] channels) {
+        this.radioAccessNetwork = ran;
+        this.bands = bands;
+        this.channels = channels;
+    }
+
+    public static final Parcelable.Creator<RadioAccessSpecifier> CREATOR =
+            new Parcelable.Creator<RadioAccessSpecifier> (){
+                @Override
+                public RadioAccessSpecifier createFromParcel(Parcel in) {
+                    return new RadioAccessSpecifier(in);
+                }
+
+                @Override
+                public RadioAccessSpecifier[] newArray(int size) {
+                    return new RadioAccessSpecifier[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(radioAccessNetwork);
+        dest.writeIntArray(bands);
+        dest.writeIntArray(channels);
+    }
+
+    private RadioAccessSpecifier(Parcel in) {
+        radioAccessNetwork = in.readInt();
+        bands = in.createIntArray();
+        channels = in.createIntArray();
+    }
+
+    @Override
+    public boolean equals (Object o) {
+        RadioAccessSpecifier ras;
+
+        try {
+            ras = (RadioAccessSpecifier) o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return (radioAccessNetwork == ras.radioAccessNetwork
+                && Arrays.equals(bands, ras.bands)
+                && Arrays.equals(channels, ras.channels));
+    }
+
+    @Override
+    public int hashCode () {
+        return ((radioAccessNetwork * 31)
+                + (Arrays.hashCode(bands) * 37)
+                + (Arrays.hashCode(channels)) * 39);
+    }
+}
diff --git a/telephony/java/android/telephony/RadioNetworkConstants.java b/telephony/java/android/telephony/RadioNetworkConstants.java
new file mode 100644
index 0000000..1a9072d
--- /dev/null
+++ b/telephony/java/android/telephony/RadioNetworkConstants.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Contains radio access network related constants.
+ * @hide
+ */
+public final class RadioNetworkConstants {
+
+    public static final class RadioAccessNetworks {
+        public static final int GERAN = 1;
+        public static final int UTRAN = 2;
+        public static final int EUTRAN = 3;
+        /** @hide */
+        public static final int CDMA2000 = 4;
+    }
+
+    /**
+     * Frenquency bands for GERAN.
+     * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf
+     */
+    public static final class GeranBands {
+        public static final int BAND_T380 = 1;
+        public static final int BAND_T410 = 2;
+        public static final int BAND_450 = 3;
+        public static final int BAND_480 = 4;
+        public static final int BAND_710 = 5;
+        public static final int BAND_750 = 6;
+        public static final int BAND_T810 = 7;
+        public static final int BAND_850 = 8;
+        public static final int BAND_P900 = 9;
+        public static final int BAND_E900 = 10;
+        public static final int BAND_R900 = 11;
+        public static final int BAND_DCS1800 = 12;
+        public static final int BAND_PCS1900 = 13;
+        public static final int BAND_ER900 = 14;
+    }
+
+    /**
+     * Frenquency bands for UTRAN.
+     * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf
+     */
+    public static final class UtranBands {
+        public static final int BAND_1 = 1;
+        public static final int BAND_2 = 2;
+        public static final int BAND_3 = 3;
+        public static final int BAND_4 = 4;
+        public static final int BAND_5 = 5;
+        public static final int BAND_6 = 6;
+        public static final int BAND_7 = 7;
+        public static final int BAND_8 = 8;
+        public static final int BAND_9 = 9;
+        public static final int BAND_10 = 10;
+        public static final int BAND_11 = 11;
+        public static final int BAND_12 = 12;
+        public static final int BAND_13 = 13;
+        public static final int BAND_14 = 14;
+        /** band 15, 16, 17, 18 are reserved */
+        public static final int BAND_19 = 19;
+        public static final int BAND_20 = 20;
+        public static final int BAND_21 = 21;
+        public static final int BAND_22 = 22;
+        /** band 23, 24 are reserved */
+        public static final int BAND_25 = 25;
+        public static final int BAND_26 = 26;
+    }
+
+    /**
+     * Frenquency bands for EUTRAN.
+     * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf
+     */
+    public static final class EutranBands {
+        public static final int BAND_1 = 1;
+        public static final int BAND_2 = 2;
+        public static final int BAND_3 = 3;
+        public static final int BAND_4 = 4;
+        public static final int BAND_5 = 5;
+        public static final int BAND_6 = 6;
+        public static final int BAND_7 = 7;
+        public static final int BAND_8 = 8;
+        public static final int BAND_9 = 9;
+        public static final int BAND_10 = 10;
+        public static final int BAND_11 = 11;
+        public static final int BAND_12 = 12;
+        public static final int BAND_13 = 13;
+        public static final int BAND_14 = 14;
+        public static final int BAND_17 = 17;
+        public static final int BAND_18 = 18;
+        public static final int BAND_19 = 19;
+        public static final int BAND_20 = 20;
+        public static final int BAND_21 = 21;
+        public static final int BAND_22 = 22;
+        public static final int BAND_23 = 23;
+        public static final int BAND_24 = 24;
+        public static final int BAND_25 = 25;
+        public static final int BAND_26 = 26;
+        public static final int BAND_27 = 27;
+        public static final int BAND_28 = 28;
+        public static final int BAND_30 = 30;
+        public static final int BAND_31 = 31;
+        public static final int BAND_33 = 33;
+        public static final int BAND_34 = 34;
+        public static final int BAND_35 = 35;
+        public static final int BAND_36 = 36;
+        public static final int BAND_37 = 37;
+        public static final int BAND_38 = 38;
+        public static final int BAND_39 = 39;
+        public static final int BAND_40 = 40;
+        public static final int BAND_41 = 41;
+        public static final int BAND_42 = 42;
+        public static final int BAND_43 = 43;
+        public static final int BAND_44 = 44;
+        public static final int BAND_45 = 45;
+        public static final int BAND_46 = 46;
+        public static final int BAND_47 = 47;
+        public static final int BAND_48 = 48;
+        public static final int BAND_65 = 65;
+        public static final int BAND_66 = 66;
+        public static final int BAND_68 = 68;
+        public static final int BAND_70 = 70;
+    }
+
+    /**
+     * Frenquency bands for CDMA2000.
+     * http://www.3gpp2.org/Public_html/Specs/C.S0057-E_v1.0_Bandclass_Specification.pdf
+     * @hide
+     *
+     * TODO(yinxu): Check with the nexus team about the definition of CDMA bands.
+     */
+    public static final class CdmaBands {
+        public static final int BAND_0 = 1;
+        public static final int BAND_1 = 2;
+        public static final int BAND_2 = 3;
+        public static final int BAND_3 = 4;
+        public static final int BAND_4 = 5;
+        public static final int BAND_5 = 6;
+        public static final int BAND_6 = 7;
+        public static final int BAND_7 = 8;
+        public static final int BAND_8 = 9;
+        public static final int BAND_9 = 10;
+        public static final int BAND_10 = 11;
+        public static final int BAND_11 = 12;
+        public static final int BAND_12 = 13;
+        public static final int BAND_13 = 14;
+        public static final int BAND_14 = 15;
+        public static final int BAND_15 = 16;
+        public static final int BAND_16 = 17;
+        public static final int BAND_17 = 18;
+        public static final int BAND_18 = 19;
+        public static final int BAND_19 = 20;
+        public static final int BAND_20 = 21;
+        public static final int BAND_21 = 22;
+    }
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 32536fc..5c718c7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -39,6 +39,11 @@
 import android.provider.Settings.SettingNotFoundException;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -144,6 +149,7 @@
     private final Context mContext;
     private final int mSubId;
     private SubscriptionManager mSubscriptionManager;
+    private TelephonyScanManager mTelephonyScanManager;
 
     private static String multiSimConfig =
             SystemProperties.get(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG);
@@ -4573,6 +4579,32 @@
     }
 
     /**
+     * Request a network scan.
+     *
+     * This method is asynchronous, so the network scan results will be returned by callback.
+     * The returned NetworkScan will contain a callback method which can be used to stop the scan.
+     *
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * @param request Contains all the RAT with bands/channels that need to be scanned.
+     * @param callback Returns network scan results or errors.
+     * @return A NetworkScan obj which contains a callback which can stop the scan.
+     * @hide
+     */
+    public NetworkScan requestNetworkScan(
+            NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
+        synchronized (this) {
+            if (mTelephonyScanManager == null) {
+                mTelephonyScanManager = new TelephonyScanManager();
+            }
+        }
+        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, callback);
+    }
+
+    /**
      * Ask the radio to connect to the input network and change selection mode to manual.
      *
      * <p>
@@ -5289,9 +5321,10 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.isDataConnectivityPossible();
+                return telephony.isDataConnectivityPossible(getSubId(SubscriptionManager
+                        .getDefaultDataSubscriptionId()));
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isDataConnectivityPossible", e);
+            Log.e(TAG, "Error calling ITelephony#isDataAllowed", e);
         }
         return false;
     }
@@ -5696,35 +5729,75 @@
     }
 
     /**
-     * Set SIM card power state. Request is equivalent to inserting or removing the card.
+     * Requested state of SIM
      *
-     * @param powerUp True if powering up the SIM, otherwise powering down
+     * CARD_POWER_DOWN
+     * Powers down the SIM. SIM must be up prior.
+     *
+     * CARD_POWER_UP
+     * Powers up the SIM normally. SIM must be down prior.
+     *
+     * CARD_POWER_UP_PASS_THROUGH
+     * Powers up the SIM in PASS_THROUGH mode. SIM must be down prior.
+     * When SIM is powered up in PASS_THOUGH mode, the modem does not send
+     * any command to it (for example SELECT of MF, or TERMINAL CAPABILITY),
+     * and the SIM card is controlled completely by Telephony sending APDUs
+     * directly. The SIM card state will be RIL_CARDSTATE_PRESENT and the
+     * number of card apps will be 0.
+     * No new error code is generated. Emergency calls are supported in the
+     * same way as if the SIM card is absent.
+     * The PASS_THROUGH mode is valid only for the specific card session where it
+     * is activated, and normal behavior occurs at the next SIM initialization,
+     * unless PASS_THROUGH mode is requested again. Hence, the last power-up mode
+     * is NOT persistent across boots. On reboot, SIM will power up normally.
+     */
+    /** @hide */
+    public static final int CARD_POWER_DOWN = 0;
+    /** @hide */
+    public static final int CARD_POWER_UP = 1;
+    /** @hide */
+    public static final int CARD_POWER_UP_PASS_THROUGH = 2;
+
+    /**
+     * Set SIM card power state.
+     *
+     * @param state  State of SIM (power down, power up, pass through)
+     * @see #CARD_POWER_DOWN
+     * @see #CARD_POWER_UP
+     * @see #CARD_POWER_UP_PASS_THROUGH
+     * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED}
+     * broadcasts to determine success or failure and timeout if needed.
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
      * @hide
      **/
-    public void setSimPowerState(boolean powerUp) {
-        setSimPowerStateForSlot(getSlotIndex(), powerUp);
+    public void setSimPowerState(int state) {
+        setSimPowerStateForSlot(getSlotIndex(), state);
     }
 
     /**
-     * Set SIM card power state. Request is equivalent to inserting or removing the card.
+     * Set SIM card power state.
      *
      * @param slotIndex SIM slot id
-     * @param powerUp True if powering up the SIM, otherwise powering down
+     * @param state  State of SIM (power down, power up, pass through)
+     * @see #CARD_POWER_DOWN
+     * @see #CARD_POWER_UP
+     * @see #CARD_POWER_UP_PASS_THROUGH
+     * Callers should monitor for {@link TelephonyIntents#ACTION_SIM_STATE_CHANGED}
+     * broadcasts to determine success or failure and timeout if needed.
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
      * @hide
      **/
-    public void setSimPowerStateForSlot(int slotIndex, boolean powerUp) {
+    public void setSimPowerStateForSlot(int slotIndex, int state) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                telephony.setSimPowerStateForSlot(slotIndex, powerUp);
+                telephony.setSimPowerStateForSlot(slotIndex, state);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e);
@@ -6442,5 +6515,26 @@
         }
         return false;
     }
+
+    /**
+     * Get the most recently available signal strength information.
+     *
+     * Get the most recent SignalStrength information reported by the modem. Due
+     * to power saving this information may not always be current.
+     * @return the most recent cached signal strength info from the modem
+     * @hide
+     */
+    @Nullable
+    public SignalStrength getSignalStrength() {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.getSignalStrength(getSubId());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getSignalStrength", e);
+        }
+        return null;
+    }
 }
 
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
new file mode 100644
index 0000000..c905d3a
--- /dev/null
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.SparseArray;
+import java.util.List;
+
+import com.android.internal.telephony.ITelephony;
+
+/**
+ * Manages the radio access network scan requests and callbacks.
+ * @hide
+ */
+public final class TelephonyScanManager {
+
+    private static final String TAG = "TelephonyScanManager";
+
+    /** @hide */
+    public static final int CALLBACK_SCAN_RESULTS = 1;
+    /** @hide */
+    public static final int CALLBACK_SCAN_ERROR = 2;
+    /** @hide */
+    public static final int CALLBACK_SCAN_COMPLETE = 3;
+
+    /**
+     * The caller of {@link #requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} should
+     * implement and provide this callback so that the scan results or errors can be returned.
+     */
+    public static abstract class NetworkScanCallback {
+        /** Returns the scan results to the user, this callback will be called multiple times. */
+        public void onResults(List<CellInfo> results) {}
+
+        /**
+         * Informs the user that the scan has stopped.
+         *
+         * This callback will be called when the scan is finished or cancelled by the user.
+         * The related NetworkScanRequest will be deleted after this callback.
+         */
+        public void onComplete() {}
+
+        /**
+         * Informs the user that there is some error about the scan.
+         *
+         * This callback will be called whenever there is any error about the scan, but the scan
+         * won't stop unless the onComplete() callback is called.
+         */
+        public void onError(int error) {}
+    }
+
+    private static class NetworkScanInfo {
+        private final NetworkScanRequest mRequest;
+        private final NetworkScanCallback mCallback;
+
+        NetworkScanInfo(NetworkScanRequest request, NetworkScanCallback callback) {
+            mRequest = request;
+            mCallback = callback;
+        }
+    }
+
+    private final Looper mLooper;
+    private final Messenger mMessenger;
+    private SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
+
+    public TelephonyScanManager() {
+        HandlerThread thread = new HandlerThread(TAG);
+        thread.start();
+        mLooper = thread.getLooper();
+        mMessenger = new Messenger(new Handler(mLooper) {
+            @Override
+            public void handleMessage(Message message) {
+                checkNotNull(message, "message cannot be null");
+                NetworkScanInfo nsi;
+                synchronized (mScanInfo) {
+                    nsi = mScanInfo.get(message.arg2);
+                }
+                if (nsi == null) {
+                    throw new RuntimeException(
+                        "Failed to find NetworkScanInfo with id " + message.arg2);
+                }
+                NetworkScanCallback callback = nsi.mCallback;
+                if (callback == null) {
+                    throw new RuntimeException(
+                        "Failed to find NetworkScanCallback with id " + message.arg2);
+                }
+
+                switch (message.what) {
+                    case CALLBACK_SCAN_RESULTS:
+                        try {
+                            callback.onResults((List<CellInfo>) message.obj);
+                        } catch (Exception e) {
+                            Rlog.e(TAG, "Exception in networkscan callback onResults", e);
+                        }
+                        break;
+                    case CALLBACK_SCAN_ERROR:
+                        try {
+                            callback.onError(message.arg1);
+                        } catch (Exception e) {
+                            Rlog.e(TAG, "Exception in networkscan callback onError", e);
+                        }
+                        break;
+                    case CALLBACK_SCAN_COMPLETE:
+                        try {
+                            callback.onComplete();
+                            mScanInfo.remove(message.arg2);
+                        } catch (Exception e) {
+                            Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
+                        }
+                        break;
+                    default:
+                        Rlog.e(TAG, "Unhandled message " + Integer.toHexString(message.what));
+                        break;
+                }
+            }
+        });
+    }
+
+    /**
+     * Request a network scan.
+     *
+     * This method is asynchronous, so the network scan results will be returned by callback.
+     * The returned NetworkScan will contain a callback method which can be used to stop the scan.
+     *
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * @param request Contains all the RAT with bands/channels that need to be scanned.
+     * @param callback Returns network scan results or errors.
+     * @return A NetworkScan obj which contains a callback which can stop the scan.
+     * @hide
+     */
+    public NetworkScan requestNetworkScan(int subId,
+            NetworkScanRequest request, NetworkScanCallback callback) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder());
+                saveScanInfo(scanId, request, callback);
+                return new NetworkScan(scanId, subId);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "requestNetworkScan RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "requestNetworkScan NPE", ex);
+        }
+        return null;
+    }
+
+    private void saveScanInfo(int id, NetworkScanRequest request, NetworkScanCallback callback) {
+        synchronized (mScanInfo) {
+            mScanInfo.put(id, new NetworkScanInfo(request, callback));
+        }
+    }
+
+    private ITelephony getITelephony() {
+        return ITelephony.Stub.asInterface(
+            ServiceManager.getService(Context.TELEPHONY_SERVICE));
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
index 7b4ecf2..891edad 100755
--- a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
@@ -17,15 +17,13 @@
 package android.telephony.mbms;
 
 import android.net.Uri;
-import android.telephony.SignalStrength;
-import android.telephony.mbms.StreamingService;
 
 /**
  * @hide
  */
 oneway interface IStreamingServiceCallback {
     void error(int errorCode, String message);
-    void streamStateChanged(in StreamingService service, int state);
+    void streamStateChanged(int state);
     void uriUpdated(in Uri uri);
-    void signalStrengthUpdated(in SignalStrength signalStrength);
+    void broadcastSignalStrengthUpdated(int signalStrength);
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index cc4a02a..a0ded9b 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -16,22 +16,25 @@
 
 package android.telephony.mbms;
 
-import android.os.RemoteException;
-
 /** @hide */
-public class MbmsException extends RemoteException {
+public class MbmsException extends Exception {
     public static final int SUCCESS = 0;
     public static final int ERROR_NO_SERVICE_INSTALLED = 1;
     public static final int ERROR_MULTIPLE_SERVICES_INSTALLED = 2;
     public static final int ERROR_BIND_TIMEOUT_OR_FAILURE = 3;
-    public static final int ERROR_INITIALIZATION_REMOTE_EXCEPTION = 4;
+    public static final int ERROR_UNKNOWN_REMOTE_EXCEPTION = 4;
     public static final int ERROR_ALREADY_INITIALIZED = 5;
+    public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 6;
+    public static final int ERROR_MIDDLEWARE_NOT_BOUND = 7;
+    public static final int ERROR_UNABLE_TO_START_SERVICE = 8;
+    public static final int ERROR_STREAM_ALREADY_STARTED = 9;
+    public static final int ERROR_END_OF_SESSION = 10;
 
     private final int mErrorCode;
 
     /** @hide
      * TODO: future systemapi
-     * */
+     */
     public MbmsException(int errorCode) {
         super();
         mErrorCode = errorCode;
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index cb621c8..f167f0ab 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -114,6 +114,7 @@
         sessionEndTime = (java.util.Date) in.readSerializable();
     }
 
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         Set<Locale> keySet = names.keySet();
         dest.writeInt(keySet.size());
@@ -128,7 +129,33 @@
         dest.writeSerializable(sessionEndTime);
     }
 
+    @Override
     public int describeContents() {
         return 0;
     }
+
+    public Map<Locale, String> getNames() {
+        return names;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public Locale getLocale() {
+        return locale;
+    }
+
+    public String getServiceId() {
+        return serviceId;
+    }
+
+    public Date getSessionStartTime() {
+        return sessionStartTime;
+    }
+
+    public Date getSessionEndTime() {
+        return sessionEndTime;
+    }
+
 }
diff --git a/telephony/java/android/telephony/mbms/StreamingService.aidl b/telephony/java/android/telephony/mbms/StreamingService.aidl
deleted file mode 100755
index 0c286f3..0000000
--- a/telephony/java/android/telephony/mbms/StreamingService.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-** 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;
-
-parcelable StreamingService;
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index 8cc6043..ac5fbdf 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -19,46 +19,60 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.SignalStrength;
+import android.os.RemoteException;
+import android.telephony.mbms.vendor.IMbmsStreamingService;
+import android.util.Log;
 
 /**
  * @hide
  */
 public class StreamingService {
-
+    private static final String LOG_TAG = "MbmsStreamingService";
     public final static int STATE_STOPPED = 1;
     public final static int STATE_STARTED = 2;
     public final static int STATE_STALLED = 3;
 
+    private final String mAppName;
+    private final int mSubscriptionId;
+    private final IMbmsStreamingService mService;
+    private final StreamingServiceInfo mServiceInfo;
+    private final IStreamingServiceCallback mCallback;
     /**
+     * @hide
      */
-    StreamingService(StreamingServiceInfo streamingServiceInfo,
-            IStreamingServiceCallback listener) {
+    public StreamingService(String appName,
+            int subscriptionId,
+            IMbmsStreamingService service,
+            StreamingServiceInfo streamingServiceInfo,
+            IStreamingServiceCallback callback) {
+        mAppName = appName;
+        mSubscriptionId = subscriptionId;
+        mService = service;
+        mServiceInfo = streamingServiceInfo;
+        mCallback = callback;
     }
 
     /**
      * Retreive the Uri used to play this stream.
      *
-     * This may throw a RemoteException.
+     * This may throw a {@link MbmsException} with the error code
+     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION}
+     * @return The {@link Uri} to pass to the streaming client.
      */
-    public Uri getPlaybackUri() {
-        return null;
+    public Uri getPlaybackUri() throws MbmsException {
+        try {
+            return mService.getPlaybackUri(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Caught remote exception calling getPlaybackUri: " + e);
+            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
+        }
     }
 
     /**
      * Retreive the info for this StreamingService.
      */
     public StreamingServiceInfo getInfo() {
-        return null;
-    }
-
-    /**
-     * Retreive the current state of this stream.
-     *
-     * This may throw a RemoteException.
-     */
-    public int getState() {
-        return STATE_STOPPED;
+        return mServiceInfo;
     }
 
     /**
@@ -69,41 +83,13 @@
     public void stopStreaming() {
     }
 
-    /**
-     * Switch this stream to a different service.  Used for smooth transitions.
-     *
-     * This may throw a RemoteException.
-     *
-     * Asynchronous errors through the listener include any of the errors except
-     * <li>ERROR_MSDC_UNABLE_TO_INITIALIZE</li>
-     */
-    public void switchStream(StreamingServiceInfo streamingServiceInfo) {
-    }
-
-    public void dispose() {
-    }
-
-    public static final Parcelable.Creator<StreamingService> CREATOR =
-            new Parcelable.Creator<StreamingService>() {
-        @Override
-        public StreamingService createFromParcel(Parcel in) {
-            return new StreamingService(in);
+    public void dispose() throws MbmsException {
+        try {
+            mService.disposeStream(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Caught remote exception calling disposeStream: " + e);
+            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
         }
-
-        @Override
-        public StreamingService[] newArray(int size) {
-            return new StreamingService[size];
-        }
-    };
-
-    private StreamingService(Parcel in) {
-    }
-
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-
-    public int describeContents() {
-        return 0;
     }
 }
 
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
index 7f5c486..bd0a1b3 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
@@ -17,14 +17,21 @@
 package android.telephony.mbms;
 
 import android.net.Uri;
-import android.telephony.SignalStrength;
 
 /**
- * A Callback class for use when the applicaiton is actively streaming content.
+ * A Callback class for use when the application is actively streaming content.
  * @hide
  */
 public class StreamingServiceCallback extends IStreamingServiceCallback.Stub {
 
+    /**
+     * Indicates broadcast signal strength is not available for this service.
+     *
+     * This may be due to the service no longer being available due to geography
+     * or timing (end of service) or because lack of demand has caused the service
+     * to be delivered via unicast.
+     */
+    public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
 
     public void error(int errorCode, String message) {
         // default implementation empty
@@ -36,7 +43,7 @@
      * See {@link StreamingService#STATE_STOPPED}, {@link StreamingService#STATE_STARTED}
      * and {@link StreamingService#STATE_STALLED}.
      */
-    public void streamStateChanged(StreamingService service, int state) {
+    public void streamStateChanged(int state) {
         // default implementation empty
     }
 
@@ -51,19 +58,16 @@
     }
 
     /**
-     * Signal Strength updated.
+     * 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.
-     *
-     * A {@link android.telephony.SignalStrength#getLevel} result of 0 means
-     * you don't have coverage for this stream, either due to geographic
-     * restrictions, poor tower coverage or something (yards of concrete?)
-     * interferring with the signal.
+     * strength.  It a simple int from 0-4 for valid levels or
+     * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
+     * for this service due to timing, geography or popularity.
      */
-    public void signalStrengthUpdated(SignalStrength signalStrength) {
+    public void broadcastSignalStrengthUpdated(int signalStrength) {
         // default implementation empty
     }
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
index ddc661d..8ff7fa7 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
@@ -19,9 +19,7 @@
 import android.net.Uri;
 import android.telephony.mbms.IMbmsStreamingManagerCallback;
 import android.telephony.mbms.IStreamingServiceCallback;
-import android.telephony.mbms.StreamingService;
 import android.telephony.mbms.StreamingServiceInfo;
-import android.telephony.SignalStrength;
 
 /**
  * The interface the opaque MbmsStreamingService will satisfy.
@@ -29,55 +27,23 @@
  */
 interface IMbmsStreamingService
 {
-    /**
-     * Initialize streaming service
-     * Registers this listener, subId with this appName
-     *
-     */
     int initialize(IMbmsStreamingManagerCallback listener, String appName, int subId);
 
-
-    /**
-     * - Registers serviceClasses of interest with the uid/appName/subId key.
-     * - Starts asynch fetching data on streaming services of matching classes to be reported
-     * later by callback.
-     *
-     * Note that subsequent calls with the same callback, appName, subId and uid will replace
-     * the service class list.
-     */
     int getStreamingServices(String appName, int subId, in List<String> serviceClasses);
 
-    /**
-     * - Starts streaming the serviceId given.
-     * - if the uid/appName/subId don't match a previously registered callback an error will
-     *   be returned
-     * - Streaming status will be sent via the included listener, including an initial
-     *   URL-change and State-change pair.
-     */
     int startStreaming(String appName, int subId, String serviceId,
             IStreamingServiceCallback listener);
 
     /**
-     * Asynchronously fetches all Services being streamed by this uid/appName/subId.
-     */
-    int getActiveStreamingServices(String appName, int subId);
-
-
-    /**
      * Per-stream api.  Note each specifies what stream they apply to.
      */
 
     Uri getPlaybackUri(String appName, int subId, String serviceId);
 
-    void switchStreams(String appName, int subId, String oldServiceId, String newServiceId);
-
-    int getState(String appName, int subId, String serviceId);
-
     void stopStreaming(String appName, int subId, String serviceId);
 
     void disposeStream(String appName, int subId, String serviceId);
 
-
     /**
      * End of life for all MbmsStreamingManager's created by this uid/appName/subId.
      * Ends any streams run under this uid/appname/subId and calls the disposed methods
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 9f0c0e9..37f47ff 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -16,11 +16,12 @@
 
 package android.telephony.mbms.vendor;
 
+import android.annotation.Nullable;
 import android.net.Uri;
 import android.os.RemoteException;
 import android.telephony.mbms.IMbmsStreamingManagerCallback;
 import android.telephony.mbms.IStreamingServiceCallback;
-import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.MbmsException;
 
 import java.util.List;
 
@@ -29,43 +30,81 @@
  * TODO: future systemapi
  */
 public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
-
+    /**
+     * Initialize streaming service for this app and subId, registering the listener.
+     *
+     * May throw an {@link IllegalArgumentException} or a {@link SecurityException}
+     *
+     * @param listener The callback to use to communicate with the app.
+     * @param appName The app name as negotiated with the wireless carrier.
+     * @param subscriptionId The subscription ID to use.
+     * @return {@link MbmsException#SUCCESS} or {@link MbmsException#ERROR_ALREADY_INITIALIZED}
+     */
     @Override
-    public int initialize(IMbmsStreamingManagerCallback listener, String appName, int subId)
-            throws RemoteException {
+    public int initialize(IMbmsStreamingManagerCallback listener, String appName,
+            int subscriptionId) throws RemoteException {
         return 0;
     }
 
+    /**
+     * Registers serviceClasses of interest with the appName/subId key.
+     * Starts async fetching data on streaming services of matching classes to be reported
+     * later via {@link IMbmsStreamingManagerCallback#streamingServicesUpdated(List)}
+     *
+     * Note that subsequent calls with the same uid, appName and subId will replace
+     * the service class list.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param appName The app name as negotiated with the wireless carrier.
+     * @param subscriptionId The subscription id to use.
+     * @param serviceClasses The service classes that the app wishes to get info on. The strings
+     *                       may contain arbitrary data as negotiated between the app and the
+     *                       carrier.
+     * @return One of {@link MbmsException#SUCCESS},
+     *         {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND},
+     *         {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+     */
     @Override
-    public int getStreamingServices(String appName, int subId, List<String> serviceClasses)
-            throws RemoteException {
+    public int getStreamingServices(String appName, int subscriptionId,
+            List<String> serviceClasses) throws RemoteException {
         return 0;
     }
 
+    /**
+     * Starts streaming on a particular service. This method may perform asynchronous work. When
+     * the middleware is ready to send bits to the frontend, it should inform the app via
+     * {@link IStreamingServiceCallback#streamStateChanged(int)}.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param appName The app name as negotiated with the wireless carrier.
+     * @param subscriptionId The subscription id to use.
+     * @param serviceId The ID of the streaming service that the app has requested.
+     * @param listener The listener object on which the app wishes to receive updates.
+     * @return TODO: document possible errors
+     */
     @Override
-    public int startStreaming(String appName, int subId,
+    public int startStreaming(String appName, int subscriptionId,
             String serviceId, IStreamingServiceCallback listener) throws RemoteException {
         return 0;
     }
 
+    /**
+     * Retrieves the streaming URI for a particular service. If the middleware is not yet ready to
+     * stream the service, this method may return null.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * @param appName The app name as negotiated with the wireless carrier.
+     * @param subscriptionId The subscription id to use.
+     * @param serviceId The ID of the streaming service that the app has requested.
+     * @return An opaque {@link Uri} to be passed to a video player that understands the format.
+     */
     @Override
-    public int getActiveStreamingServices(String appName, int subId) throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public Uri getPlaybackUri(String appName, int subId, String serviceId) throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public void switchStreams(String appName, int subId, String oldServiceId, String newServiceId)
+    public @Nullable Uri getPlaybackUri(String appName, int subscriptionId, String serviceId)
             throws RemoteException {
-    }
-
-    @Override
-    public int getState(String appName, int subId, String serviceId) throws RemoteException {
-        return 0;
+        return null;
     }
 
     @Override
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f2e1e26..fb6782e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -19,6 +19,8 @@
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Messenger;
 import android.os.ResultReceiver;
 import android.net.Uri;
 import android.service.carrier.CarrierIdentifier;
@@ -29,8 +31,10 @@
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
+import android.telephony.NetworkScanRequest;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyHistogram;
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import com.android.ims.internal.IImsServiceController;
@@ -367,7 +371,7 @@
     /**
      * Report whether data connectivity is possible.
      */
-    boolean isDataConnectivityPossible();
+    boolean isDataConnectivityPossible(int subId);
 
     Bundle getCellLocation(String callingPkg);
 
@@ -794,6 +798,26 @@
     CellNetworkScanResult getCellNetworkScanResults(int subId);
 
     /**
+     * Perform a radio network scan and return the id of this scan.
+     *
+     * @param subId the id of the subscription.
+     * @param request Defines all the configs for network scan.
+     * @param messenger Callback messages will be sent using this messenger.
+     * @param binder the binder object instantiated in TelephonyManager.
+     * @return An id for this scan.
+     */
+    int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,
+            in IBinder binder);
+
+    /**
+     * Stop an existing radio network scan.
+     *
+     * @param subId the id of the subscription.
+     * @param scanId The id of the scan that is going to be stopped.
+     */
+    void stopNetworkScan(int subId, int scanId);
+
+    /**
      * Ask the radio to connect to the input network and change selection mode to manual.
      *
      * @param subId the id of the subscription.
@@ -1267,12 +1291,12 @@
     List<ClientRequestStats> getClientRequestStats(String callingPackage, int subid);
 
     /**
-     * Set SIM card power state. Request is equivalent to inserting or removing the card.
+     * Set SIM card power state.
      * @param slotIndex SIM slot id
-     * @param powerUp True if powering up the SIM, otherwise powering down
+     * @param state  State of SIM (power down, power up, pass through)
      * @hide
      * */
-    void setSimPowerStateForSlot(int slotIndex, boolean powerUp);
+    void setSimPowerStateForSlot(int slotIndex, int state);
 
     /**
      * Returns a list of Forbidden PLMNs from the specified SIM App
@@ -1293,4 +1317,15 @@
      * @hide
      */
     boolean getEmergencyCallbackMode(int subId);
+
+    /**
+     * Get the most recently available signal strength information.
+     *
+     * Get the most recent SignalStrength information reported by the modem. Due
+     * to power saving this information may not always be current.
+     * @param subId Subscription index
+     * @return the most recent cached signal strength info from the modem
+     * @hide
+     */
+    SignalStrength getSignalStrength(int subId);
 }
diff --git a/telephony/java/com/android/internal/telephony/NetworkScanResult.java b/telephony/java/com/android/internal/telephony/NetworkScanResult.java
new file mode 100644
index 0000000..0099961
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/NetworkScanResult.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.CellInfo;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Defines the incremental network scan result.
+ *
+ * This class contains the network scan results. When the user starts a new scan, multiple
+ * NetworkScanResult may be returned, containing either the scan result or error. When the user
+ * stops an ongoing scan, only one NetworkScanResult will be returned to indicate either the scan
+ * is now complete or there is some error stopping it.
+ * @hide
+ */
+public final class NetworkScanResult implements Parcelable {
+
+    // Contains only part of the scan result and more are coming.
+    public static final int SCAN_STATUS_PARTIAL = 0;
+
+    // Contains the last part of the scan result and the scan is now complete.
+    public static final int SCAN_STATUS_COMPLETE = 1;
+
+    // The status of the scan, only valid when scanError = SUCCESS.
+    public int scanStatus;
+
+    /**
+     * The error code of the scan
+     *
+     * This is the error code returned from the RIL, see {@link RILConstants} for more details
+     */
+    public int scanError;
+
+    // The scan results, only valid when scanError = SUCCESS.
+    public List<CellInfo> networkInfos;
+
+    /**
+     * Creates a new NetworkScanResult with scanStatus, scanError and networkInfos
+     *
+     * @param scanStatus The status of the scan.
+     * @param scanError The error code of the scan.
+     * @param networkInfos List of the CellInfo.
+     */
+    public NetworkScanResult(int scanStatus, int scanError, List<CellInfo> networkInfos) {
+        this.scanStatus = scanStatus;
+        this.scanError = scanError;
+        this.networkInfos = networkInfos;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(scanStatus);
+        dest.writeInt(scanError);
+        CellInfo[] ci = networkInfos.toArray(new CellInfo[networkInfos.size()]);
+        dest.writeParcelableArray(ci, flags);
+    }
+
+    private NetworkScanResult(Parcel in) {
+        scanStatus = in.readInt();
+        scanError = in.readInt();
+        CellInfo[] ci = (CellInfo[]) in.readParcelableArray(
+                Object.class.getClassLoader(),
+                CellInfo.class);
+        networkInfos = Arrays.asList(ci);
+    }
+
+    @Override
+    public boolean equals (Object o) {
+        NetworkScanResult nsr;
+
+        try {
+            nsr = (NetworkScanResult) o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return (scanStatus == nsr.scanStatus
+                && scanError == nsr.scanError
+                && networkInfos.equals(nsr.networkInfos));
+    }
+
+    @Override
+    public int hashCode () {
+        return ((scanStatus * 31)
+                + (scanError * 23)
+                + (networkInfos.hashCode() * 37));
+    }
+
+    public static final Creator<NetworkScanResult> CREATOR =
+        new Creator<NetworkScanResult>() {
+            @Override
+            public NetworkScanResult createFromParcel(Parcel in) {
+                return new NetworkScanResult(in);
+            }
+
+            @Override
+            public NetworkScanResult[] newArray(int size) {
+                return new NetworkScanResult[size];
+            }
+        };
+}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 954b17f..e2d25b8 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -415,6 +415,8 @@
     int RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER = 139;
     int RIL_REQUEST_SET_SIM_CARD_POWER = 140;
     int RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION = 141;
+    int RIL_REQUEST_START_NETWORK_SCAN = 142;
+    int RIL_REQUEST_STOP_NETWORK_SCAN = 143;
 
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
 
@@ -466,5 +468,7 @@
     int RIL_UNSOL_STK_CC_ALPHA_NOTIFY = 1044;
     int RIL_UNSOL_LCEDATA_RECV = 1045;
     int RIL_UNSOL_PCO_DATA = 1046;
-    int RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION = 1047;
+    int RIL_UNSOL_MODEM_RESTART = 1047;
+    int RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION = 1048;
+    int RIL_UNSOL_NETWORK_SCAN_RESULT = 1049;
 }
diff --git a/test-runner/src/android/test/AndroidTestRunner.java b/test-runner/src/android/test/AndroidTestRunner.java
index 50eaafb..7313a28 100644
--- a/test-runner/src/android/test/AndroidTestRunner.java
+++ b/test-runner/src/android/test/AndroidTestRunner.java
@@ -20,8 +20,7 @@
 import android.content.Context;
 import android.os.PerformanceCollector.PerformanceResultsWriter;
 
-import com.google.android.collect.Lists;
-
+import java.util.ArrayList;
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestListener;
@@ -48,7 +47,7 @@
     private Context mContext;
     private boolean mSkipExecution = false;
 
-    private List<TestListener> mTestListeners = Lists.newArrayList();
+    private List<TestListener> mTestListeners = new ArrayList<>();
     private Instrumentation mInstrumentation;
     private PerformanceResultsWriter mPerfWriter;
 
@@ -58,7 +57,8 @@
 
         if (shouldRunSingleTestMethod(testMethodName, testClass)) {
             TestCase testCase = buildSingleTestMethod(testClass, testMethodName);
-            mTestCases = Lists.newArrayList(testCase);
+            mTestCases = new ArrayList<>();
+            mTestCases.add(testCase);
             mTestClassName = testClass.getSimpleName();
         } else {
             setTest(getTest(testClass), testClass);
diff --git a/test-runner/src/android/test/ClassPathPackageInfo.java b/test-runner/src/android/test/ClassPathPackageInfo.java
index 1ab7c7f..2cf76af 100644
--- a/test-runner/src/android/test/ClassPathPackageInfo.java
+++ b/test-runner/src/android/test/ClassPathPackageInfo.java
@@ -16,9 +16,8 @@
 
 package android.test;
 
-import com.google.android.collect.Sets;
-
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Set;
 
 /**
@@ -44,7 +43,7 @@
     }
 
     public Set<ClassPathPackageInfo> getSubpackages() {
-        Set<ClassPathPackageInfo> info = Sets.newHashSet();
+        Set<ClassPathPackageInfo> info = new HashSet<>();
         for (String name : subpackageNames) {
             info.add(source.getPackageInfo(name));
         }
@@ -52,7 +51,7 @@
     }
 
     public Set<Class<?>> getTopLevelClassesRecursive() {
-        Set<Class<?>> set = Sets.newHashSet();
+        Set<Class<?>> set = new HashSet<>();
         addTopLevelClassesTo(set);
         return set;
     }
diff --git a/test-runner/src/android/test/ClassPathPackageInfoSource.java b/test-runner/src/android/test/ClassPathPackageInfoSource.java
index 89bb494..9bcc25a 100644
--- a/test-runner/src/android/test/ClassPathPackageInfoSource.java
+++ b/test-runner/src/android/test/ClassPathPackageInfoSource.java
@@ -17,13 +17,13 @@
 package android.test;
 
 import android.util.Log;
-import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
 import dalvik.system.DexFile;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
@@ -57,7 +57,7 @@
     private static String[] apkPaths;
 
     // A cache of jar file contents
-    private final Map<File, Set<String>> jarFiles = Maps.newHashMap();
+    private final Map<File, Set<String>> jarFiles = new HashMap<>();
     private ClassLoader classLoader;
 
     ClassPathPackageInfoSource() {
@@ -76,7 +76,7 @@
     private ClassPathPackageInfo createPackageInfo(String packageName) {
         Set<String> subpackageNames = new TreeSet<String>();
         Set<String> classNames = new TreeSet<String>();
-        Set<Class<?>> topLevelClasses = Sets.newHashSet();
+        Set<Class<?>> topLevelClasses = new HashSet<>();
         findClasses(packageName, classNames, subpackageNames);
         for (String className : classNames) {
             if (className.endsWith(".R") || className.endsWith(".Manifest")) {
@@ -248,7 +248,7 @@
             throws IOException {
         Set<String> entryNames = jarFiles.get(jarFile);
         if (entryNames == null) {
-            entryNames = Sets.newHashSet();
+            entryNames = new HashSet<>();
             ZipFile zipFile = new ZipFile(jarFile);
             Enumeration<? extends ZipEntry> entries = zipFile.entries();
             while (entries.hasMoreElements()) {
diff --git a/test-runner/src/android/test/DatabaseTestUtils.java b/test-runner/src/android/test/DatabaseTestUtils.java
index 42ef48b..1980d92 100644
--- a/test-runner/src/android/test/DatabaseTestUtils.java
+++ b/test-runner/src/android/test/DatabaseTestUtils.java
@@ -16,11 +16,10 @@
 
 package android.test;
 
-import com.google.android.collect.Sets;
-
 import android.database.sqlite.SQLiteDatabase;
 import android.database.Cursor;
 
+import java.util.HashSet;
 import java.util.Set;
 
 /**
@@ -42,7 +41,7 @@
     }
 
     private static Set<String> getSchemaSet(SQLiteDatabase db) {
-        Set<String> schemaSet = Sets.newHashSet();
+        Set<String> schemaSet = new HashSet<>();
 
         Cursor entityCursor = db.rawQuery("SELECT sql FROM sqlite_master", null);
         try {
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index 3abf38f..0b77c00 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -16,8 +16,6 @@
 
 package android.test;
 
-import com.google.android.collect.Lists;
-
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerCallback;
 import android.accounts.AccountManagerFuture;
@@ -38,6 +36,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
 import java.util.List;
 
@@ -55,7 +54,7 @@
     private ContentResolver mResolver;
     private final MockAccountManager mMockAccountManager;
 
-    private List<Intent> mBroadcastIntents = Lists.newArrayList();
+    private List<Intent> mBroadcastIntents = new ArrayList<>();
 
     public IsolatedContext(
             ContentResolver resolver, Context targetContext) {
@@ -67,7 +66,7 @@
     /** Returns the list of intents that were broadcast since the last call to this method. */
     public List<Intent> getAndClearBroadcastIntents() {
         List<Intent> intents = mBroadcastIntents;
-        mBroadcastIntents = Lists.newArrayList();
+        mBroadcastIntents = new ArrayList<>();
         return intents;
     }
 
diff --git a/test-runner/src/android/test/RenamingDelegatingContext.java b/test-runner/src/android/test/RenamingDelegatingContext.java
index 36786b0..fd33321 100644
--- a/test-runner/src/android/test/RenamingDelegatingContext.java
+++ b/test-runner/src/android/test/RenamingDelegatingContext.java
@@ -16,20 +16,24 @@
 
 package android.test;
 
-import com.google.android.collect.Sets;
-
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.ContentProvider;
 import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
-import android.os.FileUtils;
 import android.util.Log;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.EnumSet;
+import java.util.HashSet;
 import java.util.Set;
 
 /**
@@ -48,8 +52,8 @@
     private File mCacheDir;
     private final Object mSync = new Object();
 
-    private Set<String> mDatabaseNames = Sets.newHashSet();
-    private Set<String> mFileNames = Sets.newHashSet();
+    private Set<String> mDatabaseNames = new HashSet<>();
+    private Set<String> mFileNames = new HashSet<>();
 
     public static <T extends ContentProvider> T providerWithRenamedContext(
             Class<T> contentProvider, Context c, String filePrefix)
@@ -237,10 +241,14 @@
                     Log.w("RenamingDelegatingContext", "Unable to create cache directory");
                     return null;
                 }
-                FileUtils.setPermissions(
-                        mCacheDir.getPath(),
-                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
-                        -1, -1);
+                try {
+                    // Give the directory all possible permissions.
+                    Files.setPosixFilePermissions(mCacheDir.toPath(),
+                            EnumSet.allOf(PosixFilePermission.class));
+                } catch (IOException e) {
+                    Log.e("RenamingDelegatingContext",
+                            "Could not set permissions of test cacheDir", e);
+                }
             }
         }
         return mCacheDir;
diff --git a/test-runner/src/android/test/TestCaseUtil.java b/test-runner/src/android/test/TestCaseUtil.java
index c46d403..dc053a2 100644
--- a/test-runner/src/android/test/TestCaseUtil.java
+++ b/test-runner/src/android/test/TestCaseUtil.java
@@ -16,8 +16,7 @@
 
 package android.test;
 
-import com.google.android.collect.Lists;
-
+import java.util.ArrayList;
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
@@ -44,7 +43,7 @@
     @SuppressWarnings("unchecked")
     public static List<String> getTestCaseNames(Test test, boolean flatten) {
         List<Test> tests = (List<Test>) getTests(test, flatten);
-        List<String> testCaseNames = Lists.newArrayList();
+        List<String> testCaseNames = new ArrayList<>();
         for (Test aTest : tests) {
             testCaseNames.add(getTestName(aTest));
         }
@@ -57,7 +56,7 @@
 
     private static List<? extends Test> getTests(Test test, boolean flatten,
             Set<Class<?>> seen) {
-        List<Test> testCases = Lists.newArrayList();
+        List<Test> testCases = new ArrayList<>();
         if (test != null) {
 
             Test workingTest = null;
diff --git a/test-runner/src/android/test/TestRunner.java b/test-runner/src/android/test/TestRunner.java
index beecc6f..ff045c3 100644
--- a/test-runner/src/android/test/TestRunner.java
+++ b/test-runner/src/android/test/TestRunner.java
@@ -32,7 +32,6 @@
 import junit.framework.TestListener;
 import junit.framework.Test;
 import junit.framework.TestResult;
-import com.google.android.collect.Lists;
 
 /**
  * Support class that actually runs a test. Android uses this class,
@@ -54,7 +53,7 @@
 
     private int mMode = REGRESSION;
 
-    private List<Listener> mListeners = Lists.newArrayList();
+    private List<Listener> mListeners = new ArrayList<>();
     private int mPassed;
     private int mFailed;
 
diff --git a/test-runner/src/android/test/mock/MockContentResolver.java b/test-runner/src/android/test/mock/MockContentResolver.java
index d8e0977..a70152c 100644
--- a/test-runner/src/android/test/mock/MockContentResolver.java
+++ b/test-runner/src/android/test/mock/MockContentResolver.java
@@ -23,8 +23,7 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 
-import com.google.android.collect.Maps;
-
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -67,7 +66,7 @@
      */
     public MockContentResolver(Context context) {
         super(context);
-        mProviders = Maps.newHashMap();
+        mProviders = new HashMap<>();
     }
 
     /**
diff --git a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
index 3b920cf..cf6936b 100644
--- a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
+++ b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
@@ -21,7 +21,6 @@
 import android.test.TestCaseUtil;
 import android.util.Log;
 import com.android.internal.util.Predicate;
-import com.google.android.collect.Lists;
 import static android.test.suitebuilder.TestGrouping.SORT_BY_FULLY_QUALIFIED_NAME;
 import static android.test.suitebuilder.TestPredicates.REJECT_SUPPRESSED;
 
@@ -69,7 +68,7 @@
     public TestSuiteBuilder(String name, ClassLoader classLoader) {
         this.suiteName = name;
         this.testGrouping.setClassLoader(classLoader);
-        this.testCases = Lists.newArrayList();
+        this.testCases = new ArrayList<>();
         addRequirements(REJECT_SUPPRESSED);
     }
 
diff --git a/test-runner/tests/src/android/test/AndroidTestRunnerTest.java b/test-runner/tests/src/android/test/AndroidTestRunnerTest.java
index 0574704..6723548 100644
--- a/test-runner/tests/src/android/test/AndroidTestRunnerTest.java
+++ b/test-runner/tests/src/android/test/AndroidTestRunnerTest.java
@@ -19,8 +19,7 @@
 import android.test.mock.MockContext;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.google.android.collect.Lists;
-
+import java.util.ArrayList;
 import junit.framework.TestCase;
 import junit.framework.AssertionFailedError;
 import junit.framework.Test;
@@ -140,7 +139,7 @@
     public void testSetTestClassWithTestSuiteProvider() throws Exception {
         mAndroidTestRunner.setTestClassName(SampleTestSuiteProvider.class.getName(), null);
         List<TestCase> testCases = mAndroidTestRunner.getTestCases();
-        List<String> testNames = Lists.newArrayList();
+        List<String> testNames = new ArrayList<>();
         for (TestCase testCase : testCases) {
             testNames.add(testCase.getName());
         }
@@ -152,7 +151,7 @@
     public void testSetTestClassWithTestSuite() throws Exception {
         mAndroidTestRunner.setTestClassName(SampleTestSuite.class.getName(), null);
         List<TestCase> testCases = mAndroidTestRunner.getTestCases();
-        List<String> testNames = Lists.newArrayList();
+        List<String> testNames = new ArrayList<>();
         for (TestCase testCase : testCases) {
             testNames.add(testCase.getName());
         }
@@ -163,7 +162,7 @@
         String testMethodName = "testTwo";
         mAndroidTestRunner.setTestClassName(TwoTestTestCase.class.getName(), testMethodName);
         List<TestCase> testCases = mAndroidTestRunner.getTestCases();
-        List<String> testNames = Lists.newArrayList();
+        List<String> testNames = new ArrayList<>();
         for (TestCase testCase : testCases) {
             testNames.add(testCase.getName());
         }
@@ -255,7 +254,7 @@
     }
 
     private static class TestListenerStub implements TestListener {
-        List<String> testNames = Lists.newArrayList();
+        List<String> testNames = new ArrayList<>();
 
         public void addError(Test test, Throwable t) {
         }
diff --git a/tests/net/java/android/net/util/SharedLogTest.java b/tests/net/java/android/net/util/SharedLogTest.java
new file mode 100644
index 0000000..7fd7a63
--- /dev/null
+++ b/tests/net/java/android/net/util/SharedLogTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Vector;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SharedLogTest {
+    private static final String TIMESTAMP_PATTERN =
+            "^[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9]";
+    private static final String TIMESTAMP = "mm-dd HH:MM:SS.xxx";
+
+    @Test
+    public void testBasicOperation() {
+        final SharedLog logTop = new SharedLog("top");
+        logTop.mark("first post!");
+
+        final SharedLog logLevel2a = logTop.forSubComponent("twoA");
+        final SharedLog logLevel2b = logTop.forSubComponent("twoB");
+        logLevel2b.e("2b or not 2b");
+        logLevel2a.w("second post?");
+
+        final SharedLog logLevel3 = logLevel2a.forSubComponent("three");
+        logTop.log("still logging");
+        logLevel3.log("3 >> 2");
+        logLevel2a.mark("ok: last post");
+
+        final String[] expected = {
+            TIMESTAMP + " - MARK first post!",
+            TIMESTAMP + " - [twoB] ERROR 2b or not 2b",
+            TIMESTAMP + " - [twoA] WARN second post?",
+            TIMESTAMP + " - still logging",
+            TIMESTAMP + " - [twoA.three] 3 >> 2",
+            TIMESTAMP + " - [twoA] MARK ok: last post",
+        };
+        // Verify the logs are all there and in the correct order.
+        verifyLogLines(expected, logTop);
+
+        // In fact, because they all share the same underlying LocalLog,
+        // every subcomponent SharedLog's dump() is identical.
+        verifyLogLines(expected, logLevel2a);
+        verifyLogLines(expected, logLevel2b);
+        verifyLogLines(expected, logLevel3);
+    }
+
+    private static void verifyLogLines(String[] expected, SharedLog log) {
+        final ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+        final PrintWriter pw = new PrintWriter(ostream, true);
+        log.dump(null, pw, null);
+
+        final String dumpOutput = ostream.toString();
+        assertTrue(dumpOutput != null);
+        assertTrue(!"".equals(dumpOutput));
+
+        final String[] lines = dumpOutput.split("\n");
+        assertEquals(expected.length, lines.length);
+
+        for (int i = 0; i < lines.length; i++) {
+            // Fix up the timestamps.
+            lines[i] = lines[i].replaceAll(TIMESTAMP_PATTERN, TIMESTAMP);
+        }
+
+        for (int i = 0; i < expected.length; i++) {
+            assertEquals(expected[i], lines[i]);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/connectivity/NetworkNotificationManagerTest.java
rename to tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 21c2de7..f201bc7 100644
--- a/services/tests/servicestests/src/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -91,7 +91,7 @@
         final int NETWORK_ID_BASE = 100;
         List<NotificationType> types = Arrays.asList(NotificationType.values());
         List<Integer> ids = new ArrayList<>(types.size());
-        for (int i = 0; i < ids.size(); i++) {
+        for (int i = 0; i < types.size(); i++) {
             ids.add(NETWORK_ID_BASE + i);
         }
         Collections.shuffle(ids);
@@ -101,9 +101,10 @@
             mManager.showNotification(ids.get(i), types.get(i), mWifiNai, mCellNai, null, false);
         }
 
-        Collections.shuffle(ids);
+        List<Integer> idsToClear = new ArrayList<>(ids);
+        Collections.shuffle(idsToClear);
         for (int i = 0; i < ids.size(); i++) {
-            mManager.clearNotification(ids.get(i));
+            mManager.clearNotification(idsToClear.get(i));
         }
 
         for (int i = 0; i < ids.size(); i++) {
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 3172c6e..bc89c0f 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -56,6 +56,8 @@
 import android.telephony.CarrierConfigManager;
 
 import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.server.connectivity.tethering.OffloadHardwareInterface;
+import com.android.server.connectivity.tethering.TetheringDependencies;
 
 import org.junit.After;
 import org.junit.Before;
@@ -78,7 +80,9 @@
     @Mock private INetworkStatsService mStatsService;
     @Mock private INetworkPolicyManager mPolicyManager;
     @Mock private MockableSystemProperties mSystemProperties;
+    @Mock private OffloadHardwareInterface mOffloadHardwareInterface;
     @Mock private Resources mResources;
+    @Mock private TetheringDependencies mTetheringDependencies;
     @Mock private UsbManager mUsbManager;
     @Mock private WifiManager mWifiManager;
     @Mock private CarrierConfigManager mCarrierConfigManager;
@@ -138,8 +142,11 @@
         };
         mServiceContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+        when(mTetheringDependencies.getOffloadHardwareInterface())
+                .thenReturn(mOffloadHardwareInterface);
         mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
-                                   mLooper.getLooper(), mSystemProperties);
+                                   mLooper.getLooper(), mSystemProperties,
+                                   mTetheringDependencies);
     }
 
     @After
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index a3f33dc..27e683c 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -38,6 +38,7 @@
 import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
+import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
@@ -63,12 +64,14 @@
     @Mock private IControlsTethering mTetherHelper;
     @Mock private InterfaceConfiguration mInterfaceConfiguration;
     @Mock private IPv6TetheringInterfaceServices mIPv6TetheringInterfaceServices;
+    @Mock private SharedLog mSharedLog;
 
     private final TestLooper mLooper = new TestLooper();
     private TetherInterfaceStateMachine mTestedSm;
 
     private void initStateMachine(int interfaceType) throws Exception {
-        mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), interfaceType,
+        mTestedSm = new TetherInterfaceStateMachine(
+                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
                 mNMService, mStatsService, mTetherHelper, mIPv6TetheringInterfaceServices);
         mTestedSm.start();
         // Starting the state machine always puts us in a consistent state and notifies
@@ -90,12 +93,13 @@
 
     @Before public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
     }
 
     @Test
     public void startsOutAvailable() {
         mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
-                TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper,
+                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
                 mIPv6TetheringInterfaceServices);
         mTestedSm.start();
         mLooper.dispatchAll();
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
new file mode 100644
index 0000000..9fcd1b5
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_NOT_REQUIRED;
+import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_REQUIRED;
+import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_UNSPECIFIED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.test.BroadcastInterceptingContext;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetheringConfigurationTest {
+    @Mock private Context mContext;
+    @Mock private TelephonyManager mTelephonyManager;
+    @Mock private Resources mResources;
+    private Context mMockContext;
+    private boolean mHasTelephonyManager;
+
+    private class MockContext extends BroadcastInterceptingContext {
+        MockContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public Resources getResources() { return mResources; }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (Context.TELEPHONY_SERVICE.equals(name)) {
+                return mHasTelephonyManager ? mTelephonyManager : null;
+            }
+            return super.getSystemService(name);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
+                .thenReturn(new String[0]);
+        when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
+                .thenReturn(new String[0]);
+        when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
+                .thenReturn(new String[]{ "test_wlan\\d" });
+        when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
+                .thenReturn(new String[0]);
+        mMockContext = new MockContext(mContext);
+    }
+
+    @Test
+    public void testDunFromTelephonyManagerMeansDun() {
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[]{TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI});
+        mHasTelephonyManager = true;
+        when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_REQUIRED);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext);
+        assertTrue(cfg.isDunRequired);
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
+        assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
+        assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
+        // Just to prove we haven't clobbered Wi-Fi:
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
+    }
+
+    @Test
+    public void testDunNotRequiredFromTelephonyManagerMeansNoDun() {
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[]{TYPE_MOBILE_DUN, TYPE_WIFI});
+        mHasTelephonyManager = true;
+        when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_NOT_REQUIRED);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext);
+        assertFalse(cfg.isDunRequired);
+        assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI));
+        // Just to prove we haven't clobbered Wi-Fi:
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
+    }
+
+    @Test
+    public void testDunFromUpstreamConfigMeansDun() {
+        when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
+                .thenReturn(new int[]{TYPE_MOBILE_DUN, TYPE_WIFI});
+        mHasTelephonyManager = false;
+        when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
+
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext);
+        assertTrue(cfg.isDunRequired);
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
+        // Just to prove we haven't clobbered Wi-Fi:
+        assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_WIFI));
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index c72efb0..9bb392a 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -25,11 +25,13 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.os.Handler;
@@ -40,6 +42,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.net.util.SharedLog;
 
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -69,6 +72,7 @@
 
     @Mock private Context mContext;
     @Mock private IConnectivityManager mCS;
+    @Mock private SharedLog mLog;
 
     private TestStateMachine mSM;
     private TestConnectivityManager mCM;
@@ -78,10 +82,12 @@
         MockitoAnnotations.initMocks(this);
         reset(mContext);
         reset(mCS);
+        reset(mLog);
+        when(mLog.forSubComponent(anyString())).thenReturn(mLog);
 
         mCM = spy(new TestConnectivityManager(mContext, mCS));
         mSM = new TestStateMachine();
-        mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM);
+        mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM, mLog);
     }
 
     @After public void tearDown() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java
rename to tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
index bb8f9d1..e6b1c6d 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
@@ -25,6 +25,7 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.support.test.filters.SmallTest;
 import android.telephony.TelephonyManager;
 
 import com.android.server.LocalServices;
@@ -34,6 +35,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+@SmallTest
 public class NetworkStatsAccessTest extends TestCase {
     private static final String TEST_PKG = "com.example.test";
     private static final int TEST_UID = 12345;
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
rename to tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 9f53c87..2a32b73 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -31,11 +31,11 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
+import android.support.test.filters.SmallTest;
 import android.test.AndroidTestCase;
 import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.MediumTest;
 
-import com.android.frameworks.servicestests.R;
+import com.android.frameworks.tests.net.R;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -51,7 +51,7 @@
 /**
  * Tests for {@link NetworkStatsCollection}.
  */
-@MediumTest
+@SmallTest
 public class NetworkStatsCollectionTest extends AndroidTestCase {
 
     private static final String TEST_FILE = "test.bin";
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
rename to tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index 5eee7b9..fe7840d 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -38,16 +38,16 @@
 import android.net.NetworkIdentity;
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
+import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.Process;
-
-import android.os.ConditionVariable;
 import android.os.Looper;
-import android.os.Messenger;
 import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
 import android.os.UserHandle;
+import android.support.test.filters.SmallTest;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 
@@ -69,6 +69,7 @@
 /**
  * Tests for {@link NetworkStatsObservers}.
  */
+@SmallTest
 public class NetworkStatsObserversTest extends TestCase {
     private static final String TEST_IFACE = "test0";
     private static final String TEST_IFACE2 = "test1";
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
rename to tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 728eb73..029693f 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -85,6 +85,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
@@ -119,6 +120,7 @@
  * still uses the Easymock structure, which could be simplified.
  */
 @RunWith(AndroidJUnit4.class)
+@SmallTest
 public class NetworkStatsServiceTest {
     private static final String TAG = "NetworkStatsServiceTest";
 
diff --git a/services/tests/servicestests/res/raw/netstats_uid_v4 b/tests/net/res/raw/netstats_uid_v4
similarity index 100%
rename from services/tests/servicestests/res/raw/netstats_uid_v4
rename to tests/net/res/raw/netstats_uid_v4
Binary files differ
diff --git a/services/tests/servicestests/res/raw/netstats_v1 b/tests/net/res/raw/netstats_v1
similarity index 100%
rename from services/tests/servicestests/res/raw/netstats_v1
rename to tests/net/res/raw/netstats_v1
Binary files differ