Revert "Move webrtc/{base => rtc_base}" (https://codereview.webrtc.org/2877023002)

Will reland in two different commits to preserve git blame history.

BUG=webrtc:7634
NOTRY=True
TBR=kwiberg@webrtc.org

Change-Id: I550da8525aeb9c5b8f96338fcf1c9714f3dcdab1
Reviewed-on: https://chromium-review.googlesource.com/554610
Reviewed-by: Henrik Kjellander <kjellander@webrtc.org>
Cr-Original-Commit-Position: refs/heads/master@{#18820}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: ec78f1cebcbf1181d93450bd9c91efe8f6a7688d
diff --git a/base/BUILD.gn b/base/BUILD.gn
index c786f15..1eecc86 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -26,6 +26,39 @@
     ":sequenced_task_checker",
     ":weak_ptr",
   ]
+  if (is_android) {
+    public_deps += [ ":base_java" ]
+  }
+}
+
+config("rtc_base_approved_all_dependent_config") {
+  if (is_mac && !build_with_chromium) {
+    libs = [ "Foundation.framework" ]  # needed for logging_mac.mm
+  }
+}
+
+config("rtc_base_chromium_config") {
+  defines = [ "NO_MAIN_THREAD_WRAPPING" ]
+}
+
+config("rtc_base_all_dependent_config") {
+  if (is_ios) {
+    libs = [
+      "CFNetwork.framework",
+      "Security.framework",
+      "SystemConfiguration.framework",
+      "UIKit.framework",
+    ]
+  }
+  if (is_mac) {
+    libs = [
+      "Cocoa.framework",
+      "Foundation.framework",
+      "IOKit.framework",
+      "Security.framework",
+      "SystemConfiguration.framework",
+    ]
+  }
 }
 
 if (!rtc_build_ssl) {
@@ -36,100 +69,965 @@
   }
 }
 
-# The targets below are deprecated and only exist here temporarily during
-# refactoring. See https://bugs.webrtc.org/7634 for more details.
-
-group("protobuf_utils") {
-  public_deps = [ "../rtc_base:protobuf_utils" ]
+source_set("protobuf_utils") {
+  sources = [
+    "protobuf_utils.h",
+  ]
+  if (rtc_enable_protobuf) {
+    public_deps = [
+      "//third_party/protobuf:protobuf_lite",
+    ]
+  }
 }
 
-group("compile_assert_c") {
-  public_deps = [ "../rtc_base:compile_assert_c" ]
+source_set("compile_assert_c") {
+  sources = [
+    "compile_assert_c.h",
+  ]
 }
 
-group("rtc_base_approved") {
-  public_deps = [ "../rtc_base:rtc_base_approved" ]
+# The subset of rtc_base approved for use outside of libjingle.
+rtc_static_library("rtc_base_approved") {
+  # TODO(kjellander): Remove (bugs.webrtc.org/7480)
+  # Enabling GN check triggers a cyclic dependency caused by rate_limiter.cc:
+  #   :rtc_base_approved -> //webrtc/system_wrappers -> :rtc_base_approved
+  check_includes = false
+  defines = []
+  libs = []
+  deps = []
+  all_dependent_configs = [ ":rtc_base_approved_all_dependent_config" ]
+
+  sources = [
+    "array_view.h",
+    "arraysize.h",
+    "atomicops.h",
+    "base64.cc",
+    "base64.h",
+    "basictypes.h",
+    "bind.h",
+    "bitbuffer.cc",
+    "bitbuffer.h",
+    "buffer.h",
+    "bufferqueue.cc",
+    "bufferqueue.h",
+    "bytebuffer.cc",
+    "bytebuffer.h",
+    "byteorder.h",
+    "checks.cc",
+    "checks.h",
+    "constructormagic.h",
+    "copyonwritebuffer.cc",
+    "copyonwritebuffer.h",
+    "criticalsection.cc",
+    "criticalsection.h",
+    "deprecation.h",
+    "event.cc",
+    "event.h",
+    "event_tracer.cc",
+    "event_tracer.h",
+    "file.cc",
+    "file.h",
+    "flags.cc",
+    "flags.h",
+    "format_macros.h",
+    "function_view.h",
+    "ignore_wundef.h",
+    "location.cc",
+    "location.h",
+    "mod_ops.h",
+    "onetimeevent.h",
+    "optional.cc",
+    "optional.h",
+    "pathutils.cc",
+    "pathutils.h",
+    "platform_file.cc",
+    "platform_file.h",
+    "platform_thread.cc",
+    "platform_thread.h",
+    "platform_thread_types.h",
+    "ptr_util.h",
+    "race_checker.cc",
+    "race_checker.h",
+    "random.cc",
+    "random.h",
+    "rate_limiter.cc",
+    "rate_limiter.h",
+    "rate_statistics.cc",
+    "rate_statistics.h",
+    "ratetracker.cc",
+    "ratetracker.h",
+    "refcount.h",
+    "refcountedobject.h",
+    "safe_compare.h",
+    "safe_conversions.h",
+    "safe_conversions_impl.h",
+    "safe_minmax.h",
+    "sanitizer.h",
+    "scoped_ref_ptr.h",
+    "string_to_number.cc",
+    "string_to_number.h",
+    "stringencode.cc",
+    "stringencode.h",
+    "stringize_macros.h",
+    "stringutils.cc",
+    "stringutils.h",
+    "swap_queue.h",
+    "template_util.h",
+    "thread_annotations.h",
+    "thread_checker.h",
+    "thread_checker_impl.cc",
+    "thread_checker_impl.h",
+    "timestampaligner.cc",
+    "timestampaligner.h",
+    "timeutils.cc",
+    "timeutils.h",
+    "trace_event.h",
+    "type_traits.h",
+  ]
+
+  deps += [ "..:webrtc_common" ]
+
+  if (is_android) {
+    libs += [ "log" ]
+  }
+
+  if (is_posix) {
+    sources += [ "file_posix.cc" ]
+  }
+
+  if (is_win) {
+    sources += [ "file_win.cc" ]
+  }
+
+  if (build_with_chromium) {
+    # Dependency on chromium's logging (in //base).
+    deps += [ "//base:base" ]
+    sources += [
+      "../../webrtc_overrides/webrtc/base/logging.cc",
+      "../../webrtc_overrides/webrtc/base/logging.h",
+    ]
+  } else {
+    sources += [
+      "logging.cc",
+      "logging.h",
+      "logging_mac.mm",
+    ]
+  }
+  if (is_component_build && is_win) {
+    # Copy the VS runtime DLLs into the isolate so that they don't have to be
+    # preinstalled on the target machine. The debug runtimes have a "d" at
+    # the end.
+    # This is a copy of https://codereview.chromium.org/1783973002.
+    # TODO(ehmaldonado): We'd like Chromium to make this changes easier to use,
+    # so we don't have to copy their changes and risk breakages.
+    # See http://crbug.com/653569
+    if (is_debug) {
+      vcrt_suffix = "d"
+    } else {
+      vcrt_suffix = ""
+    }
+
+    # These runtime files are copied to the output directory by the
+    # vs_toolchain script that runs as part of toolchain configuration.
+    data = [
+      "$root_out_dir/msvcp140${vcrt_suffix}.dll",
+      "$root_out_dir/vccorlib140${vcrt_suffix}.dll",
+      "$root_out_dir/vcruntime140${vcrt_suffix}.dll",
+
+      # Universal Windows 10 CRT files
+      "$root_out_dir/api-ms-win-core-console-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-datetime-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-debug-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-errorhandling-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-file-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-file-l1-2-0.dll",
+      "$root_out_dir/api-ms-win-core-file-l2-1-0.dll",
+      "$root_out_dir/api-ms-win-core-handle-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-heap-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-interlocked-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-libraryloader-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-localization-l1-2-0.dll",
+      "$root_out_dir/api-ms-win-core-memory-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-namedpipe-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-processenvironment-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-processthreads-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-processthreads-l1-1-1.dll",
+      "$root_out_dir/api-ms-win-core-profile-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-rtlsupport-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-string-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-synch-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-synch-l1-2-0.dll",
+      "$root_out_dir/api-ms-win-core-sysinfo-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-timezone-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-core-util-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-conio-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-convert-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-environment-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-filesystem-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-heap-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-locale-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-math-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-multibyte-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-private-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-process-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-runtime-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-stdio-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-string-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-time-l1-1-0.dll",
+      "$root_out_dir/api-ms-win-crt-utility-l1-1-0.dll",
+      "$root_out_dir/ucrtbase${vcrt_suffix}.dll",
+    ]
+    if (is_asan) {
+      if (current_cpu == "x64") {
+        data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-x86_64.dll" ]
+      } else {
+        data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-i386.dll" ]
+      }
+    }
+  }
+  if (is_nacl) {
+    deps += [ "//native_client_sdk/src/libraries/nacl_io" ]
+  }
 }
 
-group("rtc_task_queue") {
-  public_deps = [ "../rtc_base:rtc_task_queue" ]
+config("enable_libevent_config") {
+  defines = [ "WEBRTC_BUILD_LIBEVENT" ]
 }
 
-group("sequenced_task_checker") {
-  public_deps = [ "../rtc_base:sequenced_task_checker" ]
+rtc_static_library("rtc_task_queue") {
+  public_deps = [
+    ":rtc_base_approved",
+  ]
+
+  if (build_with_chromium) {
+    sources = [
+      "../../webrtc_overrides/webrtc/base/task_queue.cc",
+      "../../webrtc_overrides/webrtc/base/task_queue.h",
+    ]
+  } else {
+    sources = [
+      "task_queue.h",
+      "task_queue_posix.h",
+    ]
+    if (rtc_build_libevent) {
+      deps = [
+        "//base/third_party/libevent",
+      ]
+    }
+
+    if (rtc_enable_libevent) {
+      sources += [
+        "task_queue_libevent.cc",
+        "task_queue_posix.cc",
+      ]
+      all_dependent_configs = [ ":enable_libevent_config" ]
+    } else {
+      if (is_mac || is_ios) {
+        sources += [
+          "task_queue_gcd.cc",
+          "task_queue_posix.cc",
+        ]
+      }
+      if (is_win) {
+        sources += [ "task_queue_win.cc" ]
+      }
+    }
+  }
 }
 
-group("weak_ptr") {
-  public_deps = [ "../rtc_base:weak_ptr" ]
+rtc_static_library("sequenced_task_checker") {
+  sources = [
+    "sequenced_task_checker.h",
+    "sequenced_task_checker_impl.cc",
+    "sequenced_task_checker_impl.h",
+  ]
+  deps = [
+    ":rtc_task_queue",
+  ]
 }
 
-group("rtc_numerics") {
-  public_deps = [ "../rtc_base:rtc_numerics" ]
+rtc_static_library("weak_ptr") {
+  sources = [
+    "weak_ptr.cc",
+    "weak_ptr.h",
+  ]
+  deps = [
+    ":rtc_base_approved",
+    ":sequenced_task_checker",
+  ]
 }
 
-group("rtc_json") {
-  public_deps = [ "../rtc_base:rtc_json" ]
+rtc_static_library("rtc_numerics") {
+  sources = [
+    "numerics/exp_filter.cc",
+    "numerics/exp_filter.h",
+    "numerics/percentile_filter.h",
+  ]
+  deps = [
+    ":rtc_base_approved",
+  ]
 }
 
-group("rtc_base") {
-  public_deps = [ "../rtc_base:rtc_base" ]
+config("rtc_base_warnings_config") {
+  if (is_win && is_clang) {
+    cflags = [
+      # Disable warnings failing when compiling with Clang on Windows.
+      # https://bugs.chromium.org/p/webrtc/issues/detail?id=5366
+      "-Wno-sign-compare",
+      "-Wno-missing-braces",
+    ]
+  }
 }
 
-group("gtest_prod") {
-  public_deps = [ "../rtc_base:gtest_prod" ]
+rtc_source_set("rtc_json") {
+  defines = []
+  sources = [
+    "json.cc",
+    "json.h",
+  ]
+  if (rtc_build_json) {
+    public_deps = [
+      "//third_party/jsoncpp",
+    ]
+  } else {
+    include_dirs = [ "$rtc_jsoncpp_root" ]
+
+    # When defined changes the include path for json.h to where it is
+    # expected to be when building json outside of the standalone build.
+    defines += [ "WEBRTC_EXTERNAL_JSON" ]
+  }
 }
 
-group("rtc_base_tests_utils") {
+rtc_static_library("rtc_base") {
+  cflags = []
+  cflags_cc = []
+  libs = []
+  defines = []
+  deps = [
+    "..:webrtc_common",
+  ]
+  public_deps = [
+    ":rtc_base_approved",
+  ]
+  public_configs = []
+
+  all_dependent_configs = [ ":rtc_base_all_dependent_config" ]
+
+  sources = [
+    "applefilesystem.mm",
+    "asyncinvoker-inl.h",
+    "asyncinvoker.cc",
+    "asyncinvoker.h",
+    "asyncpacketsocket.cc",
+    "asyncpacketsocket.h",
+    "asyncresolverinterface.cc",
+    "asyncresolverinterface.h",
+    "asyncsocket.cc",
+    "asyncsocket.h",
+    "asynctcpsocket.cc",
+    "asynctcpsocket.h",
+    "asyncudpsocket.cc",
+    "asyncudpsocket.h",
+    "crc32.cc",
+    "crc32.h",
+    "cryptstring.cc",
+    "cryptstring.h",
+    "filerotatingstream.cc",
+    "filerotatingstream.h",
+    "fileutils.cc",
+    "fileutils.h",
+    "gunit_prod.h",
+    "helpers.cc",
+    "helpers.h",
+    "httpbase.cc",
+    "httpbase.h",
+    "httpcommon-inl.h",
+    "httpcommon.cc",
+    "httpcommon.h",
+    "ipaddress.cc",
+    "ipaddress.h",
+    "messagedigest.cc",
+    "messagedigest.h",
+    "messagehandler.cc",
+    "messagehandler.h",
+    "messagequeue.cc",
+    "messagequeue.h",
+    "nethelpers.cc",
+    "nethelpers.h",
+    "network.cc",
+    "network.h",
+    "networkmonitor.cc",
+    "networkmonitor.h",
+    "nullsocketserver.cc",
+    "nullsocketserver.h",
+    "openssl.h",
+    "openssladapter.cc",
+    "openssladapter.h",
+    "openssldigest.cc",
+    "openssldigest.h",
+    "opensslidentity.cc",
+    "opensslidentity.h",
+    "opensslstreamadapter.cc",
+    "opensslstreamadapter.h",
+    "physicalsocketserver.cc",
+    "physicalsocketserver.h",
+    "proxyinfo.cc",
+    "proxyinfo.h",
+    "ratelimiter.cc",
+    "ratelimiter.h",
+    "rtccertificate.cc",
+    "rtccertificate.h",
+    "rtccertificategenerator.cc",
+    "rtccertificategenerator.h",
+    "signalthread.cc",
+    "signalthread.h",
+    "sigslot.cc",
+    "sigslot.h",
+    "socket.h",
+    "socketadapters.cc",
+    "socketadapters.h",
+    "socketaddress.cc",
+    "socketaddress.h",
+    "socketaddresspair.cc",
+    "socketaddresspair.h",
+    "socketfactory.h",
+    "socketserver.h",
+    "socketstream.cc",
+    "socketstream.h",
+    "ssladapter.cc",
+    "ssladapter.h",
+    "sslfingerprint.cc",
+    "sslfingerprint.h",
+    "sslidentity.cc",
+    "sslidentity.h",
+    "sslstreamadapter.cc",
+    "sslstreamadapter.h",
+    "stream.cc",
+    "stream.h",
+    "thread.cc",
+    "thread.h",
+  ]
+
+  # TODO(henrike): issue 3307, make rtc_base build with the Chromium default
+  # compiler settings.
+  suppressed_configs += [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+  if (!is_win) {
+    cflags += [ "-Wno-uninitialized" ]
+  }
+
+  if (build_with_chromium) {
+    if (is_win) {
+      sources += [ "../../webrtc_overrides/webrtc/base/win32socketinit.cc" ]
+    }
+    include_dirs = [ "../../boringssl/src/include" ]
+    public_configs += [ ":rtc_base_chromium_config" ]
+  } else {
+    configs += [ ":rtc_base_warnings_config" ]
+    sources += [
+      "callback.h",
+      "logsinks.cc",
+      "logsinks.h",
+      "mathutils.h",
+      "optionsfile.cc",
+      "optionsfile.h",
+      "rollingaccumulator.h",
+      "sslroots.h",
+      "transformadapter.cc",
+      "transformadapter.h",
+      "window.h",
+    ]
+
+    if (is_win) {
+      sources += [
+        "win32socketinit.cc",
+        "win32socketinit.h",
+        "win32socketserver.cc",
+        "win32socketserver.h",
+      ]
+    }
+  }  # !build_with_chromium
+
+  if (rtc_build_ssl) {
+    deps += [ "//third_party/boringssl" ]
+  } else {
+    configs += [ ":external_ssl_library" ]
+  }
+
+  if (is_android) {
+    sources += [
+      "ifaddrs-android.cc",
+      "ifaddrs-android.h",
+    ]
+
+    libs += [
+      "log",
+      "GLESv2",
+    ]
+  }
+
+  if (is_ios || is_mac) {
+    sources += [
+      "macifaddrs_converter.cc",
+      "thread_darwin.mm",
+    ]
+  }
+
+  if (use_x11) {
+    libs += [
+      "dl",
+      "rt",
+      "Xext",
+      "X11",
+      "Xcomposite",
+      "Xrender",
+    ]
+  }
+
+  if (is_linux) {
+    libs += [
+      "dl",
+      "rt",
+    ]
+  }
+
+  if (is_mac) {
+    sources += [
+      "macutils.cc",
+      "macutils.h",
+    ]
+    libs += [
+      # For ProcessInformationCopyDictionary in unixfilesystem.cc.
+      "ApplicationServices.framework",
+    ]
+  }
+
+  if (is_win) {
+    sources += [
+      "win32.cc",
+      "win32.h",
+      "win32filesystem.cc",
+      "win32filesystem.h",
+      "win32securityerrors.cc",
+      "win32window.cc",
+      "win32window.h",
+    ]
+
+    libs += [
+      "crypt32.lib",
+      "iphlpapi.lib",
+      "secur32.lib",
+    ]
+
+    cflags += [
+      # Suppress warnings about WIN32_LEAN_AND_MEAN.
+      "/wd4005",
+      "/wd4703",
+    ]
+
+    defines += [ "_CRT_NONSTDC_NO_DEPRECATE" ]
+  }
+
+  if (is_posix) {
+    sources += [
+      "ifaddrs_converter.cc",
+      "ifaddrs_converter.h",
+      "unixfilesystem.cc",
+      "unixfilesystem.h",
+    ]
+  }
+
+  if (is_nacl) {
+    deps += [ "//native_client_sdk/src/libraries/nacl_io" ]
+    defines += [ "timezone=_timezone" ]
+    sources -= [ "ifaddrs_converter.cc" ]
+  }
+  if (is_win && is_clang) {
+    # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+    suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+  }
+}
+
+rtc_source_set("gtest_prod") {
+  sources = [
+    "gtest_prod_util.h",
+  ]
+}
+
+config("rtc_base_tests_utils_exported_config") {
+  defines = [ "GTEST_RELATIVE_PATH" ]
+}
+
+config("rtc_base_tests_utils_warnings_config") {
+  if (is_win && is_clang) {
+    cflags = [
+      # See https://bugs.chromium.org/p/webrtc/issues/detail?id=6270
+      "-Wno-reorder",
+      "-Wno-sign-compare",
+    ]
+  }
+}
+
+rtc_source_set("rtc_base_tests_utils") {
   testonly = true
-  public_deps = [ "../rtc_base:rtc_base_tests_utils" ]
+  sources = [
+    # Also use this as a convenient dumping ground for misc files that are
+    # included by multiple targets below.
+    "cpu_time.cc",
+    "cpu_time.h",
+    "fakeclock.cc",
+    "fakeclock.h",
+    "fakenetwork.h",
+    "fakesslidentity.h",
+    "firewallsocketserver.cc",
+    "firewallsocketserver.h",
+    "gunit.h",
+    "httpserver.cc",
+    "httpserver.h",
+    "md5.cc",
+    "md5.h",
+    "md5digest.cc",
+    "md5digest.h",
+    "memory_usage.cc",
+    "memory_usage.h",
+    "natserver.cc",
+    "natserver.h",
+    "natsocketfactory.cc",
+    "natsocketfactory.h",
+    "nattypes.cc",
+    "nattypes.h",
+    "proxyserver.cc",
+    "proxyserver.h",
+    "sha1.cc",
+    "sha1.h",
+    "sha1digest.cc",
+    "sha1digest.h",
+    "sigslottester.h",
+    "sigslottester.h.pump",
+    "testbase64.h",
+    "testclient.cc",
+    "testclient.h",
+    "testechoserver.h",
+    "testutils.h",
+    "timedelta.h",
+    "virtualsocketserver.cc",
+    "virtualsocketserver.h",
+  ]
+  configs += [ ":rtc_base_tests_utils_warnings_config" ]
+  public_configs = [ ":rtc_base_tests_utils_exported_config" ]
+  deps = [
+    ":rtc_base",
+    "../test:field_trial",
+    "../test:test_support",
+  ]
+  public_deps = [
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+
+  if (!build_with_chromium && is_clang) {
+    # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+    suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+  }
 }
 
 if (rtc_include_tests) {
-  group("rtc_base_tests_main") {
+  rtc_source_set("rtc_base_tests_main") {
     testonly = true
-    public_deps = [ "../rtc_base:rtc_base_tests_main" ]
+    sources = [
+      "unittest_main.cc",
+    ]
+    public_configs = [ ":rtc_base_tests_utils_exported_config" ]
+    deps = [
+      ":rtc_base",
+      ":rtc_base_approved",
+      ":rtc_base_tests_utils",
+      "../test:field_trial",
+      "../test:test_support",
+    ]
+
+    public_deps = [
+      "//testing/gmock",
+      "//testing/gtest",
+    ]
+
+    if (!build_with_chromium && is_clang) {
+      # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+      suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+    }
   }
 
-  group("rtc_base_nonparallel_tests") {
+  rtc_source_set("rtc_base_nonparallel_tests") {
     testonly = true
-    public_deps = [ "../rtc_base:rtc_base_nonparallel_tests" ]
+
+    # Skip restricting visibility on mobile platforms since the tests on those
+    # gets additional generated targets which would require many lines here to
+    # cover (which would be confusing to read and hard to maintain).
+    if (!is_android && !is_ios) {
+      visibility = [ "//webrtc:webrtc_nonparallel_tests" ]
+    }
+    sources = [
+      "cpu_time_unittest.cc",
+      "filerotatingstream_unittest.cc",
+      "nullsocketserver_unittest.cc",
+      "physicalsocketserver_unittest.cc",
+      "socket_unittest.cc",
+      "socket_unittest.h",
+      "socketaddress_unittest.cc",
+    ]
+    deps = [
+      ":rtc_base",
+      ":rtc_base_tests_main",
+      ":rtc_base_tests_utils",
+      "../system_wrappers:system_wrappers",
+      "../test:test_support",
+      "//testing/gtest",
+    ]
+    if (is_win) {
+      sources += [ "win32socketserver_unittest.cc" ]
+    }
+
+    if (!build_with_chromium && is_clang) {
+      # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+      suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+    }
   }
 
-  group("rtc_base_approved_unittests") {
+  rtc_source_set("rtc_base_approved_unittests") {
     testonly = true
-    public_deps = [ "../rtc_base:rtc_base_approved_unittests" ]
+
+    # Skip restricting visibility on mobile platforms since the tests on those
+    # gets additional generated targets which would require many lines here to
+    # cover (which would be confusing to read and hard to maintain).
+    if (!is_android && !is_ios) {
+      visibility = [ "//webrtc:rtc_unittests" ]
+    }
+    sources = [
+      "array_view_unittest.cc",
+      "atomicops_unittest.cc",
+      "base64_unittest.cc",
+      "basictypes_unittest.cc",
+      "bind_unittest.cc",
+      "bitbuffer_unittest.cc",
+      "buffer_unittest.cc",
+      "bufferqueue_unittest.cc",
+      "bytebuffer_unittest.cc",
+      "byteorder_unittest.cc",
+      "copyonwritebuffer_unittest.cc",
+      "criticalsection_unittest.cc",
+      "event_tracer_unittest.cc",
+      "event_unittest.cc",
+      "file_unittest.cc",
+      "function_view_unittest.cc",
+      "logging_unittest.cc",
+      "md5digest_unittest.cc",
+      "mod_ops_unittest.cc",
+      "onetimeevent_unittest.cc",
+      "optional_unittest.cc",
+      "pathutils_unittest.cc",
+      "platform_thread_unittest.cc",
+      "random_unittest.cc",
+      "rate_limiter_unittest.cc",
+      "rate_statistics_unittest.cc",
+      "ratetracker_unittest.cc",
+      "refcountedobject_unittest.cc",
+      "safe_compare_unittest.cc",
+      "safe_minmax_unittest.cc",
+      "string_to_number_unittest.cc",
+      "stringencode_unittest.cc",
+      "stringize_macros_unittest.cc",
+      "stringutils_unittest.cc",
+      "swap_queue_unittest.cc",
+      "thread_annotations_unittest.cc",
+      "thread_checker_unittest.cc",
+      "timestampaligner_unittest.cc",
+      "timeutils_unittest.cc",
+      "virtualsocket_unittest.cc",
+    ]
+    deps = [
+      ":rtc_base",
+      ":rtc_base_approved",
+      ":rtc_base_tests_main",
+      ":rtc_base_tests_utils",
+      ":rtc_task_queue",
+      "../system_wrappers:system_wrappers",
+      "../test:test_support",
+    ]
+    if (!build_with_chromium && is_clang) {
+      # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+      suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+    }
   }
 
-  group("sequenced_task_checker_unittests") {
+  rtc_source_set("rtc_task_queue_unittests") {
     testonly = true
-    public_deps = [ "../rtc_base:sequenced_task_checker_unittests" ]
+
+    # Skip restricting visibility on mobile platforms since the tests on those
+    # gets additional generated targets which would require many lines here to
+    # cover (which would be confusing to read and hard to maintain).
+    if (!is_android && !is_ios) {
+      visibility = [ "//webrtc:rtc_unittests" ]
+    }
+    sources = [
+      "task_queue_unittest.cc",
+    ]
+    deps = [
+      ":rtc_base_tests_main",
+      ":rtc_base_tests_utils",
+      ":rtc_task_queue",
+      "../test:test_support",
+    ]
+    if (!build_with_chromium && is_clang) {
+      # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+      suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+    }
   }
 
-  group("weak_ptr_unittests") {
+  rtc_source_set("sequenced_task_checker_unittests") {
     testonly = true
-    public_deps = [ "../rtc_base:weak_ptr_unittests" ]
+
+    # Skip restricting visibility on mobile platforms since the tests on those
+    # gets additional generated targets which would require many lines here to
+    # cover (which would be confusing to read and hard to maintain).
+    if (!is_android && !is_ios) {
+      visibility = [ "//webrtc:rtc_unittests" ]
+    }
+    sources = [
+      "sequenced_task_checker_unittest.cc",
+    ]
+    deps = [
+      ":rtc_base_approved",
+      ":rtc_base_tests_main",
+      ":rtc_task_queue",
+      ":sequenced_task_checker",
+      "../test:test_support",
+    ]
   }
 
-  group("rtc_task_queue_unittests") {
+  rtc_source_set("weak_ptr_unittests") {
     testonly = true
-    public_deps = [ "../rtc_base:rtc_task_queue_unittests" ]
+
+    # Skip restricting visibility on mobile platforms since the tests on those
+    # gets additional generated targets which would require many lines here to
+    # cover (which would be confusing to read and hard to maintain).
+    if (!is_android && !is_ios) {
+      visibility = [ "//webrtc:rtc_unittests" ]
+    }
+    sources = [
+      "weak_ptr_unittest.cc",
+    ]
+    deps = [
+      ":rtc_base_tests_main",
+      ":rtc_base_tests_utils",
+      ":rtc_task_queue",
+      ":weak_ptr",
+      "../test:test_support",
+    ]
   }
 
-
-  group("rtc_numerics_unittests") {
+  rtc_source_set("rtc_numerics_unittests") {
     testonly = true
-    public_deps = [ "../rtc_base:rtc_numerics_unittests" ]
+
+    # Skip restricting visibility on mobile platforms since the tests on those
+    # gets additional generated targets which would require many lines here to
+    # cover (which would be confusing to read and hard to maintain).
+    if (!is_android && !is_ios) {
+      visibility = [ "//webrtc:rtc_unittests" ]
+    }
+    sources = [
+      "numerics/exp_filter_unittest.cc",
+      "numerics/percentile_filter_unittest.cc",
+    ]
+    deps = [
+      ":rtc_base_approved",
+      ":rtc_base_tests_main",
+      ":rtc_numerics",
+      "../test:test_support",
+    ]
   }
 
-  group("rtc_base_unittests") {
+  config("rtc_base_unittests_config") {
+    if (is_clang) {
+      cflags = [ "-Wno-unused-const-variable" ]
+    }
+  }
+  rtc_source_set("rtc_base_unittests") {
     testonly = true
-    public_deps = [ "../rtc_base:rtc_base_unittests" ]
+
+    # Skip restricting visibility on mobile platforms since the tests on those
+    # gets additional generated targets which would require many lines here to
+    # cover (which would be confusing to read and hard to maintain).
+    if (!is_android && !is_ios) {
+      visibility = [ "//webrtc:rtc_unittests" ]
+    }
+    sources = [
+      "callback_unittest.cc",
+      "crc32_unittest.cc",
+      "fileutils_unittest.cc",
+      "helpers_unittest.cc",
+      "httpbase_unittest.cc",
+      "httpcommon_unittest.cc",
+      "httpserver_unittest.cc",
+      "ipaddress_unittest.cc",
+      "memory_usage_unittest.cc",
+      "messagedigest_unittest.cc",
+      "messagequeue_unittest.cc",
+      "nat_unittest.cc",
+      "network_unittest.cc",
+      "optionsfile_unittest.cc",
+      "proxy_unittest.cc",
+      "ptr_util_unittest.cc",
+      "ratelimiter_unittest.cc",
+      "rollingaccumulator_unittest.cc",
+      "rtccertificate_unittest.cc",
+      "rtccertificategenerator_unittest.cc",
+      "sha1digest_unittest.cc",
+      "signalthread_unittest.cc",
+      "sigslot_unittest.cc",
+      "sigslottester_unittest.cc",
+      "stream_unittest.cc",
+      "testclient_unittest.cc",
+      "thread_unittest.cc",
+    ]
+    if (is_win) {
+      sources += [
+        "win32_unittest.cc",
+        "win32window_unittest.cc",
+      ]
+    }
+    if (is_mac) {
+      sources += [ "macutils_unittest.cc" ]
+    }
+    if (is_posix) {
+      sources += [
+        "ssladapter_unittest.cc",
+        "sslidentity_unittest.cc",
+        "sslstreamadapter_unittest.cc",
+      ]
+    }
+    deps = [
+      ":rtc_base_tests_main",
+      ":rtc_base_tests_utils",
+      "../test:test_support",
+    ]
+    public_deps = [
+      ":rtc_base",
+    ]
+    configs += [ ":rtc_base_unittests_config" ]
+    if (!build_with_chromium && is_clang) {
+      # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+      suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+    }
   }
 }
 
 if (is_android) {
   android_library("base_java") {
-    java_files = [ "Dummy.java" ]  # Need one file to avoid hitting an assert.
-    deps = [ "../rtc_base:base_java" ]
+    java_files = [
+      "java/src/org/webrtc/ContextUtils.java",
+      "java/src/org/webrtc/Logging.java",
+      "java/src/org/webrtc/Size.java",
+      "java/src/org/webrtc/ThreadUtils.java",
+    ]
   }
 }
diff --git a/base/DEPS b/base/DEPS
new file mode 100644
index 0000000..6abcfb8
--- /dev/null
+++ b/base/DEPS
@@ -0,0 +1,15 @@
+include_rules = [
+  "+base/third_party/libevent",
+  "+json",
+  "+third_party/jsoncpp",
+  "+webrtc/system_wrappers",
+]
+
+specific_include_rules = {
+  "gunit_prod.h": [
+    "+gtest",
+  ],
+  "protobuf_utils.h": [
+    "+third_party/protobuf",
+  ],
+}
diff --git a/base/Dummy.java b/base/Dummy.java
deleted file mode 100644
index 60cd440..0000000
--- a/base/Dummy.java
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * This class only exists as glue in a transition.
- * TODO(kjellander): Remove.
- * See https://bugs.webrtc.org/7634 for more details.
- */
-class Dummy {
-  Dummy() {
-  }
-}
diff --git a/base/OWNERS b/base/OWNERS
new file mode 100644
index 0000000..f7e8d2a
--- /dev/null
+++ b/base/OWNERS
@@ -0,0 +1,19 @@
+henrika@webrtc.org
+henrikg@webrtc.org
+hta@webrtc.org
+juberti@webrtc.org
+mflodman@webrtc.org
+perkj@webrtc.org
+pthatcher@webrtc.org
+sergeyu@chromium.org
+tommi@webrtc.org
+deadbeef@webrtc.org
+kwiberg@webrtc.org
+
+# These are for the common case of adding or renaming files. If you're doing
+# structural changes, please get a review from a reviewer in this file.
+per-file *.gn=*
+per-file *.gni=*
+
+per-file rate_statistics*=sprang@webrtc.org
+per-file rate_statistics*=stefan@webrtc.org
diff --git a/base/applefilesystem.mm b/base/applefilesystem.mm
new file mode 100644
index 0000000..5fa2055
--- /dev/null
+++ b/base/applefilesystem.mm
@@ -0,0 +1,53 @@
+/*
+ *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+// This file only exists because various iOS and macOS system APIs are only
+// available from Objective-C. See unixfilesystem.cc for the only use
+// (enforced by a lack of a header file).
+
+#import <Foundation/NSPathUtilities.h>
+#import <Foundation/NSProcessInfo.h>
+#include <string.h>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/pathutils.h"
+
+// Return a new[]'d |char*| copy of the UTF8 representation of |s|.
+// Caller owns the returned memory and must use delete[] on it.
+static char* copyString(NSString* s) {
+  const char* utf8 = [s UTF8String];
+  size_t len = strlen(utf8) + 1;
+  char* copy = new char[len];
+  // This uses a new[] + strcpy (instead of strdup) because the
+  // receiver expects to be able to delete[] the returned pointer
+  // (instead of free()ing it).
+  strcpy(copy, utf8);
+  return copy;
+}
+
+// Return a (leaked) copy of a directory name suitable for application data.
+char* AppleDataDirectory() {
+  NSArray* paths = NSSearchPathForDirectoriesInDomains(
+      NSApplicationSupportDirectory, NSUserDomainMask, YES);
+  RTC_DCHECK([paths count] == 1);
+  return copyString([paths objectAtIndex:0]);
+}
+
+// Return a (leaked) copy of a directory name suitable for use as a $TEMP.
+char* AppleTempDirectory() {
+  return copyString(NSTemporaryDirectory());
+}
+
+// Return the binary's path.
+void AppleAppName(rtc::Pathname* path) {
+  NSProcessInfo* pInfo = [NSProcessInfo processInfo];
+  NSString* argv0 = [[pInfo arguments] objectAtIndex:0];
+  path->SetPathname([argv0 UTF8String]);
+}
diff --git a/base/array_view.h b/base/array_view.h
index a451b59..7a0bb28 100644
--- a/base/array_view.h
+++ b/base/array_view.h
@@ -11,9 +11,243 @@
 #ifndef WEBRTC_BASE_ARRAY_VIEW_H_
 #define WEBRTC_BASE_ARRAY_VIEW_H_
 
+#include "webrtc/base/checks.h"
+#include "webrtc/base/type_traits.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/array_view.h"
+namespace rtc {
+
+// Many functions read from or write to arrays. The obvious way to do this is
+// to use two arguments, a pointer to the first element and an element count:
+//
+//   bool Contains17(const int* arr, size_t size) {
+//     for (size_t i = 0; i < size; ++i) {
+//       if (arr[i] == 17)
+//         return true;
+//     }
+//     return false;
+//   }
+//
+// This is flexible, since it doesn't matter how the array is stored (C array,
+// std::vector, rtc::Buffer, ...), but it's error-prone because the caller has
+// to correctly specify the array length:
+//
+//   Contains17(arr, arraysize(arr));     // C array
+//   Contains17(arr.data(), arr.size());  // std::vector
+//   Contains17(arr, size);               // pointer + size
+//   ...
+//
+// It's also kind of messy to have two separate arguments for what is
+// conceptually a single thing.
+//
+// Enter rtc::ArrayView<T>. It contains a T pointer (to an array it doesn't
+// own) and a count, and supports the basic things you'd expect, such as
+// indexing and iteration. It allows us to write our function like this:
+//
+//   bool Contains17(rtc::ArrayView<const int> arr) {
+//     for (auto e : arr) {
+//       if (e == 17)
+//         return true;
+//     }
+//     return false;
+//   }
+//
+// And even better, because a bunch of things will implicitly convert to
+// ArrayView, we can call it like this:
+//
+//   Contains17(arr);                             // C array
+//   Contains17(arr);                             // std::vector
+//   Contains17(rtc::ArrayView<int>(arr, size));  // pointer + size
+//   Contains17(nullptr);                         // nullptr -> empty ArrayView
+//   ...
+//
+// ArrayView<T> stores both a pointer and a size, but you may also use
+// ArrayView<T, N>, which has a size that's fixed at compile time (which means
+// it only has to store the pointer).
+//
+// One important point is that ArrayView<T> and ArrayView<const T> are
+// different types, which allow and don't allow mutation of the array elements,
+// respectively. The implicit conversions work just like you'd hope, so that
+// e.g. vector<int> will convert to either ArrayView<int> or ArrayView<const
+// int>, but const vector<int> will convert only to ArrayView<const int>.
+// (ArrayView itself can be the source type in such conversions, so
+// ArrayView<int> will convert to ArrayView<const int>.)
+//
+// Note: ArrayView is tiny (just a pointer and a count if variable-sized, just
+// a pointer if fix-sized) and trivially copyable, so it's probably cheaper to
+// pass it by value than by const reference.
+
+namespace impl {
+
+// Magic constant for indicating that the size of an ArrayView is variable
+// instead of fixed.
+enum : std::ptrdiff_t { kArrayViewVarSize = -4711 };
+
+// Base class for ArrayViews of fixed nonzero size.
+template <typename T, std::ptrdiff_t Size>
+class ArrayViewBase {
+  static_assert(Size > 0, "ArrayView size must be variable or non-negative");
+
+ public:
+  ArrayViewBase(T* data, size_t size) : data_(data) {}
+
+  static constexpr size_t size() { return Size; }
+  static constexpr bool empty() { return false; }
+  T* data() const { return data_; }
+
+ protected:
+  static constexpr bool fixed_size() { return true; }
+
+ private:
+  T* data_;
+};
+
+// Specialized base class for ArrayViews of fixed zero size.
+template <typename T>
+class ArrayViewBase<T, 0> {
+ public:
+  explicit ArrayViewBase(T* data, size_t size) {}
+
+  static constexpr size_t size() { return 0; }
+  static constexpr bool empty() { return true; }
+  T* data() const { return nullptr; }
+
+ protected:
+  static constexpr bool fixed_size() { return true; }
+};
+
+// Specialized base class for ArrayViews of variable size.
+template <typename T>
+class ArrayViewBase<T, impl::kArrayViewVarSize> {
+ public:
+  ArrayViewBase(T* data, size_t size)
+      : data_(size == 0 ? nullptr : data), size_(size) {}
+
+  size_t size() const { return size_; }
+  bool empty() const { return size_ == 0; }
+  T* data() const { return data_; }
+
+ protected:
+  static constexpr bool fixed_size() { return false; }
+
+ private:
+  T* data_;
+  size_t size_;
+};
+
+}  // namespace impl
+
+template <typename T, std::ptrdiff_t Size = impl::kArrayViewVarSize>
+class ArrayView final : public impl::ArrayViewBase<T, Size> {
+ public:
+  using value_type = T;
+  using const_iterator = const T*;
+
+  // Construct an ArrayView from a pointer and a length.
+  template <typename U>
+  ArrayView(U* data, size_t size)
+      : impl::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
+    RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data());
+    RTC_DCHECK_EQ(size, this->size());
+    RTC_DCHECK_EQ(!this->data(),
+                  this->size() == 0);  // data is null iff size == 0.
+  }
+
+  // Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0
+  // cannot be empty.
+  ArrayView() : ArrayView(nullptr, 0) {}
+  ArrayView(std::nullptr_t) : ArrayView() {}
+  ArrayView(std::nullptr_t, size_t size)
+      : ArrayView(static_cast<T*>(nullptr), size) {
+    static_assert(Size == 0 || Size == impl::kArrayViewVarSize, "");
+    RTC_DCHECK_EQ(0, size);
+  }
+
+  // Construct an ArrayView from an array.
+  template <typename U, size_t N>
+  ArrayView(U (&array)[N]) : ArrayView(array, N) {
+    static_assert(Size == N || Size == impl::kArrayViewVarSize,
+                  "Array size must match ArrayView size");
+  }
+
+  // (Only if size is fixed.) Construct an ArrayView from any type U that has a
+  // static constexpr size() method whose return value is equal to Size, and a
+  // data() method whose return value converts implicitly to T*. In particular,
+  // this means we allow conversion from ArrayView<T, N> to ArrayView<const T,
+  // N>, but not the other way around. We also don't allow conversion from
+  // ArrayView<T> to ArrayView<T, N>, or from ArrayView<T, M> to ArrayView<T,
+  // N> when M != N.
+  template <typename U,
+            typename std::enable_if<
+                Size != impl::kArrayViewVarSize &&
+                HasDataAndSize<U, T>::value>::type* = nullptr>
+  ArrayView(U& u) : ArrayView(u.data(), u.size()) {
+    static_assert(U::size() == Size, "Sizes must match exactly");
+  }
+
+  // (Only if size is variable.) Construct an ArrayView from any type U that
+  // has a size() method whose return value converts implicitly to size_t, and
+  // a data() method whose return value converts implicitly to T*. In
+  // particular, this means we allow conversion from ArrayView<T> to
+  // ArrayView<const T>, but not the other way around. Other allowed
+  // conversions include
+  // ArrayView<T, N> to ArrayView<T> or ArrayView<const T>,
+  // std::vector<T> to ArrayView<T> or ArrayView<const T>,
+  // const std::vector<T> to ArrayView<const T>,
+  // rtc::Buffer to ArrayView<uint8_t> or ArrayView<const uint8_t>, and
+  // const rtc::Buffer to ArrayView<const uint8_t>.
+  template <
+      typename U,
+      typename std::enable_if<Size == impl::kArrayViewVarSize &&
+                              HasDataAndSize<U, T>::value>::type* = nullptr>
+  ArrayView(U& u) : ArrayView(u.data(), u.size()) {}
+
+  // Indexing and iteration. These allow mutation even if the ArrayView is
+  // const, because the ArrayView doesn't own the array. (To prevent mutation,
+  // use a const element type.)
+  T& operator[](size_t idx) const {
+    RTC_DCHECK_LT(idx, this->size());
+    RTC_DCHECK(this->data());
+    return this->data()[idx];
+  }
+  T* begin() const { return this->data(); }
+  T* end() const { return this->data() + this->size(); }
+  const T* cbegin() const { return this->data(); }
+  const T* cend() const { return this->data() + this->size(); }
+
+  ArrayView<T> subview(size_t offset, size_t size) const {
+    return offset < this->size()
+               ? ArrayView<T>(this->data() + offset,
+                              std::min(size, this->size() - offset))
+               : ArrayView<T>();
+  }
+  ArrayView<T> subview(size_t offset) const {
+    return subview(offset, this->size());
+  }
+};
+
+// Comparing two ArrayViews compares their (pointer,size) pairs; it does *not*
+// dereference the pointers.
+template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
+bool operator==(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
+  return a.data() == b.data() && a.size() == b.size();
+}
+template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
+bool operator!=(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
+  return !(a == b);
+}
+
+// Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews
+// are the size of one pointer. (And as a special case, fixed-size ArrayViews
+// of size 0 require no storage.)
+static_assert(sizeof(ArrayView<int>) == 2 * sizeof(int*), "");
+static_assert(sizeof(ArrayView<int, 17>) == sizeof(int*), "");
+static_assert(std::is_empty<ArrayView<int, 0>>::value, "");
+
+template <typename T>
+inline ArrayView<T> MakeArrayView(T* data, size_t size) {
+  return ArrayView<T>(data, size);
+}
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_ARRAY_VIEW_H_
diff --git a/base/array_view_unittest.cc b/base/array_view_unittest.cc
new file mode 100644
index 0000000..035f25c
--- /dev/null
+++ b/base/array_view_unittest.cc
@@ -0,0 +1,411 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "webrtc/base/array_view.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/test/gmock.h"
+
+namespace rtc {
+
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+
+template <typename T>
+void Call(ArrayView<T>) {}
+
+}  // namespace
+
+TEST(ArrayViewTest, TestConstructFromPtrAndArray) {
+  char arr[] = "Arrr!";
+  const char carr[] = "Carrr!";
+  Call<const char>(arr);
+  Call<const char>(carr);
+  Call<char>(arr);
+  // Call<char>(carr);  // Compile error, because can't drop const.
+  // Call<int>(arr);  // Compile error, because incompatible types.
+  ArrayView<int*> x;
+  EXPECT_EQ(0u, x.size());
+  EXPECT_EQ(nullptr, x.data());
+  ArrayView<char> y = arr;
+  EXPECT_EQ(6u, y.size());
+  EXPECT_EQ(arr, y.data());
+  ArrayView<char, 6> yf = arr;
+  static_assert(yf.size() == 6, "");
+  EXPECT_EQ(arr, yf.data());
+  ArrayView<const char> z(arr + 1, 3);
+  EXPECT_EQ(3u, z.size());
+  EXPECT_EQ(arr + 1, z.data());
+  ArrayView<const char, 3> zf(arr + 1, 3);
+  static_assert(zf.size() == 3, "");
+  EXPECT_EQ(arr + 1, zf.data());
+  ArrayView<const char> w(arr, 2);
+  EXPECT_EQ(2u, w.size());
+  EXPECT_EQ(arr, w.data());
+  ArrayView<const char, 2> wf(arr, 2);
+  static_assert(wf.size() == 2, "");
+  EXPECT_EQ(arr, wf.data());
+  ArrayView<char> q(arr, 0);
+  EXPECT_EQ(0u, q.size());
+  EXPECT_EQ(nullptr, q.data());
+  ArrayView<char, 0> qf(arr, 0);
+  static_assert(qf.size() == 0, "");
+  EXPECT_EQ(nullptr, qf.data());
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+  // DCHECK error (nullptr with nonzero size).
+  EXPECT_DEATH(ArrayView<int>(static_cast<int*>(nullptr), 5), "");
+#endif
+  // These are compile errors, because incompatible types.
+  // ArrayView<int> m = arr;
+  // ArrayView<float> n(arr + 2, 2);
+}
+
+TEST(ArrayViewTest, TestCopyConstructorVariable) {
+  char arr[] = "Arrr!";
+  ArrayView<char> x = arr;
+  EXPECT_EQ(6u, x.size());
+  EXPECT_EQ(arr, x.data());
+  ArrayView<char> y = x;  // Copy non-const -> non-const.
+  EXPECT_EQ(6u, y.size());
+  EXPECT_EQ(arr, y.data());
+  ArrayView<const char> z = x;  // Copy non-const -> const.
+  EXPECT_EQ(6u, z.size());
+  EXPECT_EQ(arr, z.data());
+  ArrayView<const char> w = z;  // Copy const -> const.
+  EXPECT_EQ(6u, w.size());
+  EXPECT_EQ(arr, w.data());
+  // ArrayView<char> v = z;  // Compile error, because can't drop const.
+}
+
+TEST(ArrayViewTest, TestCopyConstructorFixed) {
+  char arr[] = "Arrr!";
+  ArrayView<char, 6> x = arr;
+  static_assert(x.size() == 6, "");
+  EXPECT_EQ(arr, x.data());
+
+  // Copy fixed -> fixed.
+  ArrayView<char, 6> y = x;  // Copy non-const -> non-const.
+  static_assert(y.size() == 6, "");
+  EXPECT_EQ(arr, y.data());
+  ArrayView<const char, 6> z = x;  // Copy non-const -> const.
+  static_assert(z.size() == 6, "");
+  EXPECT_EQ(arr, z.data());
+  ArrayView<const char, 6> w = z;  // Copy const -> const.
+  static_assert(w.size() == 6, "");
+  EXPECT_EQ(arr, w.data());
+  // ArrayView<char, 6> v = z;  // Compile error, because can't drop const.
+
+  // Copy fixed -> variable.
+  ArrayView<char> yv = x;  // Copy non-const -> non-const.
+  EXPECT_EQ(6u, yv.size());
+  EXPECT_EQ(arr, yv.data());
+  ArrayView<const char> zv = x;  // Copy non-const -> const.
+  EXPECT_EQ(6u, zv.size());
+  EXPECT_EQ(arr, zv.data());
+  ArrayView<const char> wv = z;  // Copy const -> const.
+  EXPECT_EQ(6u, wv.size());
+  EXPECT_EQ(arr, wv.data());
+  // ArrayView<char> vv = z;  // Compile error, because can't drop const.
+}
+
+TEST(ArrayViewTest, TestCopyAssignmentVariable) {
+  char arr[] = "Arrr!";
+  ArrayView<char> x(arr);
+  EXPECT_EQ(6u, x.size());
+  EXPECT_EQ(arr, x.data());
+  ArrayView<char> y;
+  y = x;  // Copy non-const -> non-const.
+  EXPECT_EQ(6u, y.size());
+  EXPECT_EQ(arr, y.data());
+  ArrayView<const char> z;
+  z = x;  // Copy non-const -> const.
+  EXPECT_EQ(6u, z.size());
+  EXPECT_EQ(arr, z.data());
+  ArrayView<const char> w;
+  w = z;  // Copy const -> const.
+  EXPECT_EQ(6u, w.size());
+  EXPECT_EQ(arr, w.data());
+  // ArrayView<char> v;
+  // v = z;  // Compile error, because can't drop const.
+}
+
+TEST(ArrayViewTest, TestCopyAssignmentFixed) {
+  char arr[] = "Arrr!";
+  char init[] = "Init!";
+  ArrayView<char, 6> x(arr);
+  EXPECT_EQ(arr, x.data());
+
+  // Copy fixed -> fixed.
+  ArrayView<char, 6> y(init);
+  y = x;  // Copy non-const -> non-const.
+  EXPECT_EQ(arr, y.data());
+  ArrayView<const char, 6> z(init);
+  z = x;  // Copy non-const -> const.
+  EXPECT_EQ(arr, z.data());
+  ArrayView<const char, 6> w(init);
+  w = z;  // Copy const -> const.
+  EXPECT_EQ(arr, w.data());
+  // ArrayView<char, 6> v(init);
+  // v = z;  // Compile error, because can't drop const.
+
+  // Copy fixed -> variable.
+  ArrayView<char> yv;
+  yv = x;  // Copy non-const -> non-const.
+  EXPECT_EQ(6u, yv.size());
+  EXPECT_EQ(arr, yv.data());
+  ArrayView<const char> zv;
+  zv = x;  // Copy non-const -> const.
+  EXPECT_EQ(6u, zv.size());
+  EXPECT_EQ(arr, zv.data());
+  ArrayView<const char> wv;
+  wv = z;  // Copy const -> const.
+  EXPECT_EQ(6u, wv.size());
+  EXPECT_EQ(arr, wv.data());
+  // ArrayView<char> v;
+  // v = z;  // Compile error, because can't drop const.
+}
+
+TEST(ArrayViewTest, TestStdVector) {
+  std::vector<int> v;
+  v.push_back(3);
+  v.push_back(11);
+  Call<const int>(v);
+  Call<int>(v);
+  // Call<unsigned int>(v);  // Compile error, because incompatible types.
+  ArrayView<int> x = v;
+  EXPECT_EQ(2u, x.size());
+  EXPECT_EQ(v.data(), x.data());
+  ArrayView<const int> y;
+  y = v;
+  EXPECT_EQ(2u, y.size());
+  EXPECT_EQ(v.data(), y.data());
+  // ArrayView<double> d = v;  // Compile error, because incompatible types.
+  const std::vector<int> cv;
+  Call<const int>(cv);
+  // Call<int>(cv);  // Compile error, because can't drop const.
+  ArrayView<const int> z = cv;
+  EXPECT_EQ(0u, z.size());
+  EXPECT_EQ(nullptr, z.data());
+  // ArrayView<int> w = cv;  // Compile error, because can't drop const.
+}
+
+TEST(ArrayViewTest, TestRtcBuffer) {
+  rtc::Buffer b = "so buffer";
+  Call<const uint8_t>(b);
+  Call<uint8_t>(b);
+  // Call<int8_t>(b);  // Compile error, because incompatible types.
+  ArrayView<uint8_t> x = b;
+  EXPECT_EQ(10u, x.size());
+  EXPECT_EQ(b.data(), x.data());
+  ArrayView<const uint8_t> y;
+  y = b;
+  EXPECT_EQ(10u, y.size());
+  EXPECT_EQ(b.data(), y.data());
+  // ArrayView<char> d = b;  // Compile error, because incompatible types.
+  const rtc::Buffer cb = "very const";
+  Call<const uint8_t>(cb);
+  // Call<uint8_t>(cb);  // Compile error, because can't drop const.
+  ArrayView<const uint8_t> z = cb;
+  EXPECT_EQ(11u, z.size());
+  EXPECT_EQ(cb.data(), z.data());
+  // ArrayView<uint8_t> w = cb;  // Compile error, because can't drop const.
+}
+
+TEST(ArrayViewTest, TestSwapVariable) {
+  const char arr[] = "Arrr!";
+  const char aye[] = "Aye, Cap'n!";
+  ArrayView<const char> x(arr);
+  EXPECT_EQ(6u, x.size());
+  EXPECT_EQ(arr, x.data());
+  ArrayView<const char> y(aye);
+  EXPECT_EQ(12u, y.size());
+  EXPECT_EQ(aye, y.data());
+  using std::swap;
+  swap(x, y);
+  EXPECT_EQ(12u, x.size());
+  EXPECT_EQ(aye, x.data());
+  EXPECT_EQ(6u, y.size());
+  EXPECT_EQ(arr, y.data());
+  // ArrayView<char> z;
+  // swap(x, z);  // Compile error, because can't drop const.
+}
+
+TEST(FixArrayViewTest, TestSwapFixed) {
+  const char arr[] = "Arr!";
+  char aye[] = "Aye!";
+  ArrayView<const char, 5> x(arr);
+  EXPECT_EQ(arr, x.data());
+  ArrayView<const char, 5> y(aye);
+  EXPECT_EQ(aye, y.data());
+  using std::swap;
+  swap(x, y);
+  EXPECT_EQ(aye, x.data());
+  EXPECT_EQ(arr, y.data());
+  // ArrayView<char, 5> z(aye);
+  // swap(x, z);  // Compile error, because can't drop const.
+  // ArrayView<const char, 4> w(aye, 4);
+  // swap(x, w);  // Compile error, because different sizes.
+}
+
+TEST(ArrayViewTest, TestIndexing) {
+  char arr[] = "abcdefg";
+  ArrayView<char> x(arr);
+  const ArrayView<char> y(arr);
+  ArrayView<const char, 8> z(arr);
+  EXPECT_EQ(8u, x.size());
+  EXPECT_EQ(8u, y.size());
+  EXPECT_EQ(8u, z.size());
+  EXPECT_EQ('b', x[1]);
+  EXPECT_EQ('c', y[2]);
+  EXPECT_EQ('d', z[3]);
+  x[3] = 'X';
+  y[2] = 'Y';
+  // z[1] = 'Z';  // Compile error, because z's element type is const char.
+  EXPECT_EQ('b', x[1]);
+  EXPECT_EQ('Y', y[2]);
+  EXPECT_EQ('X', z[3]);
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+  EXPECT_DEATH(z[8], "");  // DCHECK error (index out of bounds).
+#endif
+}
+
+TEST(ArrayViewTest, TestIterationEmpty) {
+  // Variable-size.
+  ArrayView<std::vector<std::vector<std::vector<std::string>>>> av;
+  EXPECT_EQ(av.begin(), av.end());
+  EXPECT_EQ(av.cbegin(), av.cend());
+  for (auto& e : av) {
+    EXPECT_TRUE(false);
+    EXPECT_EQ(42u, e.size());  // Dummy use of e to prevent unused var warning.
+  }
+
+  // Fixed-size.
+  ArrayView<std::vector<std::vector<std::vector<std::string>>>, 0> af;
+  EXPECT_EQ(af.begin(), af.end());
+  EXPECT_EQ(af.cbegin(), af.cend());
+  for (auto& e : af) {
+    EXPECT_TRUE(false);
+    EXPECT_EQ(42u, e.size());  // Dummy use of e to prevent unused var warning.
+  }
+}
+
+TEST(ArrayViewTest, TestIterationVariable) {
+  char arr[] = "Arrr!";
+  ArrayView<char> av(arr);
+  EXPECT_EQ('A', *av.begin());
+  EXPECT_EQ('A', *av.cbegin());
+  EXPECT_EQ('\0', *(av.end() - 1));
+  EXPECT_EQ('\0', *(av.cend() - 1));
+  char i = 0;
+  for (auto& e : av) {
+    EXPECT_EQ(arr + i, &e);
+    e = 's' + i;
+    ++i;
+  }
+  i = 0;
+  for (auto& e : ArrayView<const char>(av)) {
+    EXPECT_EQ(arr + i, &e);
+    // e = 'q' + i;  // Compile error, because e is a const char&.
+    ++i;
+  }
+}
+
+TEST(ArrayViewTest, TestIterationFixed) {
+  char arr[] = "Arrr!";
+  ArrayView<char, 6> av(arr);
+  EXPECT_EQ('A', *av.begin());
+  EXPECT_EQ('A', *av.cbegin());
+  EXPECT_EQ('\0', *(av.end() - 1));
+  EXPECT_EQ('\0', *(av.cend() - 1));
+  char i = 0;
+  for (auto& e : av) {
+    EXPECT_EQ(arr + i, &e);
+    e = 's' + i;
+    ++i;
+  }
+  i = 0;
+  for (auto& e : ArrayView<const char, 6>(av)) {
+    EXPECT_EQ(arr + i, &e);
+    // e = 'q' + i;  // Compile error, because e is a const char&.
+    ++i;
+  }
+}
+
+TEST(ArrayViewTest, TestEmpty) {
+  EXPECT_TRUE(ArrayView<int>().empty());
+  const int a[] = {1, 2, 3};
+  EXPECT_FALSE(ArrayView<const int>(a).empty());
+
+  static_assert(ArrayView<int, 0>::empty(), "");
+  static_assert(!ArrayView<int, 3>::empty(), "");
+}
+
+TEST(ArrayViewTest, TestCompare) {
+  int a[] = {1, 2, 3};
+  int b[] = {1, 2, 3};
+
+  EXPECT_EQ(ArrayView<int>(a), ArrayView<int>(a));
+  EXPECT_EQ((ArrayView<int, 3>(a)), (ArrayView<int, 3>(a)));
+  EXPECT_EQ(ArrayView<int>(a), (ArrayView<int, 3>(a)));
+  EXPECT_EQ(ArrayView<int>(), ArrayView<int>());
+  EXPECT_EQ(ArrayView<int>(), ArrayView<int>(a, 0));
+  EXPECT_EQ(ArrayView<int>(a, 0), ArrayView<int>(b, 0));
+  EXPECT_EQ((ArrayView<int, 0>(a, 0)), ArrayView<int>());
+
+  EXPECT_NE(ArrayView<int>(a), ArrayView<int>(b));
+  EXPECT_NE((ArrayView<int, 3>(a)), (ArrayView<int, 3>(b)));
+  EXPECT_NE((ArrayView<int, 3>(a)), ArrayView<int>(b));
+  EXPECT_NE(ArrayView<int>(a), ArrayView<int>());
+  EXPECT_NE(ArrayView<int>(a), ArrayView<int>(a, 2));
+  EXPECT_NE((ArrayView<int, 3>(a)), (ArrayView<int, 2>(a, 2)));
+}
+
+TEST(ArrayViewTest, TestSubViewVariable) {
+  int a[] = {1, 2, 3};
+  ArrayView<int> av(a);
+
+  EXPECT_EQ(av.subview(0), av);
+
+  EXPECT_THAT(av.subview(1), ElementsAre(2, 3));
+  EXPECT_THAT(av.subview(2), ElementsAre(3));
+  EXPECT_THAT(av.subview(3), IsEmpty());
+  EXPECT_THAT(av.subview(4), IsEmpty());
+
+  EXPECT_THAT(av.subview(1, 0), IsEmpty());
+  EXPECT_THAT(av.subview(1, 1), ElementsAre(2));
+  EXPECT_THAT(av.subview(1, 2), ElementsAre(2, 3));
+  EXPECT_THAT(av.subview(1, 3), ElementsAre(2, 3));
+}
+
+TEST(ArrayViewTest, TestSubViewFixed) {
+  int a[] = {1, 2, 3};
+  ArrayView<int, 3> av(a);
+
+  EXPECT_EQ(av.subview(0), av);
+
+  EXPECT_THAT(av.subview(1), ElementsAre(2, 3));
+  EXPECT_THAT(av.subview(2), ElementsAre(3));
+  EXPECT_THAT(av.subview(3), IsEmpty());
+  EXPECT_THAT(av.subview(4), IsEmpty());
+
+  EXPECT_THAT(av.subview(1, 0), IsEmpty());
+  EXPECT_THAT(av.subview(1, 1), ElementsAre(2));
+  EXPECT_THAT(av.subview(1, 2), ElementsAre(2, 3));
+  EXPECT_THAT(av.subview(1, 3), ElementsAre(2, 3));
+}
+
+}  // namespace rtc
diff --git a/base/arraysize.h b/base/arraysize.h
index 8b37efa..56a1039 100644
--- a/base/arraysize.h
+++ b/base/arraysize.h
@@ -11,9 +11,21 @@
 #ifndef WEBRTC_BASE_ARRAYSIZE_H_
 #define WEBRTC_BASE_ARRAYSIZE_H_
 
+#include <stddef.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/arraysize.h"
+// This file defines the arraysize() macro and is derived from Chromium's
+// base/macros.h.
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example.  If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N];
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
 
 #endif  // WEBRTC_BASE_ARRAYSIZE_H_
diff --git a/base/asyncinvoker-inl.h b/base/asyncinvoker-inl.h
index cce4226..5f7cd49 100644
--- a/base/asyncinvoker-inl.h
+++ b/base/asyncinvoker-inl.h
@@ -11,9 +11,46 @@
 #ifndef WEBRTC_BASE_ASYNCINVOKER_INL_H_
 #define WEBRTC_BASE_ASYNCINVOKER_INL_H_
 
+#include "webrtc/base/atomicops.h"
+#include "webrtc/base/bind.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/messagehandler.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/thread_annotations.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/asyncinvoker-inl.h"
+namespace rtc {
+
+class AsyncInvoker;
+
+// Helper class for AsyncInvoker. Runs a task and triggers a callback
+// on the calling thread if necessary.
+class AsyncClosure {
+ public:
+  explicit AsyncClosure(AsyncInvoker* invoker) : invoker_(invoker) {}
+  virtual ~AsyncClosure();
+  // Runs the asynchronous task, and triggers a callback to the calling
+  // thread if needed. Should be called from the target thread.
+  virtual void Execute() = 0;
+
+ protected:
+  AsyncInvoker* invoker_;
+};
+
+// Simple closure that doesn't trigger a callback for the calling thread.
+template <class FunctorT>
+class FireAndForgetAsyncClosure : public AsyncClosure {
+ public:
+  explicit FireAndForgetAsyncClosure(AsyncInvoker* invoker,
+                                     const FunctorT& functor)
+      : AsyncClosure(invoker), functor_(functor) {}
+  virtual void Execute() {
+    functor_();
+  }
+ private:
+  FunctorT functor_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_ASYNCINVOKER_INL_H_
diff --git a/base/asyncinvoker.cc b/base/asyncinvoker.cc
new file mode 100644
index 0000000..bfd1317
--- /dev/null
+++ b/base/asyncinvoker.cc
@@ -0,0 +1,119 @@
+/*
+ *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/asyncinvoker.h"
+
+#include "webrtc/base/atomicops.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+
+namespace rtc {
+
+AsyncInvoker::AsyncInvoker() : invocation_complete_(false, false) {}
+
+AsyncInvoker::~AsyncInvoker() {
+  destroying_ = true;
+  // Messages for this need to be cleared *before* our destructor is complete.
+  MessageQueueManager::Clear(this);
+  // And we need to wait for any invocations that are still in progress on
+  // other threads.
+  while (AtomicOps::AcquireLoad(&pending_invocations_)) {
+    // If the destructor was called while AsyncInvoke was being called by
+    // another thread, WITHIN an AsyncInvoked functor, it may do another
+    // Thread::Post even after we called MessageQueueManager::Clear(this). So
+    // we need to keep calling Clear to discard these posts.
+    MessageQueueManager::Clear(this);
+    invocation_complete_.Wait(Event::kForever);
+  }
+}
+
+void AsyncInvoker::OnMessage(Message* msg) {
+  // Get the AsyncClosure shared ptr from this message's data.
+  ScopedMessageData<AsyncClosure>* data =
+      static_cast<ScopedMessageData<AsyncClosure>*>(msg->pdata);
+  // Execute the closure and trigger the return message if needed.
+  data->inner_data().Execute();
+  delete data;
+}
+
+void AsyncInvoker::Flush(Thread* thread, uint32_t id /*= MQID_ANY*/) {
+  if (destroying_) return;
+
+  // Run this on |thread| to reduce the number of context switches.
+  if (Thread::Current() != thread) {
+    thread->Invoke<void>(RTC_FROM_HERE,
+                         Bind(&AsyncInvoker::Flush, this, thread, id));
+    return;
+  }
+
+  MessageList removed;
+  thread->Clear(this, id, &removed);
+  for (MessageList::iterator it = removed.begin(); it != removed.end(); ++it) {
+    // This message was pending on this thread, so run it now.
+    thread->Send(it->posted_from, it->phandler, it->message_id, it->pdata);
+  }
+}
+
+void AsyncInvoker::DoInvoke(const Location& posted_from,
+                            Thread* thread,
+                            std::unique_ptr<AsyncClosure> closure,
+                            uint32_t id) {
+  if (destroying_) {
+    LOG(LS_WARNING) << "Tried to invoke while destroying the invoker.";
+    return;
+  }
+  AtomicOps::Increment(&pending_invocations_);
+  thread->Post(posted_from, this, id,
+               new ScopedMessageData<AsyncClosure>(std::move(closure)));
+}
+
+void AsyncInvoker::DoInvokeDelayed(const Location& posted_from,
+                                   Thread* thread,
+                                   std::unique_ptr<AsyncClosure> closure,
+                                   uint32_t delay_ms,
+                                   uint32_t id) {
+  if (destroying_) {
+    LOG(LS_WARNING) << "Tried to invoke while destroying the invoker.";
+    return;
+  }
+  AtomicOps::Increment(&pending_invocations_);
+  thread->PostDelayed(posted_from, delay_ms, this, id,
+                      new ScopedMessageData<AsyncClosure>(std::move(closure)));
+}
+
+GuardedAsyncInvoker::GuardedAsyncInvoker() : thread_(Thread::Current()) {
+  thread_->SignalQueueDestroyed.connect(this,
+                                        &GuardedAsyncInvoker::ThreadDestroyed);
+}
+
+GuardedAsyncInvoker::~GuardedAsyncInvoker() {
+}
+
+bool GuardedAsyncInvoker::Flush(uint32_t id) {
+  rtc::CritScope cs(&crit_);
+  if (thread_ == nullptr)
+    return false;
+  invoker_.Flush(thread_, id);
+  return true;
+}
+
+void GuardedAsyncInvoker::ThreadDestroyed() {
+  rtc::CritScope cs(&crit_);
+  // We should never get more than one notification about the thread dying.
+  RTC_DCHECK(thread_ != nullptr);
+  thread_ = nullptr;
+}
+
+AsyncClosure::~AsyncClosure() {
+  AtomicOps::Decrement(&invoker_->pending_invocations_);
+  invoker_->invocation_complete_.Set();
+}
+
+}  // namespace rtc
diff --git a/base/asyncinvoker.h b/base/asyncinvoker.h
index 0fcfc04..5414867 100644
--- a/base/asyncinvoker.h
+++ b/base/asyncinvoker.h
@@ -11,9 +11,211 @@
 #ifndef WEBRTC_BASE_ASYNCINVOKER_H_
 #define WEBRTC_BASE_ASYNCINVOKER_H_
 
+#include <memory>
+#include <utility>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/asyncinvoker.h"
+#include "webrtc/base/asyncinvoker-inl.h"
+#include "webrtc/base/bind.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+// Invokes function objects (aka functors) asynchronously on a Thread, and
+// owns the lifetime of calls (ie, when this object is destroyed, calls in
+// flight are cancelled). AsyncInvoker can optionally execute a user-specified
+// function when the asynchronous call is complete, or operates in
+// fire-and-forget mode otherwise.
+//
+// AsyncInvoker does not own the thread it calls functors on.
+//
+// A note about async calls and object lifetimes: users should
+// be mindful of object lifetimes when calling functions asynchronously and
+// ensure objects used by the function _cannot_ be deleted between the
+// invocation and execution of the functor. AsyncInvoker is designed to
+// help: any calls in flight will be cancelled when the AsyncInvoker used to
+// make the call is destructed, and any calls executing will be allowed to
+// complete before AsyncInvoker destructs.
+//
+// The easiest way to ensure lifetimes are handled correctly is to create a
+// class that owns the Thread and AsyncInvoker objects, and then call its
+// methods asynchronously as needed.
+//
+// Example:
+//   class MyClass {
+//    public:
+//     void FireAsyncTaskWithResult(Thread* thread, int x) {
+//       // Specify a callback to get the result upon completion.
+//       invoker_.AsyncInvoke<int>(RTC_FROM_HERE,
+//           thread, Bind(&MyClass::AsyncTaskWithResult, this, x),
+//           &MyClass::OnTaskComplete, this);
+//     }
+//     void FireAnotherAsyncTask(Thread* thread) {
+//       // No callback specified means fire-and-forget.
+//       invoker_.AsyncInvoke<void>(RTC_FROM_HERE,
+//           thread, Bind(&MyClass::AnotherAsyncTask, this));
+//
+//    private:
+//     int AsyncTaskWithResult(int x) {
+//       // Some long running process...
+//       return x * x;
+//     }
+//     void AnotherAsyncTask() {
+//       // Some other long running process...
+//     }
+//     void OnTaskComplete(int result) { result_ = result; }
+//
+//     AsyncInvoker invoker_;
+//     int result_;
+//   };
+class AsyncInvoker : public MessageHandler {
+ public:
+  AsyncInvoker();
+  ~AsyncInvoker() override;
+
+  // Call |functor| asynchronously on |thread|, with no callback upon
+  // completion. Returns immediately.
+  template <class ReturnT, class FunctorT>
+  void AsyncInvoke(const Location& posted_from,
+                   Thread* thread,
+                   const FunctorT& functor,
+                   uint32_t id = 0) {
+    std::unique_ptr<AsyncClosure> closure(
+        new FireAndForgetAsyncClosure<FunctorT>(this, functor));
+    DoInvoke(posted_from, thread, std::move(closure), id);
+  }
+
+  // Call |functor| asynchronously on |thread| with |delay_ms|, with no callback
+  // upon completion. Returns immediately.
+  template <class ReturnT, class FunctorT>
+  void AsyncInvokeDelayed(const Location& posted_from,
+                          Thread* thread,
+                          const FunctorT& functor,
+                          uint32_t delay_ms,
+                          uint32_t id = 0) {
+    std::unique_ptr<AsyncClosure> closure(
+        new FireAndForgetAsyncClosure<FunctorT>(this, functor));
+    DoInvokeDelayed(posted_from, thread, std::move(closure), delay_ms, id);
+  }
+
+  // Synchronously execute on |thread| all outstanding calls we own
+  // that are pending on |thread|, and wait for calls to complete
+  // before returning. Optionally filter by message id.
+  // The destructor will not wait for outstanding calls, so if that
+  // behavior is desired, call Flush() before destroying this object.
+  void Flush(Thread* thread, uint32_t id = MQID_ANY);
+
+ private:
+  void OnMessage(Message* msg) override;
+  void DoInvoke(const Location& posted_from,
+                Thread* thread,
+                std::unique_ptr<AsyncClosure> closure,
+                uint32_t id);
+  void DoInvokeDelayed(const Location& posted_from,
+                       Thread* thread,
+                       std::unique_ptr<AsyncClosure> closure,
+                       uint32_t delay_ms,
+                       uint32_t id);
+  volatile int pending_invocations_ = 0;
+  Event invocation_complete_;
+  bool destroying_ = false;
+  friend class AsyncClosure;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(AsyncInvoker);
+};
+
+// Similar to AsyncInvoker, but guards against the Thread being destroyed while
+// there are outstanding dangling pointers to it. It will connect to the current
+// thread in the constructor, and will get notified when that thread is
+// destroyed. After GuardedAsyncInvoker is constructed, it can be used from
+// other threads to post functors to the thread it was constructed on. If that
+// thread dies, any further calls to AsyncInvoke() will be safely ignored.
+class GuardedAsyncInvoker : public sigslot::has_slots<> {
+ public:
+  GuardedAsyncInvoker();
+  ~GuardedAsyncInvoker() override;
+
+  // Synchronously execute all outstanding calls we own, and wait for calls to
+  // complete before returning. Optionally filter by message id. The destructor
+  // will not wait for outstanding calls, so if that behavior is desired, call
+  // Flush() first. Returns false if the thread has died.
+  bool Flush(uint32_t id = MQID_ANY);
+
+  // Call |functor| asynchronously with no callback upon completion. Returns
+  // immediately. Returns false if the thread has died.
+  template <class ReturnT, class FunctorT>
+  bool AsyncInvoke(const Location& posted_from,
+                   const FunctorT& functor,
+                   uint32_t id = 0) {
+    rtc::CritScope cs(&crit_);
+    if (thread_ == nullptr)
+      return false;
+    invoker_.AsyncInvoke<ReturnT, FunctorT>(posted_from, thread_, functor, id);
+    return true;
+  }
+
+  // Call |functor| asynchronously with |delay_ms|, with no callback upon
+  // completion. Returns immediately. Returns false if the thread has died.
+  template <class ReturnT, class FunctorT>
+  bool AsyncInvokeDelayed(const Location& posted_from,
+                          const FunctorT& functor,
+                          uint32_t delay_ms,
+                          uint32_t id = 0) {
+    rtc::CritScope cs(&crit_);
+    if (thread_ == nullptr)
+      return false;
+    invoker_.AsyncInvokeDelayed<ReturnT, FunctorT>(posted_from, thread_,
+                                                   functor, delay_ms, id);
+    return true;
+  }
+
+  // Call |functor| asynchronously, calling |callback| when done. Returns false
+  // if the thread has died.
+  template <class ReturnT, class FunctorT, class HostT>
+  bool AsyncInvoke(const Location& posted_from,
+                   const Location& callback_posted_from,
+                   const FunctorT& functor,
+                   void (HostT::*callback)(ReturnT),
+                   HostT* callback_host,
+                   uint32_t id = 0) {
+    rtc::CritScope cs(&crit_);
+    if (thread_ == nullptr)
+      return false;
+    invoker_.AsyncInvoke<ReturnT, FunctorT, HostT>(
+        posted_from, callback_posted_from, thread_, functor, callback,
+        callback_host, id);
+    return true;
+  }
+
+  // Call |functor| asynchronously calling |callback| when done. Overloaded for
+  // void return. Returns false if the thread has died.
+  template <class ReturnT, class FunctorT, class HostT>
+  bool AsyncInvoke(const Location& posted_from,
+                   const Location& callback_posted_from,
+                   const FunctorT& functor,
+                   void (HostT::*callback)(),
+                   HostT* callback_host,
+                   uint32_t id = 0) {
+    rtc::CritScope cs(&crit_);
+    if (thread_ == nullptr)
+      return false;
+    invoker_.AsyncInvoke<ReturnT, FunctorT, HostT>(
+        posted_from, callback_posted_from, thread_, functor, callback,
+        callback_host, id);
+    return true;
+  }
+
+ private:
+  // Callback when |thread_| is destroyed.
+  void ThreadDestroyed();
+
+  CriticalSection crit_;
+  Thread* thread_ GUARDED_BY(crit_);
+  AsyncInvoker invoker_ GUARDED_BY(crit_);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_ASYNCINVOKER_H_
diff --git a/base/asyncpacketsocket.cc b/base/asyncpacketsocket.cc
new file mode 100644
index 0000000..fe36401
--- /dev/null
+++ b/base/asyncpacketsocket.cc
@@ -0,0 +1,29 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/asyncpacketsocket.h"
+
+namespace rtc {
+
+PacketTimeUpdateParams::PacketTimeUpdateParams()
+    : rtp_sendtime_extension_id(-1),
+      srtp_auth_tag_len(-1),
+      srtp_packet_index(-1) {
+}
+
+PacketTimeUpdateParams::~PacketTimeUpdateParams() = default;
+
+AsyncPacketSocket::AsyncPacketSocket() {
+}
+
+AsyncPacketSocket::~AsyncPacketSocket() {
+}
+
+};  // namespace rtc
diff --git a/base/asyncpacketsocket.h b/base/asyncpacketsocket.h
index 809f178..a540947 100644
--- a/base/asyncpacketsocket.h
+++ b/base/asyncpacketsocket.h
@@ -11,9 +11,133 @@
 #ifndef WEBRTC_BASE_ASYNCPACKETSOCKET_H_
 #define WEBRTC_BASE_ASYNCPACKETSOCKET_H_
 
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/dscp.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/socket.h"
+#include "webrtc/base/timeutils.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/asyncpacketsocket.h"
+namespace rtc {
+
+// This structure holds the info needed to update the packet send time header
+// extension, including the information needed to update the authentication tag
+// after changing the value.
+struct PacketTimeUpdateParams {
+  PacketTimeUpdateParams();
+  ~PacketTimeUpdateParams();
+
+  int rtp_sendtime_extension_id;    // extension header id present in packet.
+  std::vector<char> srtp_auth_key;  // Authentication key.
+  int srtp_auth_tag_len;            // Authentication tag length.
+  int64_t srtp_packet_index;        // Required for Rtp Packet authentication.
+};
+
+// This structure holds meta information for the packet which is about to send
+// over network.
+struct PacketOptions {
+  PacketOptions() : dscp(DSCP_NO_CHANGE), packet_id(-1) {}
+  explicit PacketOptions(DiffServCodePoint dscp) : dscp(dscp), packet_id(-1) {}
+
+  DiffServCodePoint dscp;
+  int packet_id;  // 16 bits, -1 represents "not set".
+  PacketTimeUpdateParams packet_time_params;
+};
+
+// This structure will have the information about when packet is actually
+// received by socket.
+struct PacketTime {
+  PacketTime() : timestamp(-1), not_before(-1) {}
+  PacketTime(int64_t timestamp, int64_t not_before)
+      : timestamp(timestamp), not_before(not_before) {}
+
+  int64_t timestamp;   // Receive time after socket delivers the data.
+
+  // Earliest possible time the data could have arrived, indicating the
+  // potential error in the |timestamp| value, in case the system, is busy. For
+  // example, the time of the last select() call.
+  // If unknown, this value will be set to zero.
+  int64_t not_before;
+};
+
+inline PacketTime CreatePacketTime(int64_t not_before) {
+  return PacketTime(TimeMicros(), not_before);
+}
+
+// Provides the ability to receive packets asynchronously. Sends are not
+// buffered since it is acceptable to drop packets under high load.
+class AsyncPacketSocket : public sigslot::has_slots<> {
+ public:
+  enum State {
+    STATE_CLOSED,
+    STATE_BINDING,
+    STATE_BOUND,
+    STATE_CONNECTING,
+    STATE_CONNECTED
+  };
+
+  AsyncPacketSocket();
+  ~AsyncPacketSocket() override;
+
+  // Returns current local address. Address may be set to null if the
+  // socket is not bound yet (GetState() returns STATE_BINDING).
+  virtual SocketAddress GetLocalAddress() const = 0;
+
+  // Returns remote address. Returns zeroes if this is not a client TCP socket.
+  virtual SocketAddress GetRemoteAddress() const = 0;
+
+  // Send a packet.
+  virtual int Send(const void *pv, size_t cb, const PacketOptions& options) = 0;
+  virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr,
+                     const PacketOptions& options) = 0;
+
+  // Close the socket.
+  virtual int Close() = 0;
+
+  // Returns current state of the socket.
+  virtual State GetState() const = 0;
+
+  // Get/set options.
+  virtual int GetOption(Socket::Option opt, int* value) = 0;
+  virtual int SetOption(Socket::Option opt, int value) = 0;
+
+  // Get/Set current error.
+  // TODO: Remove SetError().
+  virtual int GetError() const = 0;
+  virtual void SetError(int error) = 0;
+
+  // Emitted each time a packet is read. Used only for UDP and
+  // connected TCP sockets.
+  sigslot::signal5<AsyncPacketSocket*, const char*, size_t,
+                   const SocketAddress&,
+                   const PacketTime&> SignalReadPacket;
+
+  // Emitted each time a packet is sent.
+  sigslot::signal2<AsyncPacketSocket*, const SentPacket&> SignalSentPacket;
+
+  // Emitted when the socket is currently able to send.
+  sigslot::signal1<AsyncPacketSocket*> SignalReadyToSend;
+
+  // Emitted after address for the socket is allocated, i.e. binding
+  // is finished. State of the socket is changed from BINDING to BOUND
+  // (for UDP and server TCP sockets) or CONNECTING (for client TCP
+  // sockets).
+  sigslot::signal2<AsyncPacketSocket*, const SocketAddress&> SignalAddressReady;
+
+  // Emitted for client TCP sockets when state is changed from
+  // CONNECTING to CONNECTED.
+  sigslot::signal1<AsyncPacketSocket*> SignalConnect;
+
+  // Emitted for client TCP sockets when state is changed from
+  // CONNECTED to CLOSED.
+  sigslot::signal2<AsyncPacketSocket*, int> SignalClose;
+
+  // Used only for listening TCP sockets.
+  sigslot::signal2<AsyncPacketSocket*, AsyncPacketSocket*> SignalNewConnection;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(AsyncPacketSocket);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_ASYNCPACKETSOCKET_H_
diff --git a/base/asyncresolverinterface.cc b/base/asyncresolverinterface.cc
new file mode 100644
index 0000000..7ee02f1
--- /dev/null
+++ b/base/asyncresolverinterface.cc
@@ -0,0 +1,20 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/asyncresolverinterface.h"
+
+namespace rtc {
+
+AsyncResolverInterface::AsyncResolverInterface() {
+}
+
+AsyncResolverInterface::~AsyncResolverInterface() = default;
+
+};  // namespace rtc
diff --git a/base/asyncresolverinterface.h b/base/asyncresolverinterface.h
index b2a172f..75c36ab 100644
--- a/base/asyncresolverinterface.h
+++ b/base/asyncresolverinterface.h
@@ -11,9 +11,37 @@
 #ifndef WEBRTC_BASE_ASYNCRESOLVERINTERFACE_H_
 #define WEBRTC_BASE_ASYNCRESOLVERINTERFACE_H_
 
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/socketaddress.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/asyncresolverinterface.h"
+namespace rtc {
+
+// This interface defines the methods to resolve the address asynchronously.
+class AsyncResolverInterface {
+ public:
+  AsyncResolverInterface();
+  virtual ~AsyncResolverInterface();
+
+  // Start address resolve process.
+  virtual void Start(const SocketAddress& addr) = 0;
+  // Returns top most resolved address of |family|
+  virtual bool GetResolvedAddress(int family, SocketAddress* addr) const = 0;
+  // Returns error from resolver.
+  virtual int GetError() const = 0;
+  // Delete the resolver.
+  virtual void Destroy(bool wait) = 0;
+  // Returns top most resolved IPv4 address if address is resolved successfully.
+  // Otherwise returns address set in SetAddress.
+  SocketAddress address() const {
+    SocketAddress addr;
+    GetResolvedAddress(AF_INET, &addr);
+    return addr;
+  }
+
+  // This signal is fired when address resolve process is completed.
+  sigslot::signal1<AsyncResolverInterface*> SignalDone;
+};
+
+}  // namespace rtc
 
 #endif
diff --git a/base/asyncsocket.cc b/base/asyncsocket.cc
new file mode 100644
index 0000000..c593c4f
--- /dev/null
+++ b/base/asyncsocket.cc
@@ -0,0 +1,127 @@
+/*
+ *  Copyright 2010 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/asyncsocket.h"
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+AsyncSocket::AsyncSocket() {
+}
+
+AsyncSocket::~AsyncSocket() {
+}
+
+AsyncSocketAdapter::AsyncSocketAdapter(AsyncSocket* socket) : socket_(nullptr) {
+  Attach(socket);
+}
+
+AsyncSocketAdapter::~AsyncSocketAdapter() {
+  delete socket_;
+}
+
+void AsyncSocketAdapter::Attach(AsyncSocket* socket) {
+  RTC_DCHECK(!socket_);
+  socket_ = socket;
+  if (socket_) {
+    socket_->SignalConnectEvent.connect(this,
+                                        &AsyncSocketAdapter::OnConnectEvent);
+    socket_->SignalReadEvent.connect(this, &AsyncSocketAdapter::OnReadEvent);
+    socket_->SignalWriteEvent.connect(this, &AsyncSocketAdapter::OnWriteEvent);
+    socket_->SignalCloseEvent.connect(this, &AsyncSocketAdapter::OnCloseEvent);
+  }
+}
+
+SocketAddress AsyncSocketAdapter::GetLocalAddress() const {
+  return socket_->GetLocalAddress();
+}
+
+SocketAddress AsyncSocketAdapter::GetRemoteAddress() const {
+  return socket_->GetRemoteAddress();
+}
+
+int AsyncSocketAdapter::Bind(const SocketAddress& addr) {
+  return socket_->Bind(addr);
+}
+
+int AsyncSocketAdapter::Connect(const SocketAddress& addr) {
+  return socket_->Connect(addr);
+}
+
+int AsyncSocketAdapter::Send(const void* pv, size_t cb) {
+  return socket_->Send(pv, cb);
+}
+
+int AsyncSocketAdapter::SendTo(const void* pv,
+                               size_t cb,
+                               const SocketAddress& addr) {
+  return socket_->SendTo(pv, cb, addr);
+}
+
+int AsyncSocketAdapter::Recv(void* pv, size_t cb, int64_t* timestamp) {
+  return socket_->Recv(pv, cb, timestamp);
+}
+
+int AsyncSocketAdapter::RecvFrom(void* pv,
+                                 size_t cb,
+                                 SocketAddress* paddr,
+                                 int64_t* timestamp) {
+  return socket_->RecvFrom(pv, cb, paddr, timestamp);
+}
+
+int AsyncSocketAdapter::Listen(int backlog) {
+  return socket_->Listen(backlog);
+}
+
+AsyncSocket* AsyncSocketAdapter::Accept(SocketAddress* paddr) {
+  return socket_->Accept(paddr);
+}
+
+int AsyncSocketAdapter::Close() {
+  return socket_->Close();
+}
+
+int AsyncSocketAdapter::GetError() const {
+  return socket_->GetError();
+}
+
+void AsyncSocketAdapter::SetError(int error) {
+  return socket_->SetError(error);
+}
+
+AsyncSocket::ConnState AsyncSocketAdapter::GetState() const {
+  return socket_->GetState();
+}
+
+int AsyncSocketAdapter::GetOption(Option opt, int* value) {
+  return socket_->GetOption(opt, value);
+}
+
+int AsyncSocketAdapter::SetOption(Option opt, int value) {
+  return socket_->SetOption(opt, value);
+}
+
+void AsyncSocketAdapter::OnConnectEvent(AsyncSocket* socket) {
+  SignalConnectEvent(this);
+}
+
+void AsyncSocketAdapter::OnReadEvent(AsyncSocket* socket) {
+  SignalReadEvent(this);
+}
+
+void AsyncSocketAdapter::OnWriteEvent(AsyncSocket* socket) {
+  SignalWriteEvent(this);
+}
+
+void AsyncSocketAdapter::OnCloseEvent(AsyncSocket* socket, int err) {
+  SignalCloseEvent(this, err);
+}
+
+}  // namespace rtc
diff --git a/base/asyncsocket.h b/base/asyncsocket.h
index 9c97139..6dc41b6 100644
--- a/base/asyncsocket.h
+++ b/base/asyncsocket.h
@@ -11,9 +11,73 @@
 #ifndef WEBRTC_BASE_ASYNCSOCKET_H_
 #define WEBRTC_BASE_ASYNCSOCKET_H_
 
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/socket.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/asyncsocket.h"
+namespace rtc {
+
+// TODO: Remove Socket and rename AsyncSocket to Socket.
+
+// Provides the ability to perform socket I/O asynchronously.
+class AsyncSocket : public Socket {
+ public:
+  AsyncSocket();
+  ~AsyncSocket() override;
+
+  AsyncSocket* Accept(SocketAddress* paddr) override = 0;
+
+  // SignalReadEvent and SignalWriteEvent use multi_threaded_local to allow
+  // access concurrently from different thread.
+  // For example SignalReadEvent::connect will be called in AsyncUDPSocket ctor
+  // but at the same time the SocketDispatcher maybe signaling the read event.
+  // ready to read
+  sigslot::signal1<AsyncSocket*,
+                   sigslot::multi_threaded_local> SignalReadEvent;
+  // ready to write
+  sigslot::signal1<AsyncSocket*,
+                   sigslot::multi_threaded_local> SignalWriteEvent;
+  sigslot::signal1<AsyncSocket*> SignalConnectEvent;     // connected
+  sigslot::signal2<AsyncSocket*, int> SignalCloseEvent;  // closed
+};
+
+class AsyncSocketAdapter : public AsyncSocket, public sigslot::has_slots<> {
+ public:
+  // The adapted socket may explicitly be null, and later assigned using Attach.
+  // However, subclasses which support detached mode must override any methods
+  // that will be called during the detached period (usually GetState()), to
+  // avoid dereferencing a null pointer.
+  explicit AsyncSocketAdapter(AsyncSocket* socket);
+  ~AsyncSocketAdapter() override;
+  void Attach(AsyncSocket* socket);
+  SocketAddress GetLocalAddress() const override;
+  SocketAddress GetRemoteAddress() const override;
+  int Bind(const SocketAddress& addr) override;
+  int Connect(const SocketAddress& addr) override;
+  int Send(const void* pv, size_t cb) override;
+  int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override;
+  int Recv(void* pv, size_t cb, int64_t* timestamp) override;
+  int RecvFrom(void* pv,
+               size_t cb,
+               SocketAddress* paddr,
+               int64_t* timestamp) override;
+  int Listen(int backlog) override;
+  AsyncSocket* Accept(SocketAddress* paddr) override;
+  int Close() override;
+  int GetError() const override;
+  void SetError(int error) override;
+  ConnState GetState() const override;
+  int GetOption(Option opt, int* value) override;
+  int SetOption(Option opt, int value) override;
+
+ protected:
+  virtual void OnConnectEvent(AsyncSocket* socket);
+  virtual void OnReadEvent(AsyncSocket* socket);
+  virtual void OnWriteEvent(AsyncSocket* socket);
+  virtual void OnCloseEvent(AsyncSocket* socket, int err);
+
+  AsyncSocket* socket_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_ASYNCSOCKET_H_
diff --git a/base/asynctcpsocket.cc b/base/asynctcpsocket.cc
new file mode 100644
index 0000000..658541c
--- /dev/null
+++ b/base/asynctcpsocket.cc
@@ -0,0 +1,331 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/asynctcpsocket.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+
+#if defined(WEBRTC_POSIX)
+#include <errno.h>
+#endif  // WEBRTC_POSIX
+
+namespace rtc {
+
+static const size_t kMaxPacketSize = 64 * 1024;
+
+typedef uint16_t PacketLength;
+static const size_t kPacketLenSize = sizeof(PacketLength);
+
+static const size_t kBufSize = kMaxPacketSize + kPacketLenSize;
+
+// The input buffer will be resized so that at least kMinimumRecvSize bytes can
+// be received (but it will not grow above the maximum size passed to the
+// constructor).
+static const size_t kMinimumRecvSize = 128;
+
+static const int kListenBacklog = 5;
+
+// Binds and connects |socket|
+AsyncSocket* AsyncTCPSocketBase::ConnectSocket(
+    rtc::AsyncSocket* socket,
+    const rtc::SocketAddress& bind_address,
+    const rtc::SocketAddress& remote_address) {
+  std::unique_ptr<rtc::AsyncSocket> owned_socket(socket);
+  if (socket->Bind(bind_address) < 0) {
+    LOG(LS_ERROR) << "Bind() failed with error " << socket->GetError();
+    return nullptr;
+  }
+  if (socket->Connect(remote_address) < 0) {
+    LOG(LS_ERROR) << "Connect() failed with error " << socket->GetError();
+    return nullptr;
+  }
+  return owned_socket.release();
+}
+
+AsyncTCPSocketBase::AsyncTCPSocketBase(AsyncSocket* socket, bool listen,
+                                       size_t max_packet_size)
+    : socket_(socket),
+      listen_(listen),
+      max_insize_(max_packet_size),
+      max_outsize_(max_packet_size) {
+  if (!listen_) {
+    // Listening sockets don't send/receive data, so they don't need buffers.
+    inbuf_.EnsureCapacity(kMinimumRecvSize);
+  }
+
+  RTC_DCHECK(socket_.get() != nullptr);
+  socket_->SignalConnectEvent.connect(
+      this, &AsyncTCPSocketBase::OnConnectEvent);
+  socket_->SignalReadEvent.connect(this, &AsyncTCPSocketBase::OnReadEvent);
+  socket_->SignalWriteEvent.connect(this, &AsyncTCPSocketBase::OnWriteEvent);
+  socket_->SignalCloseEvent.connect(this, &AsyncTCPSocketBase::OnCloseEvent);
+
+  if (listen_) {
+    if (socket_->Listen(kListenBacklog) < 0) {
+      LOG(LS_ERROR) << "Listen() failed with error " << socket_->GetError();
+    }
+  }
+}
+
+AsyncTCPSocketBase::~AsyncTCPSocketBase() {}
+
+SocketAddress AsyncTCPSocketBase::GetLocalAddress() const {
+  return socket_->GetLocalAddress();
+}
+
+SocketAddress AsyncTCPSocketBase::GetRemoteAddress() const {
+  return socket_->GetRemoteAddress();
+}
+
+int AsyncTCPSocketBase::Close() {
+  return socket_->Close();
+}
+
+AsyncTCPSocket::State AsyncTCPSocketBase::GetState() const {
+  switch (socket_->GetState()) {
+    case Socket::CS_CLOSED:
+      return STATE_CLOSED;
+    case Socket::CS_CONNECTING:
+      if (listen_) {
+        return STATE_BOUND;
+      } else {
+        return STATE_CONNECTING;
+      }
+    case Socket::CS_CONNECTED:
+      return STATE_CONNECTED;
+    default:
+      RTC_NOTREACHED();
+      return STATE_CLOSED;
+  }
+}
+
+int AsyncTCPSocketBase::GetOption(Socket::Option opt, int* value) {
+  return socket_->GetOption(opt, value);
+}
+
+int AsyncTCPSocketBase::SetOption(Socket::Option opt, int value) {
+  return socket_->SetOption(opt, value);
+}
+
+int AsyncTCPSocketBase::GetError() const {
+  return socket_->GetError();
+}
+
+void AsyncTCPSocketBase::SetError(int error) {
+  return socket_->SetError(error);
+}
+
+int AsyncTCPSocketBase::SendTo(const void *pv, size_t cb,
+                               const SocketAddress& addr,
+                               const rtc::PacketOptions& options) {
+  const SocketAddress& remote_address = GetRemoteAddress();
+  if (addr == remote_address)
+    return Send(pv, cb, options);
+  // Remote address may be empty if there is a sudden network change.
+  RTC_DCHECK(remote_address.IsNil());
+  socket_->SetError(ENOTCONN);
+  return -1;
+}
+
+int AsyncTCPSocketBase::SendRaw(const void * pv, size_t cb) {
+  if (outbuf_.size() + cb > max_outsize_) {
+    socket_->SetError(EMSGSIZE);
+    return -1;
+  }
+
+  RTC_DCHECK(!listen_);
+  outbuf_.AppendData(static_cast<const uint8_t*>(pv), cb);
+
+  return FlushOutBuffer();
+}
+
+int AsyncTCPSocketBase::FlushOutBuffer() {
+  RTC_DCHECK(!listen_);
+  int res = socket_->Send(outbuf_.data(), outbuf_.size());
+  if (res <= 0) {
+    return res;
+  }
+  if (static_cast<size_t>(res) > outbuf_.size()) {
+    RTC_NOTREACHED();
+    return -1;
+  }
+  size_t new_size = outbuf_.size() - res;
+  if (new_size > 0) {
+    memmove(outbuf_.data(), outbuf_.data() + res, new_size);
+  }
+  outbuf_.SetSize(new_size);
+  return res;
+}
+
+void AsyncTCPSocketBase::AppendToOutBuffer(const void* pv, size_t cb) {
+  RTC_DCHECK(outbuf_.size() + cb <= max_outsize_);
+  RTC_DCHECK(!listen_);
+  outbuf_.AppendData(static_cast<const uint8_t*>(pv), cb);
+}
+
+void AsyncTCPSocketBase::OnConnectEvent(AsyncSocket* socket) {
+  SignalConnect(this);
+}
+
+void AsyncTCPSocketBase::OnReadEvent(AsyncSocket* socket) {
+  RTC_DCHECK(socket_.get() == socket);
+
+  if (listen_) {
+    rtc::SocketAddress address;
+    rtc::AsyncSocket* new_socket = socket->Accept(&address);
+    if (!new_socket) {
+      // TODO(stefan): Do something better like forwarding the error
+      // to the user.
+      LOG(LS_ERROR) << "TCP accept failed with error " << socket_->GetError();
+      return;
+    }
+
+    HandleIncomingConnection(new_socket);
+
+    // Prime a read event in case data is waiting.
+    new_socket->SignalReadEvent(new_socket);
+  } else {
+    size_t total_recv = 0;
+    while (true) {
+      size_t free_size = inbuf_.capacity() - inbuf_.size();
+      if (free_size < kMinimumRecvSize && inbuf_.capacity() < max_insize_) {
+        inbuf_.EnsureCapacity(std::min(max_insize_, inbuf_.capacity() * 2));
+        free_size = inbuf_.capacity() - inbuf_.size();
+      }
+
+      int len =
+          socket_->Recv(inbuf_.data() + inbuf_.size(), free_size, nullptr);
+      if (len < 0) {
+        // TODO(stefan): Do something better like forwarding the error to the
+        // user.
+        if (!socket_->IsBlocking()) {
+          LOG(LS_ERROR) << "Recv() returned error: " << socket_->GetError();
+        }
+        break;
+      }
+
+      total_recv += len;
+      inbuf_.SetSize(inbuf_.size() + len);
+      if (!len || static_cast<size_t>(len) < free_size) {
+        break;
+      }
+    }
+
+    if (!total_recv) {
+      return;
+    }
+
+    size_t size = inbuf_.size();
+    ProcessInput(inbuf_.data<char>(), &size);
+
+    if (size > inbuf_.size()) {
+      LOG(LS_ERROR) << "input buffer overflow";
+      RTC_NOTREACHED();
+      inbuf_.Clear();
+    } else {
+      inbuf_.SetSize(size);
+    }
+  }
+}
+
+void AsyncTCPSocketBase::OnWriteEvent(AsyncSocket* socket) {
+  RTC_DCHECK(socket_.get() == socket);
+
+  if (outbuf_.size() > 0) {
+    FlushOutBuffer();
+  }
+
+  if (outbuf_.size() == 0) {
+    SignalReadyToSend(this);
+  }
+}
+
+void AsyncTCPSocketBase::OnCloseEvent(AsyncSocket* socket, int error) {
+  SignalClose(this, error);
+}
+
+// AsyncTCPSocket
+// Binds and connects |socket| and creates AsyncTCPSocket for
+// it. Takes ownership of |socket|. Returns null if bind() or
+// connect() fail (|socket| is destroyed in that case).
+AsyncTCPSocket* AsyncTCPSocket::Create(
+    AsyncSocket* socket,
+    const SocketAddress& bind_address,
+    const SocketAddress& remote_address) {
+  return new AsyncTCPSocket(AsyncTCPSocketBase::ConnectSocket(
+      socket, bind_address, remote_address), false);
+}
+
+AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket, bool listen)
+    : AsyncTCPSocketBase(socket, listen, kBufSize) {
+}
+
+int AsyncTCPSocket::Send(const void *pv, size_t cb,
+                         const rtc::PacketOptions& options) {
+  if (cb > kBufSize) {
+    SetError(EMSGSIZE);
+    return -1;
+  }
+
+  // If we are blocking on send, then silently drop this packet
+  if (!IsOutBufferEmpty())
+    return static_cast<int>(cb);
+
+  PacketLength pkt_len = HostToNetwork16(static_cast<PacketLength>(cb));
+  AppendToOutBuffer(&pkt_len, kPacketLenSize);
+  AppendToOutBuffer(pv, cb);
+
+  int res = FlushOutBuffer();
+  if (res <= 0) {
+    // drop packet if we made no progress
+    ClearOutBuffer();
+    return res;
+  }
+
+  rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
+  SignalSentPacket(this, sent_packet);
+
+  // We claim to have sent the whole thing, even if we only sent partial
+  return static_cast<int>(cb);
+}
+
+void AsyncTCPSocket::ProcessInput(char * data, size_t* len) {
+  SocketAddress remote_addr(GetRemoteAddress());
+
+  while (true) {
+    if (*len < kPacketLenSize)
+      return;
+
+    PacketLength pkt_len = rtc::GetBE16(data);
+    if (*len < kPacketLenSize + pkt_len)
+      return;
+
+    SignalReadPacket(this, data + kPacketLenSize, pkt_len, remote_addr,
+                     CreatePacketTime(0));
+
+    *len -= kPacketLenSize + pkt_len;
+    if (*len > 0) {
+      memmove(data, data + kPacketLenSize + pkt_len, *len);
+    }
+  }
+}
+
+void AsyncTCPSocket::HandleIncomingConnection(AsyncSocket* socket) {
+  SignalNewConnection(this, new AsyncTCPSocket(socket, false));
+}
+
+}  // namespace rtc
diff --git a/base/asynctcpsocket.h b/base/asynctcpsocket.h
index d64927b..2e4ff9a 100644
--- a/base/asynctcpsocket.h
+++ b/base/asynctcpsocket.h
@@ -11,9 +11,98 @@
 #ifndef WEBRTC_BASE_ASYNCTCPSOCKET_H_
 #define WEBRTC_BASE_ASYNCTCPSOCKET_H_
 
+#include <memory>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/asynctcpsocket.h"
+#include "webrtc/base/asyncpacketsocket.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/socketfactory.h"
+
+namespace rtc {
+
+// Simulates UDP semantics over TCP.  Send and Recv packet sizes
+// are preserved, and drops packets silently on Send, rather than
+// buffer them in user space.
+class AsyncTCPSocketBase : public AsyncPacketSocket {
+ public:
+  AsyncTCPSocketBase(AsyncSocket* socket, bool listen, size_t max_packet_size);
+  ~AsyncTCPSocketBase() override;
+
+  // Pure virtual methods to send and recv data.
+  int Send(const void *pv, size_t cb,
+                   const rtc::PacketOptions& options) override = 0;
+  virtual void ProcessInput(char* data, size_t* len) = 0;
+  // Signals incoming connection.
+  virtual void HandleIncomingConnection(AsyncSocket* socket) = 0;
+
+  SocketAddress GetLocalAddress() const override;
+  SocketAddress GetRemoteAddress() const override;
+  int SendTo(const void* pv,
+             size_t cb,
+             const SocketAddress& addr,
+             const rtc::PacketOptions& options) override;
+  int Close() override;
+
+  State GetState() const override;
+  int GetOption(Socket::Option opt, int* value) override;
+  int SetOption(Socket::Option opt, int value) override;
+  int GetError() const override;
+  void SetError(int error) override;
+
+ protected:
+  // Binds and connects |socket| and creates AsyncTCPSocket for
+  // it. Takes ownership of |socket|. Returns null if bind() or
+  // connect() fail (|socket| is destroyed in that case).
+  static AsyncSocket* ConnectSocket(AsyncSocket* socket,
+                                    const SocketAddress& bind_address,
+                                    const SocketAddress& remote_address);
+  virtual int SendRaw(const void* pv, size_t cb);
+  int FlushOutBuffer();
+  // Add data to |outbuf_|.
+  void AppendToOutBuffer(const void* pv, size_t cb);
+
+  // Helper methods for |outpos_|.
+  bool IsOutBufferEmpty() const { return outbuf_.size() == 0; }
+  void ClearOutBuffer() { outbuf_.Clear(); }
+
+ private:
+  // Called by the underlying socket
+  void OnConnectEvent(AsyncSocket* socket);
+  void OnReadEvent(AsyncSocket* socket);
+  void OnWriteEvent(AsyncSocket* socket);
+  void OnCloseEvent(AsyncSocket* socket, int error);
+
+  std::unique_ptr<AsyncSocket> socket_;
+  bool listen_;
+  Buffer inbuf_;
+  Buffer outbuf_;
+  size_t max_insize_;
+  size_t max_outsize_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(AsyncTCPSocketBase);
+};
+
+class AsyncTCPSocket : public AsyncTCPSocketBase {
+ public:
+  // Binds and connects |socket| and creates AsyncTCPSocket for
+  // it. Takes ownership of |socket|. Returns null if bind() or
+  // connect() fail (|socket| is destroyed in that case).
+  static AsyncTCPSocket* Create(AsyncSocket* socket,
+                                const SocketAddress& bind_address,
+                                const SocketAddress& remote_address);
+  AsyncTCPSocket(AsyncSocket* socket, bool listen);
+  ~AsyncTCPSocket() override {}
+
+  int Send(const void* pv,
+           size_t cb,
+           const rtc::PacketOptions& options) override;
+  void ProcessInput(char* data, size_t* len) override;
+  void HandleIncomingConnection(AsyncSocket* socket) override;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(AsyncTCPSocket);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_ASYNCTCPSOCKET_H_
diff --git a/base/asynctcpsocket_unittest.cc b/base/asynctcpsocket_unittest.cc
new file mode 100644
index 0000000..bd7dcfb
--- /dev/null
+++ b/base/asynctcpsocket_unittest.cc
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <string>
+
+#include "webrtc/base/asynctcpsocket.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/virtualsocketserver.h"
+
+namespace rtc {
+
+class AsyncTCPSocketTest
+    : public testing::Test,
+      public sigslot::has_slots<> {
+ public:
+  AsyncTCPSocketTest()
+      : vss_(new rtc::VirtualSocketServer()),
+        socket_(vss_->CreateAsyncSocket(SOCK_STREAM)),
+        tcp_socket_(new AsyncTCPSocket(socket_, true)),
+        ready_to_send_(false) {
+    tcp_socket_->SignalReadyToSend.connect(this,
+                                           &AsyncTCPSocketTest::OnReadyToSend);
+  }
+
+  void OnReadyToSend(rtc::AsyncPacketSocket* socket) {
+    ready_to_send_ = true;
+  }
+
+ protected:
+  std::unique_ptr<VirtualSocketServer> vss_;
+  AsyncSocket* socket_;
+  std::unique_ptr<AsyncTCPSocket> tcp_socket_;
+  bool ready_to_send_;
+};
+
+TEST_F(AsyncTCPSocketTest, OnWriteEvent) {
+  EXPECT_FALSE(ready_to_send_);
+  socket_->SignalWriteEvent(socket_);
+  EXPECT_TRUE(ready_to_send_);
+}
+
+}  // namespace rtc
diff --git a/base/asyncudpsocket.cc b/base/asyncudpsocket.cc
new file mode 100644
index 0000000..0bccb73
--- /dev/null
+++ b/base/asyncudpsocket.cc
@@ -0,0 +1,130 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/asyncudpsocket.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+
+namespace rtc {
+
+static const int BUF_SIZE = 64 * 1024;
+
+AsyncUDPSocket* AsyncUDPSocket::Create(
+    AsyncSocket* socket,
+    const SocketAddress& bind_address) {
+  std::unique_ptr<AsyncSocket> owned_socket(socket);
+  if (socket->Bind(bind_address) < 0) {
+    LOG(LS_ERROR) << "Bind() failed with error " << socket->GetError();
+    return nullptr;
+  }
+  return new AsyncUDPSocket(owned_socket.release());
+}
+
+AsyncUDPSocket* AsyncUDPSocket::Create(SocketFactory* factory,
+                                       const SocketAddress& bind_address) {
+  AsyncSocket* socket =
+      factory->CreateAsyncSocket(bind_address.family(), SOCK_DGRAM);
+  if (!socket)
+    return nullptr;
+  return Create(socket, bind_address);
+}
+
+AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket)
+    : socket_(socket) {
+  size_ = BUF_SIZE;
+  buf_ = new char[size_];
+
+  // The socket should start out readable but not writable.
+  socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent);
+  socket_->SignalWriteEvent.connect(this, &AsyncUDPSocket::OnWriteEvent);
+}
+
+AsyncUDPSocket::~AsyncUDPSocket() {
+  delete [] buf_;
+}
+
+SocketAddress AsyncUDPSocket::GetLocalAddress() const {
+  return socket_->GetLocalAddress();
+}
+
+SocketAddress AsyncUDPSocket::GetRemoteAddress() const {
+  return socket_->GetRemoteAddress();
+}
+
+int AsyncUDPSocket::Send(const void *pv, size_t cb,
+                         const rtc::PacketOptions& options) {
+  rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
+  int ret = socket_->Send(pv, cb);
+  SignalSentPacket(this, sent_packet);
+  return ret;
+}
+
+int AsyncUDPSocket::SendTo(const void *pv, size_t cb,
+                           const SocketAddress& addr,
+                           const rtc::PacketOptions& options) {
+  rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
+  int ret = socket_->SendTo(pv, cb, addr);
+  SignalSentPacket(this, sent_packet);
+  return ret;
+}
+
+int AsyncUDPSocket::Close() {
+  return socket_->Close();
+}
+
+AsyncUDPSocket::State AsyncUDPSocket::GetState() const {
+  return STATE_BOUND;
+}
+
+int AsyncUDPSocket::GetOption(Socket::Option opt, int* value) {
+  return socket_->GetOption(opt, value);
+}
+
+int AsyncUDPSocket::SetOption(Socket::Option opt, int value) {
+  return socket_->SetOption(opt, value);
+}
+
+int AsyncUDPSocket::GetError() const {
+  return socket_->GetError();
+}
+
+void AsyncUDPSocket::SetError(int error) {
+  return socket_->SetError(error);
+}
+
+void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) {
+  RTC_DCHECK(socket_.get() == socket);
+
+  SocketAddress remote_addr;
+  int64_t timestamp;
+  int len = socket_->RecvFrom(buf_, size_, &remote_addr, &timestamp);
+  if (len < 0) {
+    // An error here typically means we got an ICMP error in response to our
+    // send datagram, indicating the remote address was unreachable.
+    // When doing ICE, this kind of thing will often happen.
+    // TODO: Do something better like forwarding the error to the user.
+    SocketAddress local_addr = socket_->GetLocalAddress();
+    LOG(LS_INFO) << "AsyncUDPSocket[" << local_addr.ToSensitiveString() << "] "
+                 << "receive failed with error " << socket_->GetError();
+    return;
+  }
+
+  // TODO: Make sure that we got all of the packet.
+  // If we did not, then we should resize our buffer to be large enough.
+  SignalReadPacket(
+      this, buf_, static_cast<size_t>(len), remote_addr,
+      (timestamp > -1 ? PacketTime(timestamp, 0) : CreatePacketTime(0)));
+}
+
+void AsyncUDPSocket::OnWriteEvent(AsyncSocket* socket) {
+  SignalReadyToSend(this);
+}
+
+}  // namespace rtc
diff --git a/base/asyncudpsocket.h b/base/asyncudpsocket.h
index c3212c0..e5535e0 100644
--- a/base/asyncudpsocket.h
+++ b/base/asyncudpsocket.h
@@ -11,9 +11,57 @@
 #ifndef WEBRTC_BASE_ASYNCUDPSOCKET_H_
 #define WEBRTC_BASE_ASYNCUDPSOCKET_H_
 
+#include <memory>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/asyncudpsocket.h"
+#include "webrtc/base/asyncpacketsocket.h"
+#include "webrtc/base/socketfactory.h"
+
+namespace rtc {
+
+// Provides the ability to receive packets asynchronously.  Sends are not
+// buffered since it is acceptable to drop packets under high load.
+class AsyncUDPSocket : public AsyncPacketSocket {
+ public:
+  // Binds |socket| and creates AsyncUDPSocket for it. Takes ownership
+  // of |socket|. Returns null if bind() fails (|socket| is destroyed
+  // in that case).
+  static AsyncUDPSocket* Create(AsyncSocket* socket,
+                                const SocketAddress& bind_address);
+  // Creates a new socket for sending asynchronous UDP packets using an
+  // asynchronous socket from the given factory.
+  static AsyncUDPSocket* Create(SocketFactory* factory,
+                                const SocketAddress& bind_address);
+  explicit AsyncUDPSocket(AsyncSocket* socket);
+  ~AsyncUDPSocket() override;
+
+  SocketAddress GetLocalAddress() const override;
+  SocketAddress GetRemoteAddress() const override;
+  int Send(const void* pv,
+           size_t cb,
+           const rtc::PacketOptions& options) override;
+  int SendTo(const void* pv,
+             size_t cb,
+             const SocketAddress& addr,
+             const rtc::PacketOptions& options) override;
+  int Close() override;
+
+  State GetState() const override;
+  int GetOption(Socket::Option opt, int* value) override;
+  int SetOption(Socket::Option opt, int value) override;
+  int GetError() const override;
+  void SetError(int error) override;
+
+ private:
+  // Called when the underlying socket is ready to be read from.
+  void OnReadEvent(AsyncSocket* socket);
+  // Called when the underlying socket is ready to send.
+  void OnWriteEvent(AsyncSocket* socket);
+
+  std::unique_ptr<AsyncSocket> socket_;
+  char* buf_;
+  size_t size_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_ASYNCUDPSOCKET_H_
diff --git a/base/asyncudpsocket_unittest.cc b/base/asyncudpsocket_unittest.cc
new file mode 100644
index 0000000..9922005
--- /dev/null
+++ b/base/asyncudpsocket_unittest.cc
@@ -0,0 +1,53 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <string>
+
+#include "webrtc/base/asyncudpsocket.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/virtualsocketserver.h"
+
+namespace rtc {
+
+class AsyncUdpSocketTest
+    : public testing::Test,
+      public sigslot::has_slots<> {
+ public:
+  AsyncUdpSocketTest()
+      : pss_(new rtc::PhysicalSocketServer),
+        vss_(new rtc::VirtualSocketServer(pss_.get())),
+        socket_(vss_->CreateAsyncSocket(SOCK_DGRAM)),
+        udp_socket_(new AsyncUDPSocket(socket_)),
+        ready_to_send_(false) {
+    udp_socket_->SignalReadyToSend.connect(this,
+                                           &AsyncUdpSocketTest::OnReadyToSend);
+  }
+
+  void OnReadyToSend(rtc::AsyncPacketSocket* socket) {
+    ready_to_send_ = true;
+  }
+
+ protected:
+  std::unique_ptr<PhysicalSocketServer> pss_;
+  std::unique_ptr<VirtualSocketServer> vss_;
+  AsyncSocket* socket_;
+  std::unique_ptr<AsyncUDPSocket> udp_socket_;
+  bool ready_to_send_;
+};
+
+TEST_F(AsyncUdpSocketTest, OnWriteEvent) {
+  EXPECT_FALSE(ready_to_send_);
+  socket_->SignalWriteEvent(socket_);
+  EXPECT_TRUE(ready_to_send_);
+}
+
+}  // namespace rtc
diff --git a/base/atomicops.h b/base/atomicops.h
index 3c36848..a286bf0 100644
--- a/base/atomicops.h
+++ b/base/atomicops.h
@@ -11,9 +11,77 @@
 #ifndef WEBRTC_BASE_ATOMICOPS_H_
 #define WEBRTC_BASE_ATOMICOPS_H_
 
+#if defined(WEBRTC_WIN)
+// Include winsock2.h before including <windows.h> to maintain consistency with
+// win32.h.  We can't include win32.h directly here since it pulls in
+// headers such as basictypes.h which causes problems in Chromium where webrtc
+// exists as two separate projects, webrtc and libjingle.
+#include <winsock2.h>
+#include <windows.h>
+#endif  // defined(WEBRTC_WIN)
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/atomicops.h"
+namespace rtc {
+class AtomicOps {
+ public:
+#if defined(WEBRTC_WIN)
+  // Assumes sizeof(int) == sizeof(LONG), which it is on Win32 and Win64.
+  static int Increment(volatile int* i) {
+    return ::InterlockedIncrement(reinterpret_cast<volatile LONG*>(i));
+  }
+  static int Decrement(volatile int* i) {
+    return ::InterlockedDecrement(reinterpret_cast<volatile LONG*>(i));
+  }
+  static int AcquireLoad(volatile const int* i) {
+    return *i;
+  }
+  static void ReleaseStore(volatile int* i, int value) {
+    *i = value;
+  }
+  static int CompareAndSwap(volatile int* i, int old_value, int new_value) {
+    return ::InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(i),
+                                        new_value,
+                                        old_value);
+  }
+  // Pointer variants.
+  template <typename T>
+  static T* AcquireLoadPtr(T* volatile* ptr) {
+    return *ptr;
+  }
+  template <typename T>
+  static T* CompareAndSwapPtr(T* volatile* ptr, T* old_value, T* new_value) {
+    return static_cast<T*>(::InterlockedCompareExchangePointer(
+        reinterpret_cast<PVOID volatile*>(ptr), new_value, old_value));
+  }
+#else
+  static int Increment(volatile int* i) {
+    return __sync_add_and_fetch(i, 1);
+  }
+  static int Decrement(volatile int* i) {
+    return __sync_sub_and_fetch(i, 1);
+  }
+  static int AcquireLoad(volatile const int* i) {
+    return __atomic_load_n(i, __ATOMIC_ACQUIRE);
+  }
+  static void ReleaseStore(volatile int* i, int value) {
+    __atomic_store_n(i, value, __ATOMIC_RELEASE);
+  }
+  static int CompareAndSwap(volatile int* i, int old_value, int new_value) {
+    return __sync_val_compare_and_swap(i, old_value, new_value);
+  }
+  // Pointer variants.
+  template <typename T>
+  static T* AcquireLoadPtr(T* volatile* ptr) {
+    return __atomic_load_n(ptr, __ATOMIC_ACQUIRE);
+  }
+  template <typename T>
+  static T* CompareAndSwapPtr(T* volatile* ptr, T* old_value, T* new_value) {
+    return __sync_val_compare_and_swap(ptr, old_value, new_value);
+  }
+#endif
+};
+
+
+
+}
 
 #endif  // WEBRTC_BASE_ATOMICOPS_H_
diff --git a/base/atomicops_unittest.cc b/base/atomicops_unittest.cc
new file mode 100644
index 0000000..2aa28ca
--- /dev/null
+++ b/base/atomicops_unittest.cc
@@ -0,0 +1,12 @@
+/*
+ *  Copyright 2011 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+// TODO(pbos): Move AtomicOps tests to here from
+// webrtc/base/criticalsection_unittest.cc.
diff --git a/base/base64.cc b/base/base64.cc
new file mode 100644
index 0000000..43b6325
--- /dev/null
+++ b/base/base64.cc
@@ -0,0 +1,278 @@
+
+//*********************************************************************
+//* Base64 - a simple base64 encoder and decoder.
+//*
+//*     Copyright (c) 1999, Bob Withers - bwit@pobox.com
+//*
+//* This code may be freely used for any purpose, either personal
+//* or commercial, provided the authors copyright notice remains
+//* intact.
+//*
+//* Enhancements by Stanley Yamane:
+//*     o reverse lookup table for the decode function
+//*     o reserve string buffer space in advance
+//*
+//*********************************************************************
+
+#include "webrtc/base/base64.h"
+
+#include <string.h>
+
+#include "webrtc/base/checks.h"
+
+using std::vector;
+
+namespace rtc {
+
+static const char kPad = '=';
+static const unsigned char pd = 0xFD;  // Padding
+static const unsigned char sp = 0xFE;  // Whitespace
+static const unsigned char il = 0xFF;  // Illegal base64 character
+
+const char Base64::Base64Table[] =
+    // 0000000000111111111122222222223333333333444444444455555555556666
+    // 0123456789012345678901234567890123456789012345678901234567890123
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+// Decode Table gives the index of any valid base64 character in the
+// Base64 table
+// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == /
+
+const unsigned char Base64::DecodeTable[] = {
+    // 0  1  2  3  4  5  6  7  8  9
+    il, il, il, il, il, il, il, il, il, sp,  //   0 -   9
+    sp, sp, sp, sp, il, il, il, il, il, il,  //  10 -  19
+    il, il, il, il, il, il, il, il, il, il,  //  20 -  29
+    il, il, sp, il, il, il, il, il, il, il,  //  30 -  39
+    il, il, il, 62, il, il, il, 63, 52, 53,  //  40 -  49
+    54, 55, 56, 57, 58, 59, 60, 61, il, il,  //  50 -  59
+    il, pd, il, il, il, 0,  1,  2,  3,  4,   //  60 -  69
+    5,  6,  7,  8,  9,  10, 11, 12, 13, 14,  //  70 -  79
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24,  //  80 -  89
+    25, il, il, il, il, il, il, 26, 27, 28,  //  90 -  99
+    29, 30, 31, 32, 33, 34, 35, 36, 37, 38,  // 100 - 109
+    39, 40, 41, 42, 43, 44, 45, 46, 47, 48,  // 110 - 119
+    49, 50, 51, il, il, il, il, il, il, il,  // 120 - 129
+    il, il, il, il, il, il, il, il, il, il,  // 130 - 139
+    il, il, il, il, il, il, il, il, il, il,  // 140 - 149
+    il, il, il, il, il, il, il, il, il, il,  // 150 - 159
+    il, il, il, il, il, il, il, il, il, il,  // 160 - 169
+    il, il, il, il, il, il, il, il, il, il,  // 170 - 179
+    il, il, il, il, il, il, il, il, il, il,  // 180 - 189
+    il, il, il, il, il, il, il, il, il, il,  // 190 - 199
+    il, il, il, il, il, il, il, il, il, il,  // 200 - 209
+    il, il, il, il, il, il, il, il, il, il,  // 210 - 219
+    il, il, il, il, il, il, il, il, il, il,  // 220 - 229
+    il, il, il, il, il, il, il, il, il, il,  // 230 - 239
+    il, il, il, il, il, il, il, il, il, il,  // 240 - 249
+    il, il, il, il, il, il                   // 250 - 255
+};
+
+bool Base64::IsBase64Char(char ch) {
+  return (('A' <= ch) && (ch <= 'Z')) || (('a' <= ch) && (ch <= 'z')) ||
+         (('0' <= ch) && (ch <= '9')) || (ch == '+') || (ch == '/');
+}
+
+bool Base64::GetNextBase64Char(char ch, char* next_ch) {
+  if (next_ch == nullptr) {
+    return false;
+  }
+  const char* p = strchr(Base64Table, ch);
+  if (!p)
+    return false;
+  ++p;
+  *next_ch = (*p) ? *p : Base64Table[0];
+  return true;
+}
+
+bool Base64::IsBase64Encoded(const std::string& str) {
+  for (size_t i = 0; i < str.size(); ++i) {
+    if (!IsBase64Char(str.at(i)))
+      return false;
+  }
+  return true;
+}
+
+void Base64::EncodeFromArray(const void* data,
+                             size_t len,
+                             std::string* result) {
+  RTC_DCHECK(nullptr != result);
+  result->clear();
+  result->resize(((len + 2) / 3) * 4);
+  const unsigned char* byte_data = static_cast<const unsigned char*>(data);
+
+  unsigned char c;
+  size_t i = 0;
+  size_t dest_ix = 0;
+  while (i < len) {
+    c = (byte_data[i] >> 2) & 0x3f;
+    (*result)[dest_ix++] = Base64Table[c];
+
+    c = (byte_data[i] << 4) & 0x3f;
+    if (++i < len) {
+      c |= (byte_data[i] >> 4) & 0x0f;
+    }
+    (*result)[dest_ix++] = Base64Table[c];
+
+    if (i < len) {
+      c = (byte_data[i] << 2) & 0x3f;
+      if (++i < len) {
+        c |= (byte_data[i] >> 6) & 0x03;
+      }
+      (*result)[dest_ix++] = Base64Table[c];
+    } else {
+      (*result)[dest_ix++] = kPad;
+    }
+
+    if (i < len) {
+      c = byte_data[i] & 0x3f;
+      (*result)[dest_ix++] = Base64Table[c];
+      ++i;
+    } else {
+      (*result)[dest_ix++] = kPad;
+    }
+  }
+}
+
+size_t Base64::GetNextQuantum(DecodeFlags parse_flags,
+                              bool illegal_pads,
+                              const char* data,
+                              size_t len,
+                              size_t* dpos,
+                              unsigned char qbuf[4],
+                              bool* padded) {
+  size_t byte_len = 0, pad_len = 0, pad_start = 0;
+  for (; (byte_len < 4) && (*dpos < len); ++*dpos) {
+    qbuf[byte_len] = DecodeTable[static_cast<unsigned char>(data[*dpos])];
+    if ((il == qbuf[byte_len]) || (illegal_pads && (pd == qbuf[byte_len]))) {
+      if (parse_flags != DO_PARSE_ANY)
+        break;
+      // Ignore illegal characters
+    } else if (sp == qbuf[byte_len]) {
+      if (parse_flags == DO_PARSE_STRICT)
+        break;
+      // Ignore spaces
+    } else if (pd == qbuf[byte_len]) {
+      if (byte_len < 2) {
+        if (parse_flags != DO_PARSE_ANY)
+          break;
+        // Ignore unexpected padding
+      } else if (byte_len + pad_len >= 4) {
+        if (parse_flags != DO_PARSE_ANY)
+          break;
+        // Ignore extra pads
+      } else {
+        if (1 == ++pad_len) {
+          pad_start = *dpos;
+        }
+      }
+    } else {
+      if (pad_len > 0) {
+        if (parse_flags != DO_PARSE_ANY)
+          break;
+        // Ignore pads which are followed by data
+        pad_len = 0;
+      }
+      ++byte_len;
+    }
+  }
+  for (size_t i = byte_len; i < 4; ++i) {
+    qbuf[i] = 0;
+  }
+  if (4 == byte_len + pad_len) {
+    *padded = true;
+  } else {
+    *padded = false;
+    if (pad_len) {
+      // Roll back illegal padding
+      *dpos = pad_start;
+    }
+  }
+  return byte_len;
+}
+
+bool Base64::DecodeFromArray(const char* data,
+                             size_t len,
+                             DecodeFlags flags,
+                             std::string* result,
+                             size_t* data_used) {
+  return DecodeFromArrayTemplate<std::string>(data, len, flags, result,
+                                              data_used);
+}
+
+bool Base64::DecodeFromArray(const char* data,
+                             size_t len,
+                             DecodeFlags flags,
+                             vector<char>* result,
+                             size_t* data_used) {
+  return DecodeFromArrayTemplate<vector<char>>(data, len, flags, result,
+                                               data_used);
+}
+
+bool Base64::DecodeFromArray(const char* data,
+                             size_t len,
+                             DecodeFlags flags,
+                             vector<uint8_t>* result,
+                             size_t* data_used) {
+  return DecodeFromArrayTemplate<vector<uint8_t>>(data, len, flags, result,
+                                                  data_used);
+}
+
+template <typename T>
+bool Base64::DecodeFromArrayTemplate(const char* data,
+                                     size_t len,
+                                     DecodeFlags flags,
+                                     T* result,
+                                     size_t* data_used) {
+  RTC_DCHECK(nullptr != result);
+  RTC_DCHECK(flags <= (DO_PARSE_MASK | DO_PAD_MASK | DO_TERM_MASK));
+
+  const DecodeFlags parse_flags = flags & DO_PARSE_MASK;
+  const DecodeFlags pad_flags = flags & DO_PAD_MASK;
+  const DecodeFlags term_flags = flags & DO_TERM_MASK;
+  RTC_DCHECK(0 != parse_flags);
+  RTC_DCHECK(0 != pad_flags);
+  RTC_DCHECK(0 != term_flags);
+
+  result->clear();
+  result->reserve(len);
+
+  size_t dpos = 0;
+  bool success = true, padded;
+  unsigned char c, qbuf[4];
+  while (dpos < len) {
+    size_t qlen = GetNextQuantum(parse_flags, (DO_PAD_NO == pad_flags), data,
+                                 len, &dpos, qbuf, &padded);
+    c = (qbuf[0] << 2) | ((qbuf[1] >> 4) & 0x3);
+    if (qlen >= 2) {
+      result->push_back(c);
+      c = ((qbuf[1] << 4) & 0xf0) | ((qbuf[2] >> 2) & 0xf);
+      if (qlen >= 3) {
+        result->push_back(c);
+        c = ((qbuf[2] << 6) & 0xc0) | qbuf[3];
+        if (qlen >= 4) {
+          result->push_back(c);
+          c = 0;
+        }
+      }
+    }
+    if (qlen < 4) {
+      if ((DO_TERM_ANY != term_flags) && (0 != c)) {
+        success = false;  // unused bits
+      }
+      if ((DO_PAD_YES == pad_flags) && !padded) {
+        success = false;  // expected padding
+      }
+      break;
+    }
+  }
+  if ((DO_TERM_BUFFER == term_flags) && (dpos != len)) {
+    success = false;  // unused chars
+  }
+  if (data_used) {
+    *data_used = dpos;
+  }
+  return success;
+}
+
+}  // namespace rtc
diff --git a/base/base64.h b/base/base64.h
index 1e28357..eba3cc0 100644
--- a/base/base64.h
+++ b/base/base64.h
@@ -9,12 +9,115 @@
 //* intact.
 //*********************************************************************
 
-#ifndef WEBRTC_BASE_BASE64_H_
-#define WEBRTC_BASE_BASE64_H_
+#ifndef WEBRTC_BASE_BASE64_H__
+#define WEBRTC_BASE_BASE64_H__
 
+#include <string>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/base64.h"
+namespace rtc {
 
-#endif  // WEBRTC_BASE_BASE64_H_
+class Base64 {
+ public:
+  enum DecodeOption {
+    DO_PARSE_STRICT = 1,  // Parse only base64 characters
+    DO_PARSE_WHITE = 2,   // Parse only base64 and whitespace characters
+    DO_PARSE_ANY = 3,     // Parse all characters
+    DO_PARSE_MASK = 3,
+
+    DO_PAD_YES = 4,  // Padding is required
+    DO_PAD_ANY = 8,  // Padding is optional
+    DO_PAD_NO = 12,  // Padding is disallowed
+    DO_PAD_MASK = 12,
+
+    DO_TERM_BUFFER = 16,  // Must termiante at end of buffer
+    DO_TERM_CHAR = 32,    // May terminate at any character boundary
+    DO_TERM_ANY = 48,     // May terminate at a sub-character bit offset
+    DO_TERM_MASK = 48,
+
+    // Strictest interpretation
+    DO_STRICT = DO_PARSE_STRICT | DO_PAD_YES | DO_TERM_BUFFER,
+
+    DO_LAX = DO_PARSE_ANY | DO_PAD_ANY | DO_TERM_CHAR,
+  };
+  typedef int DecodeFlags;
+
+  static bool IsBase64Char(char ch);
+
+  // Get the char next to the |ch| from the Base64Table.
+  // If the |ch| is the last one in the Base64Table then returns
+  // the first one from the table.
+  // Expects the |ch| be a base64 char.
+  // The result will be saved in |next_ch|.
+  // Returns true on success.
+  static bool GetNextBase64Char(char ch, char* next_ch);
+
+  // Determines whether the given string consists entirely of valid base64
+  // encoded characters.
+  static bool IsBase64Encoded(const std::string& str);
+
+  static void EncodeFromArray(const void* data,
+                              size_t len,
+                              std::string* result);
+  static bool DecodeFromArray(const char* data,
+                              size_t len,
+                              DecodeFlags flags,
+                              std::string* result,
+                              size_t* data_used);
+  static bool DecodeFromArray(const char* data,
+                              size_t len,
+                              DecodeFlags flags,
+                              std::vector<char>* result,
+                              size_t* data_used);
+  static bool DecodeFromArray(const char* data,
+                              size_t len,
+                              DecodeFlags flags,
+                              std::vector<uint8_t>* result,
+                              size_t* data_used);
+
+  // Convenience Methods
+  static inline std::string Encode(const std::string& data) {
+    std::string result;
+    EncodeFromArray(data.data(), data.size(), &result);
+    return result;
+  }
+  static inline std::string Decode(const std::string& data, DecodeFlags flags) {
+    std::string result;
+    DecodeFromArray(data.data(), data.size(), flags, &result, nullptr);
+    return result;
+  }
+  static inline bool Decode(const std::string& data,
+                            DecodeFlags flags,
+                            std::string* result,
+                            size_t* data_used) {
+    return DecodeFromArray(data.data(), data.size(), flags, result, data_used);
+  }
+  static inline bool Decode(const std::string& data,
+                            DecodeFlags flags,
+                            std::vector<char>* result,
+                            size_t* data_used) {
+    return DecodeFromArray(data.data(), data.size(), flags, result, data_used);
+  }
+
+ private:
+  static const char Base64Table[];
+  static const unsigned char DecodeTable[];
+
+  static size_t GetNextQuantum(DecodeFlags parse_flags,
+                               bool illegal_pads,
+                               const char* data,
+                               size_t len,
+                               size_t* dpos,
+                               unsigned char qbuf[4],
+                               bool* padded);
+  template <typename T>
+  static bool DecodeFromArrayTemplate(const char* data,
+                                      size_t len,
+                                      DecodeFlags flags,
+                                      T* result,
+                                      size_t* data_used);
+};
+
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_BASE64_H__
diff --git a/base/base64_unittest.cc b/base/base64_unittest.cc
new file mode 100644
index 0000000..6bfba79
--- /dev/null
+++ b/base/base64_unittest.cc
@@ -0,0 +1,999 @@
+/*
+ *  Copyright 2011 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/base64.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/stringutils.h"
+
+#include "webrtc/base/testbase64.h"
+
+using namespace std;
+using namespace rtc;
+
+static struct {
+  size_t plain_length;
+  const char* plaintext;
+  const char* cyphertext;
+} base64_tests[] = {
+
+  // Basic bit patterns;
+  // values obtained with "echo -n '...' | uuencode -m test"
+
+  { 1, "\000", "AA==" },
+  { 1, "\001", "AQ==" },
+  { 1, "\002", "Ag==" },
+  { 1, "\004", "BA==" },
+  { 1, "\010", "CA==" },
+  { 1, "\020", "EA==" },
+  { 1, "\040", "IA==" },
+  { 1, "\100", "QA==" },
+  { 1, "\200", "gA==" },
+
+  { 1, "\377", "/w==" },
+  { 1, "\376", "/g==" },
+  { 1, "\375", "/Q==" },
+  { 1, "\373", "+w==" },
+  { 1, "\367", "9w==" },
+  { 1, "\357", "7w==" },
+  { 1, "\337", "3w==" },
+  { 1, "\277", "vw==" },
+  { 1, "\177", "fw==" },
+  { 2, "\000\000", "AAA=" },
+  { 2, "\000\001", "AAE=" },
+  { 2, "\000\002", "AAI=" },
+  { 2, "\000\004", "AAQ=" },
+  { 2, "\000\010", "AAg=" },
+  { 2, "\000\020", "ABA=" },
+  { 2, "\000\040", "ACA=" },
+  { 2, "\000\100", "AEA=" },
+  { 2, "\000\200", "AIA=" },
+  { 2, "\001\000", "AQA=" },
+  { 2, "\002\000", "AgA=" },
+  { 2, "\004\000", "BAA=" },
+  { 2, "\010\000", "CAA=" },
+  { 2, "\020\000", "EAA=" },
+  { 2, "\040\000", "IAA=" },
+  { 2, "\100\000", "QAA=" },
+  { 2, "\200\000", "gAA=" },
+
+  { 2, "\377\377", "//8=" },
+  { 2, "\377\376", "//4=" },
+  { 2, "\377\375", "//0=" },
+  { 2, "\377\373", "//s=" },
+  { 2, "\377\367", "//c=" },
+  { 2, "\377\357", "/+8=" },
+  { 2, "\377\337", "/98=" },
+  { 2, "\377\277", "/78=" },
+  { 2, "\377\177", "/38=" },
+  { 2, "\376\377", "/v8=" },
+  { 2, "\375\377", "/f8=" },
+  { 2, "\373\377", "+/8=" },
+  { 2, "\367\377", "9/8=" },
+  { 2, "\357\377", "7/8=" },
+  { 2, "\337\377", "3/8=" },
+  { 2, "\277\377", "v/8=" },
+  { 2, "\177\377", "f/8=" },
+
+  { 3, "\000\000\000", "AAAA" },
+  { 3, "\000\000\001", "AAAB" },
+  { 3, "\000\000\002", "AAAC" },
+  { 3, "\000\000\004", "AAAE" },
+  { 3, "\000\000\010", "AAAI" },
+  { 3, "\000\000\020", "AAAQ" },
+  { 3, "\000\000\040", "AAAg" },
+  { 3, "\000\000\100", "AABA" },
+  { 3, "\000\000\200", "AACA" },
+  { 3, "\000\001\000", "AAEA" },
+  { 3, "\000\002\000", "AAIA" },
+  { 3, "\000\004\000", "AAQA" },
+  { 3, "\000\010\000", "AAgA" },
+  { 3, "\000\020\000", "ABAA" },
+  { 3, "\000\040\000", "ACAA" },
+  { 3, "\000\100\000", "AEAA" },
+  { 3, "\000\200\000", "AIAA" },
+  { 3, "\001\000\000", "AQAA" },
+  { 3, "\002\000\000", "AgAA" },
+  { 3, "\004\000\000", "BAAA" },
+  { 3, "\010\000\000", "CAAA" },
+  { 3, "\020\000\000", "EAAA" },
+  { 3, "\040\000\000", "IAAA" },
+  { 3, "\100\000\000", "QAAA" },
+  { 3, "\200\000\000", "gAAA" },
+
+  { 3, "\377\377\377", "////" },
+  { 3, "\377\377\376", "///+" },
+  { 3, "\377\377\375", "///9" },
+  { 3, "\377\377\373", "///7" },
+  { 3, "\377\377\367", "///3" },
+  { 3, "\377\377\357", "///v" },
+  { 3, "\377\377\337", "///f" },
+  { 3, "\377\377\277", "//+/" },
+  { 3, "\377\377\177", "//9/" },
+  { 3, "\377\376\377", "//7/" },
+  { 3, "\377\375\377", "//3/" },
+  { 3, "\377\373\377", "//v/" },
+  { 3, "\377\367\377", "//f/" },
+  { 3, "\377\357\377", "/+//" },
+  { 3, "\377\337\377", "/9//" },
+  { 3, "\377\277\377", "/7//" },
+  { 3, "\377\177\377", "/3//" },
+  { 3, "\376\377\377", "/v//" },
+  { 3, "\375\377\377", "/f//" },
+  { 3, "\373\377\377", "+///" },
+  { 3, "\367\377\377", "9///" },
+  { 3, "\357\377\377", "7///" },
+  { 3, "\337\377\377", "3///" },
+  { 3, "\277\377\377", "v///" },
+  { 3, "\177\377\377", "f///" },
+
+  // Random numbers: values obtained with
+  //
+  //  #! /bin/bash
+  //  dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random
+  //  od -N $1 -t o1 /tmp/bar.random
+  //  uuencode -m test < /tmp/bar.random
+  //
+  // where $1 is the number of bytes (2, 3)
+
+  { 2, "\243\361", "o/E=" },
+  { 2, "\024\167", "FHc=" },
+  { 2, "\313\252", "y6o=" },
+  { 2, "\046\041", "JiE=" },
+  { 2, "\145\236", "ZZ4=" },
+  { 2, "\254\325", "rNU=" },
+  { 2, "\061\330", "Mdg=" },
+  { 2, "\245\032", "pRo=" },
+  { 2, "\006\000", "BgA=" },
+  { 2, "\375\131", "/Vk=" },
+  { 2, "\303\210", "w4g=" },
+  { 2, "\040\037", "IB8=" },
+  { 2, "\261\372", "sfo=" },
+  { 2, "\335\014", "3Qw=" },
+  { 2, "\233\217", "m48=" },
+  { 2, "\373\056", "+y4=" },
+  { 2, "\247\232", "p5o=" },
+  { 2, "\107\053", "Rys=" },
+  { 2, "\204\077", "hD8=" },
+  { 2, "\276\211", "vok=" },
+  { 2, "\313\110", "y0g=" },
+  { 2, "\363\376", "8/4=" },
+  { 2, "\251\234", "qZw=" },
+  { 2, "\103\262", "Q7I=" },
+  { 2, "\142\312", "Yso=" },
+  { 2, "\067\211", "N4k=" },
+  { 2, "\220\001", "kAE=" },
+  { 2, "\152\240", "aqA=" },
+  { 2, "\367\061", "9zE=" },
+  { 2, "\133\255", "W60=" },
+  { 2, "\176\035", "fh0=" },
+  { 2, "\032\231", "Gpk=" },
+
+  { 3, "\013\007\144", "Cwdk" },
+  { 3, "\030\112\106", "GEpG" },
+  { 3, "\047\325\046", "J9Um" },
+  { 3, "\310\160\022", "yHAS" },
+  { 3, "\131\100\237", "WUCf" },
+  { 3, "\064\342\134", "NOJc" },
+  { 3, "\010\177\004", "CH8E" },
+  { 3, "\345\147\205", "5WeF" },
+  { 3, "\300\343\360", "wOPw" },
+  { 3, "\061\240\201", "MaCB" },
+  { 3, "\225\333\044", "ldsk" },
+  { 3, "\215\137\352", "jV/q" },
+  { 3, "\371\147\160", "+Wdw" },
+  { 3, "\030\320\051", "GNAp" },
+  { 3, "\044\174\241", "JHyh" },
+  { 3, "\260\127\037", "sFcf" },
+  { 3, "\111\045\033", "SSUb" },
+  { 3, "\202\114\107", "gkxH" },
+  { 3, "\057\371\042", "L/ki" },
+  { 3, "\223\247\244", "k6ek" },
+  { 3, "\047\216\144", "J45k" },
+  { 3, "\203\070\327", "gzjX" },
+  { 3, "\247\140\072", "p2A6" },
+  { 3, "\124\115\116", "VE1O" },
+  { 3, "\157\162\050", "b3Io" },
+  { 3, "\357\223\004", "75ME" },
+  { 3, "\052\117\156", "Kk9u" },
+  { 3, "\347\154\000", "52wA" },
+  { 3, "\303\012\142", "wwpi" },
+  { 3, "\060\035\362", "MB3y" },
+  { 3, "\130\226\361", "WJbx" },
+  { 3, "\173\013\071", "ews5" },
+  { 3, "\336\004\027", "3gQX" },
+  { 3, "\357\366\234", "7/ac" },
+  { 3, "\353\304\111", "68RJ" },
+  { 3, "\024\264\131", "FLRZ" },
+  { 3, "\075\114\251", "PUyp" },
+  { 3, "\315\031\225", "zRmV" },
+  { 3, "\154\201\276", "bIG+" },
+  { 3, "\200\066\072", "gDY6" },
+  { 3, "\142\350\267", "Yui3" },
+  { 3, "\033\000\166", "GwB2" },
+  { 3, "\210\055\077", "iC0/" },
+  { 3, "\341\037\124", "4R9U" },
+  { 3, "\161\103\152", "cUNq" },
+  { 3, "\270\142\131", "uGJZ" },
+  { 3, "\337\076\074", "3z48" },
+  { 3, "\375\106\362", "/Uby" },
+  { 3, "\227\301\127", "l8FX" },
+  { 3, "\340\002\234", "4AKc" },
+  { 3, "\121\064\033", "UTQb" },
+  { 3, "\157\134\143", "b1xj" },
+  { 3, "\247\055\327", "py3X" },
+  { 3, "\340\142\005", "4GIF" },
+  { 3, "\060\260\143", "MLBj" },
+  { 3, "\075\203\170", "PYN4" },
+  { 3, "\143\160\016", "Y3AO" },
+  { 3, "\313\013\063", "ywsz" },
+  { 3, "\174\236\135", "fJ5d" },
+  { 3, "\103\047\026", "QycW" },
+  { 3, "\365\005\343", "9QXj" },
+  { 3, "\271\160\223", "uXCT" },
+  { 3, "\362\255\172", "8q16" },
+  { 3, "\113\012\015", "SwoN" },
+
+  // various lengths, generated by this python script:
+  //
+  // from string import lowercase as lc
+  // for i in range(27):
+  //   print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i),
+  //                                     lc[:i].encode('base64').strip())
+
+  {  0, "abcdefghijklmnopqrstuvwxyz", "" },
+  {  1, "abcdefghijklmnopqrstuvwxyz", "YQ==" },
+  {  2, "abcdefghijklmnopqrstuvwxyz", "YWI=" },
+  {  3, "abcdefghijklmnopqrstuvwxyz", "YWJj" },
+  {  4, "abcdefghijklmnopqrstuvwxyz", "YWJjZA==" },
+  {  5, "abcdefghijklmnopqrstuvwxyz", "YWJjZGU=" },
+  {  6, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVm" },
+  {  7, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZw==" },
+  {  8, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2g=" },
+  {  9, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hp" },
+  { 10, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpag==" },
+  { 11, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpams=" },
+  { 12, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamts" },
+  { 13, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbQ==" },
+  { 14, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW4=" },
+  { 15, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5v" },
+  { 16, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcA==" },
+  { 17, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHE=" },
+  { 18, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFy" },
+  { 19, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFycw==" },
+  { 20, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q=" },
+  { 21, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1" },
+  { 22, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg==" },
+  { 23, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc=" },
+  { 24, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4" },
+  { 25, "abcdefghijklmnopqrstuvwxy",  "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ==" },
+  { 26, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=" },
+};
+#if 0
+static struct {
+  const char* plaintext;
+  const char* cyphertext;
+} base64_strings[] = {
+
+  // The first few Google quotes
+  // Cyphertext created with "uuencode - GNU sharutils 4.2.1"
+  {
+    "Everyone!  We're teetering on the brink of disaster."
+    " - Sergey Brin, 6/24/99, regarding the company's state "
+    "after the unleashing of Netscape/Google search",
+
+    "RXZlcnlvbmUhICBXZSdyZSB0ZWV0ZXJpbmcgb24gdGhlIGJyaW5rIG9mIGRp"
+    "c2FzdGVyLiAtIFNlcmdleSBCcmluLCA2LzI0Lzk5LCByZWdhcmRpbmcgdGhl"
+    "IGNvbXBhbnkncyBzdGF0ZSBhZnRlciB0aGUgdW5sZWFzaGluZyBvZiBOZXRz"
+    "Y2FwZS9Hb29nbGUgc2VhcmNo" },
+
+  {
+    "I'm not sure why we're still alive, but we seem to be."
+    " - Larry Page, 6/24/99, while hiding in the kitchenette "
+    "during the Netscape traffic overflow",
+
+    "SSdtIG5vdCBzdXJlIHdoeSB3ZSdyZSBzdGlsbCBhbGl2ZSwgYnV0IHdlIHNl"
+    "ZW0gdG8gYmUuIC0gTGFycnkgUGFnZSwgNi8yNC85OSwgd2hpbGUgaGlkaW5n"
+    "IGluIHRoZSBraXRjaGVuZXR0ZSBkdXJpbmcgdGhlIE5ldHNjYXBlIHRyYWZm"
+    "aWMgb3ZlcmZsb3c" },
+
+  {
+    "I think kids want porn."
+    " - Sergey Brin, 6/99, on why Google shouldn't prioritize a "
+    "filtered search for children and families",
+
+    "SSB0aGluayBraWRzIHdhbnQgcG9ybi4gLSBTZXJnZXkgQnJpbiwgNi85OSwg"
+    "b24gd2h5IEdvb2dsZSBzaG91bGRuJ3QgcHJpb3JpdGl6ZSBhIGZpbHRlcmVk"
+    "IHNlYXJjaCBmb3IgY2hpbGRyZW4gYW5kIGZhbWlsaWVz" },
+};
+#endif
+// Compare bytes 0..len-1 of x and y.  If not equal, abort with verbose error
+// message showing position and numeric value that differed.
+// Handles embedded nulls just like any other byte.
+// Only added because string.compare() in gcc-3.3.3 seems to misbehave with
+// embedded nulls.
+// TODO: switch back to string.compare() if/when gcc is fixed
+#define EXPECT_EQ_ARRAY(len, x, y, msg)                      \
+  for (size_t j = 0; j < len; ++j) {                           \
+    if (x[j] != y[j]) {                                     \
+        LOG(LS_ERROR) << "" # x << " != " # y                  \
+                   << " byte " << j << " msg: " << msg;     \
+      }                                                     \
+    }
+
+size_t Base64Escape(const unsigned char *src, size_t szsrc, char *dest,
+                    size_t szdest) {
+  std::string escaped;
+  Base64::EncodeFromArray((const char *)src, szsrc, &escaped);
+  memcpy(dest, escaped.data(), min(escaped.size(), szdest));
+  return escaped.size();
+}
+
+size_t Base64Unescape(const char *src, size_t szsrc, char *dest,
+                      size_t szdest) {
+  std::string unescaped;
+  EXPECT_TRUE(
+      Base64::DecodeFromArray(src, szsrc, Base64::DO_LAX, &unescaped, nullptr));
+  memcpy(dest, unescaped.data(), min(unescaped.size(), szdest));
+  return unescaped.size();
+}
+
+size_t Base64Unescape(const char *src, size_t szsrc, std::string *s) {
+  EXPECT_TRUE(Base64::DecodeFromArray(src, szsrc, Base64::DO_LAX, s, nullptr));
+  return s->size();
+}
+
+TEST(Base64, EncodeDecodeBattery) {
+  LOG(LS_VERBOSE) << "Testing base-64";
+
+  size_t i;
+
+  // Check the short strings; this tests the math (and boundaries)
+  for( i = 0; i < sizeof(base64_tests) / sizeof(base64_tests[0]); ++i ) {
+    char encode_buffer[100];
+    size_t encode_length;
+    char decode_buffer[100];
+    size_t decode_length;
+    size_t cypher_length;
+
+    LOG(LS_VERBOSE) << "B64: " << base64_tests[i].cyphertext;
+
+    const unsigned char* unsigned_plaintext =
+      reinterpret_cast<const unsigned char*>(base64_tests[i].plaintext);
+
+    cypher_length = strlen(base64_tests[i].cyphertext);
+
+    // The basic escape function:
+    memset(encode_buffer, 0, sizeof(encode_buffer));
+    encode_length = Base64Escape(unsigned_plaintext,
+                                 base64_tests[i].plain_length,
+                                 encode_buffer,
+                                 sizeof(encode_buffer));
+    //    Is it of the expected length?
+    EXPECT_EQ(encode_length, cypher_length);
+
+    //    Is it the expected encoded value?
+    EXPECT_STREQ(encode_buffer, base64_tests[i].cyphertext);
+
+    // If we encode it into a buffer of exactly the right length...
+    memset(encode_buffer, 0, sizeof(encode_buffer));
+    encode_length = Base64Escape(unsigned_plaintext,
+                                 base64_tests[i].plain_length,
+                                 encode_buffer,
+                                 cypher_length);
+    //    Is it still of the expected length?
+    EXPECT_EQ(encode_length, cypher_length);
+
+    //    And is the value still correct?  (i.e., not losing the last byte)
+    EXPECT_STREQ(encode_buffer, base64_tests[i].cyphertext);
+
+    // If we decode it back:
+    memset(decode_buffer, 0, sizeof(decode_buffer));
+    decode_length = Base64Unescape(encode_buffer,
+                                   cypher_length,
+                                   decode_buffer,
+                                   sizeof(decode_buffer));
+
+    //    Is it of the expected length?
+    EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+    //    Is it the expected decoded value?
+    EXPECT_EQ(0,  memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+    // Our decoder treats the padding '=' characters at the end as
+    // optional.  If encode_buffer has any, run some additional
+    // tests that fiddle with them.
+    char* first_equals = strchr(encode_buffer, '=');
+    if (first_equals) {
+      // How many equals signs does the string start with?
+      int equals = (*(first_equals+1) == '=') ? 2 : 1;
+
+      // Try chopping off the equals sign(s) entirely.  The decoder
+      // should still be okay with this.
+      std::string decoded2("this junk should also be ignored");
+      *first_equals = '\0';
+      EXPECT_NE(0U, Base64Unescape(encode_buffer, first_equals-encode_buffer,
+                           &decoded2));
+      EXPECT_EQ(decoded2.size(), base64_tests[i].plain_length);
+      EXPECT_EQ_ARRAY(decoded2.size(), decoded2.data(), base64_tests[i].plaintext, i);
+
+      size_t len;
+
+      // try putting some extra stuff after the equals signs, or in between them
+      if (equals == 2) {
+        sprintfn(first_equals, 6, " = = ");
+        len = first_equals - encode_buffer + 5;
+      } else {
+        sprintfn(first_equals, 6, " = ");
+        len = first_equals - encode_buffer + 3;
+      }
+      decoded2.assign("this junk should be ignored");
+      EXPECT_NE(0U, Base64Unescape(encode_buffer, len, &decoded2));
+      EXPECT_EQ(decoded2.size(), base64_tests[i].plain_length);
+      EXPECT_EQ_ARRAY(decoded2.size(), decoded2, base64_tests[i].plaintext, i);
+    }
+  }
+}
+
+// here's a weird case: a giant base64 encoded stream which broke our base64
+// decoding.  Let's test it explicitly.
+const char SpecificTest[] =
+  "/9j/4AAQSkZJRgABAgEASABIAAD/4Q0HRXhpZgAATU0AKgAAAAgADAEOAAIAAAAgAAAAngEPAAI\n"
+  "AAAAFAAAAvgEQAAIAAAAJAAAAwwESAAMAAAABAAEAAAEaAAUAAAABAAAAzAEbAAUAAAABAAAA1A\n"
+  "EoAAMAAAABAAIAAAExAAIAAAAUAAAA3AEyAAIAAAAUAAAA8AE8AAIAAAAQAAABBAITAAMAAAABA\n"
+  "AIAAIdpAAQAAAABAAABFAAAAsQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgAFNPTlkA\n"
+  "RFNDLVAyMDAAAAAASAAAAAEAAABIAAAAAUFkb2JlIFBob3Rvc2hvcCA3LjAAMjAwNzowMTozMCA\n"
+  "yMzoxMDowNABNYWMgT1MgWCAxMC40LjgAAByCmgAFAAAAAQAAAmqCnQAFAAAAAQAAAnKIIgADAA\n"
+  "AAAQACAACIJwADAAAAAQBkAACQAAAHAAAABDAyMjCQAwACAAAAFAAAAnqQBAACAAAAFAAAAo6RA\n"
+  "QAHAAAABAECAwCRAgAFAAAAAQAAAqKSBAAKAAAAAQAAAqqSBQAFAAAAAQAAArKSBwADAAAAAQAF\n"
+  "AACSCAADAAAAAQAAAACSCQADAAAAAQAPAACSCgAFAAAAAQAAArqgAAAHAAAABDAxMDCgAQADAAA\n"
+  "AAf//AACgAgAEAAAAAQAAAGSgAwAEAAAAAQAAAGSjAAAHAAAAAQMAAACjAQAHAAAAAQEAAACkAQ\n"
+  "ADAAAAAQAAAACkAgADAAAAAQAAAACkAwADAAAAAQAAAACkBgADAAAAAQAAAACkCAADAAAAAQAAA\n"
+  "ACkCQADAAAAAQAAAACkCgADAAAAAQAAAAAAAAAAAAAACgAAAZAAAAAcAAAACjIwMDc6MDE6MjAg\n"
+  "MjM6MDU6NTIAMjAwNzowMToyMCAyMzowNTo1MgAAAAAIAAAAAQAAAAAAAAAKAAAAMAAAABAAAAB\n"
+  "PAAAACgAAAAYBAwADAAAAAQAGAAABGgAFAAAAAQAAAxIBGwAFAAAAAQAAAxoBKAADAAAAAQACAA\n"
+  "ACAQAEAAAAAQAAAyICAgAEAAAAAQAACd0AAAAAAAAASAAAAAEAAABIAAAAAf/Y/+AAEEpGSUYAA\n"
+  "QIBAEgASAAA/+0ADEFkb2JlX0NNAAL/7gAOQWRvYmUAZIAAAAAB/9sAhAAMCAgICQgMCQkMEQsK\n"
+  "CxEVDwwMDxUYExMVExMYEQwMDAwMDBEMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAQ0LCw0\n"
+  "ODRAODhAUDg4OFBQODg4OFBEMDAwMDBERDAwMDAwMEQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA\n"
+  "wMDAz/wAARCABkAGQDASIAAhEBAxEB/90ABAAH/8QBPwAAAQUBAQEBAQEAAAAAAAAAAwABAgQFB\n"
+  "gcICQoLAQABBQEBAQEBAQAAAAAAAAABAAIDBAUGBwgJCgsQAAEEAQMCBAIFBwYIBQMMMwEAAhED\n"
+  "BCESMQVBUWETInGBMgYUkaGxQiMkFVLBYjM0coLRQwclklPw4fFjczUWorKDJkSTVGRFwqN0Nhf\n"
+  "SVeJl8rOEw9N14/NGJ5SkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2N0dXZ3eHl6e3x9fn9xEAAg\n"
+  "IBAgQEAwQFBgcHBgU1AQACEQMhMRIEQVFhcSITBTKBkRShsUIjwVLR8DMkYuFygpJDUxVjczTxJ\n"
+  "QYWorKDByY1wtJEk1SjF2RFVTZ0ZeLys4TD03Xj80aUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm\n"
+  "9ic3R1dnd4eXp7fH/9oADAMBAAIRAxEAPwDy7bKNTUXNLz9EaJPDWMjxH4ozhtpYwaACT8ShaaW\n"
+  "bW0uEc9/JFfjj0Q4Hk/PRDxwX7y47W9z/AN9Cv4+O3ILK2DcRqT2CaSvEbcl1Jbz37KG1dBldLo\n"
+  "qaS4l9xGjG9v6yoDAdYIaIjUk+AREgo4y5sapirb8Yl0NHHdKvBNm4yA1o5Pc+SPEFvCWqB3HZF\n"
+  "Hj2SbWQ/afGFP0bHP8ATY0uc4w1o1JPkkimGiS2KvqlnmBkOZQTyydzgPMM9v8A0lp4v1Nx9gF1\n"
+  "tpdqJaGtH/S3I0i3lISXW/8AMqnd/O2bfg2eUkqVYf/Q8zuncO4Bj7lZ+n7f5Mj5KsJcY8NUZ4d\n"
+  "uEDVo1HkeU0rg3Om4H2rabCWUN7DQuK1n5FWKW4uCwG92gDRJBS6exhxmMboQI+Cv4WFTQ42Bs2\n"
+  "fvnkkqEmy2YxoMMbpVzaz6jt+RbpHZs8lzkHqrasKkYOKP0jgDfZ4N/wDM1tNrcWfSPmRyq9uNV\n"
+  "DnFg2s97i7UkjxKVrq0eVz3spZsja+ASDzwsh9jnOk/JFzb3XZD3v1c4yT8UACTCniKDUnKz5Nj\n"
+  "G33XV1DV73BrT8dF23SejV4zg9g33cOsPb+SxVvqv9ViwNy8vS0iWs/daf8A0Y5dpTi1sADGxCR\n"
+  "K1o0YBEmInlXWYbDBcDLdPJXa8f71Yrx2jnUoAqLnfZK5hJaW2vdwEk5a/wD/0fN6Ia/e76IiVf\n"
+  "xavUL7CPpnT4LNbYXAVjuQt/AqDmNYO/Kjnoy4hr5J8SwMhrRMaeSvbsxrfUazcOw4UX0Cisem2\n"
+  "SBoD4+Kz8nC6llbSLCRrubJA8kwUWbUDa29X1PMa7aQWjuDC0MXMdbDbhI7eazBiUfZ6GOYRe1s\n"
+  "WvGgJ8Vbw2+m4Bx9s6JpNHuuGo1FF53r/SHYua61gLse0lzXeBP5rkvqx0o5vVWz7WY49QkiQSP\n"
+  "oN/tLoevW/ogxv0HA7tJ0AnhT+pdDGYVl/wCdcTPkGn2NU0JWNWvlgAbHV6fEqdu2gR/r2WlWwt\n"
+  "AA5VXAEsLXTqJafArQY5rRr9LiPBJiZsZCI1pJjxCi0j4oncSICSkWwzwkjeaSch//0vO7sP7Lm\n"
+  "enO9ogtd5FbPT3Q5pCpZVc4ld3Lmn3O8j9EI2BYdunKjOobMQIyI+rusc2wx4d0eutwGnHh/uQc\n"
+  "Ha7ladj6mVANGvcqOgz0Go7HJ12/GEHcwvB/dPY6ImbbaMaASGuIBjkN7qofs9Ubg9g7OI9p/t/\n"
+  "RTSmhTHr0v6eSz6UgCPP2/wAVu9Ex2V49dVY2iACB4BZeVXQ/AJ3gzGnnOi2+kACpru8flUsNmt\n"
+  "zHRf6xfWCnoeAfTh2ZaQKazx/Ke7+QxcKz61fWA2uuObaC4zGhaPJrXBL64ZFmR124O09ENraPK\n"
+  "N3/AH5GqxIrZVUyp2K2vfdkENsDnxuex9m4Ox9n82xSgNd9D+p/XR1npgseR9ppOy4Dx/NfH/CL\n"
+  "oQJGunmvMv8AFq3KHVcq3HkYQbD2nuSf0I/rMavSg6TLjLigQhJ7Z58v9QkmlsTOqSCn/9PzL7R\n"
+  "d6Qq3n0wZ2zotXpT9xLfFYvkr/S7jXeB8E0jRkhKpC3q8LcJ/kmCrTnkuAPCq4do9Q/ytVbuAeY\n"
+  "Gg5lQybQK+82GBqEQUA1kOHPYf3LLsoyN36G5w8iUfHxepbXE2l0cApALgLHzBq9UxhTXU5hMC1\n"
+  "ktnSCup6S4Ctk+C5XqVGcaHPfuiuHkeTTuWz0+9zaKiH6CC0/yXBSQ2a/MxojV57634rq+v2PLY\n"
+  "be1r2nsYG13/AFKxbfCBMcr0brGAzrGEwCG31ncx0SfBzf7S4+zoHUWWsJq3hz9oLfcBH77R9H+\n"
+  "0pA13u/qPgDp/Q6ri39JlfpXkDx+h/msWn1L6wdO6bSbcrIbU2Q0xLnSe21kuVejJspbVS5+4bd\n"
+  "ocBAkD/orG+tP1ar67Wy7GtZTm1SCXfRsb+a18fRe38x6SG3/44H1Z3f0y2I+l6DoSXD/8xPrDs\n"
+  "3enVu3bdnqN3R+//USSVo//1PLohhce+gRWS0Nsby3lRgFkKxQyW7SgUh3em5Tbq2uB9wWw1wey\n"
+  "J1XGV2XYdm5k7e4WzidXY9oMwo5RZ4T6Hd1ixwfp96PWbAJBVTHzK7O6Ky5oJB1HZMqmUEFlkGy\n"
+  "xpa4zI1Hkq31dy7bMN9BAc3HeWAnnbyxEycmuup1jiAGglZ31PyrmZ9tQg1WtNj54EHR3/S2qTH\n"
+  "1Yc5GgD1FFtzPdWGkd2AyflogZmRmsz6PSrbXbdo+txOrP337f3fzVo15DK2uyrTtqpBOnBKx6b\n"
+  "7MjJsz7tHWOAYP3WD6LU6cqGjFCNl1MmvLcxv6YtDTLSAqP27LrdtYHXFnJZI+Tp3MWg68OpDPv\n"
+  "UMUM2lkQBoouKQ6swjE9Nml+1sz1PW+z6xt27zuj+skrX2ZvqR5z8kkuOfdPt43/1fMm/grFG6f\n"
+  "Lss9JA7JG7tnZs/SfJUrfS3foJ9TvHCopJsV8nWx/t24bJn8Fo/5TjWJXMJIS+i+G36TsZ/7Q9P\n"
+  "8ATfzfeOFofVSZv2/zvt+O3X/v65dJPjt/BiyfN1/wn0zre79nVej/ADG8ep4x2/6Srjd6TdviF\n"
+  "52ko8m6/Ht9X1KnftEo+POwxzK8mSTF46vrH6T1/OEl5Okkl//Z/+0uHFBob3Rvc2hvcCAzLjAA\n"
+  "OEJJTQQEAAAAAAArHAIAAAIAAhwCeAAfICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAA\n"
+  "4QklNBCUAAAAAABD7Caa9B0wqNp2P4sxXqayFOEJJTQPqAAAAAB2wPD94bWwgdmVyc2lvbj0iMS\n"
+  "4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUgQ\n"
+  "29tcHV0ZXIvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Q\n"
+  "cm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk\n"
+  "+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1Ib3Jpem9udGFsUmVzPC9rZXk+Cgk8ZGljdD\n"
+  "4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCTxzdHJpbmc+Y\n"
+  "29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCTxrZXk+Y29tLmFwcGxlLnByaW50\n"
+  "LnRpY2tldC5pdGVtQXJyYXk8L2tleT4KCQk8YXJyYXk+CgkJCTxkaWN0PgoJCQkJPGtleT5jb20\n"
+  "uYXBwbGUucHJpbnQuUGFnZUZvcm1hdC5QTUhvcml6b250YWxSZXM8L2tleT4KCQkJCTxyZWFsPj\n"
+  "cyPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDwva2V5PgoJC\n"
+  "QkJPHN0cmluZz5jb20uYXBwbGUucHJpbnRpbmdtYW5hZ2VyPC9zdHJpbmc+CgkJCQk8a2V5PmNv\n"
+  "bS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQkJPGRhdGU+MjAwNy0wMS0zMFQ\n"
+  "yMjowODo0MVo8L2RhdGU+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuc3RhdGVGbG\n"
+  "FnPC9rZXk+CgkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQk8L2RpY3Q+CgkJPC9hcnJheT4KC\n"
+  "TwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1PcmllbnRhdGlvbjwv\n"
+  "a2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4\n"
+  "KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQk8a2V5PmNvbS\n"
+  "5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFycmF5PgoJCQk8ZGljdD4KC\n"
+  "QkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1PcmllbnRhdGlvbjwva2V5PgoJ\n"
+  "CQkJPGludGVnZXI+MTwvaW50ZWdlcj4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5\n"
+  "jbGllbnQ8L2tleT4KCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW\n"
+  "5nPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lm1vZERhdGU8L2tleT4KCQkJCTxkY\n"
+  "XRlPjIwMDctMDEtMzBUMjI6MDg6NDFaPC9kYXRlPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQu\n"
+  "dGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJPGludGVnZXI+MDwvaW50ZWdlcj4KCQkJPC9kaWN\n"
+  "0PgoJCTwvYXJyYXk+Cgk8L2RpY3Q+Cgk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0Ll\n"
+  "BNU2NhbGluZzwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZ\n"
+  "WF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4K\n"
+  "CQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFycmF5Pgo\n"
+  "JCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1TY2FsaW5nPC\n"
+  "9rZXk+CgkJCQk8cmVhbD4xPC9yZWFsPgoJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0L\n"
+  "mNsaWVudDwva2V5PgoJCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnRpbmdtYW5hZ2VyPC9zdHJp\n"
+  "bmc+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQkJPGR\n"
+  "hdGU+MjAwNy0wMS0zMFQyMjowODo0MVo8L2RhdGU+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC\n"
+  "50aWNrZXQuc3RhdGVGbGFnPC9rZXk+CgkJCQk8aW50ZWdlcj4wPC9pbnRlZ2VyPgoJCQk8L2RpY\n"
+  "3Q+CgkJPC9hcnJheT4KCTwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQu\n"
+  "UE1WZXJ0aWNhbFJlczwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V\n"
+  "0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cm\n"
+  "luZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJPGFyc\n"
+  "mF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYXQuUE1WZXJ0\n"
+  "aWNhbFJlczwva2V5PgoJCQkJPHJlYWw+NzI8L3JlYWw+CgkJCQk8a2V5PmNvbS5hcHBsZS5wcml\n"
+  "udC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbm\n"
+  "FnZXI8L3N0cmluZz4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZ\n"
+  "Xk+CgkJCQk8ZGF0ZT4yMDA3LTAxLTMwVDIyOjA4OjQxWjwvZGF0ZT4KCQkJCTxrZXk+Y29tLmFw\n"
+  "cGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI\n"
+  "+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5jb20uYXBwbGUucHJpbnQuUG\n"
+  "FnZUZvcm1hdC5QTVZlcnRpY2FsU2NhbGluZzwva2V5PgoJPGRpY3Q+CgkJPGtleT5jb20uYXBwb\n"
+  "GUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGlu\n"
+  "Z21hbmFnZXI8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF\n"
+  "5PC9rZXk+CgkJPGFycmF5PgoJCQk8ZGljdD4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2\n"
+  "VGb3JtYXQuUE1WZXJ0aWNhbFNjYWxpbmc8L2tleT4KCQkJCTxyZWFsPjE8L3JlYWw+CgkJCQk8a\n"
+  "2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY2xpZW50PC9rZXk+CgkJCQk8c3RyaW5nPmNvbS5h\n"
+  "cHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnR\n"
+  "pY2tldC5tb2REYXRlPC9rZXk+CgkJCQk8ZGF0ZT4yMDA3LTAxLTMwVDIyOjA4OjQxWjwvZGF0ZT\n"
+  "4KCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCTxpb\n"
+  "nRlZ2VyPjA8L2ludGVnZXI+CgkJCTwvZGljdD4KCQk8L2FycmF5PgoJPC9kaWN0PgoJPGtleT5j\n"
+  "b20uYXBwbGUucHJpbnQuc3ViVGlja2V0LnBhcGVyX2luZm9fdGlja2V0PC9rZXk+Cgk8ZGljdD4\n"
+  "KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUmVjdDwva2\n"
+  "V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY3JlYXRvcjwva2V5P\n"
+  "goJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJPGtleT5j\n"
+  "b20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGl\n"
+  "jdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYWdlRm9ybWF0LlBNQWRqdXN0ZWRQYWdlUm\n"
+  "VjdDwva2V5PgoJCQkJCTxhcnJheT4KCQkJCQkJPHJlYWw+MC4wPC9yZWFsPgoJCQkJCQk8cmVhb\n"
+  "D4wLjA8L3JlYWw+CgkJCQkJCTxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw+NTc2PC9yZWFs\n"
+  "PgoJCQkJCTwvYXJyYXk+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDw\n"
+  "va2V5PgoJCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQ\n"
+  "kJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPGRhdGU+M\n"
+  "jAwNy0wMS0zMFQyMjowODo0MVo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlj\n"
+  "a2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q\n"
+  "+CgkJCTwvYXJyYXk+CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhZ2VGb3JtYX\n"
+  "QuUE1BZGp1c3RlZFBhcGVyUmVjdDwva2V5PgoJCTxkaWN0PgoJCQk8a2V5PmNvbS5hcHBsZS5wc\n"
+  "mludC50aWNrZXQuY3JlYXRvcjwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21h\n"
+  "bmFnZXI8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0Lml0ZW1BcnJheTw\n"
+  "va2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC5QYW\n"
+  "dlRm9ybWF0LlBNQWRqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk+CgkJCQkJCTxyZ\n"
+  "WFsPi0xODwvcmVhbD4KCQkJCQkJPHJlYWw+LTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3Jl\n"
+  "YWw+CgkJCQkJCTxyZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk+Y29tLmF\n"
+  "wcGxlLnByaW50LnRpY2tldC5jbGllbnQ8L2tleT4KCQkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcm\n"
+  "ludGluZ21hbmFnZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQub\n"
+  "W9kRGF0ZTwva2V5PgoJCQkJCTxkYXRlPjIwMDctMDEtMzBUMjI6MDg6NDFaPC9kYXRlPgoJCQkJ\n"
+  "CTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWd\n"
+  "lcj4wPC9pbnRlZ2VyPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5Pm\n"
+  "NvbS5hcHBsZS5wcmludC5QYXBlckluZm8uUE1QYXBlck5hbWU8L2tleT4KCQk8ZGljdD4KCQkJP\n"
+  "GtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20u\n"
+  "YXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcml\n"
+  "udC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZX\n"
+  "k+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVBhcGVyTmFtZTwva2V5PgoJCQkJCTxzdHJpb\n"
+  "mc+bmEtbGV0dGVyPC9zdHJpbmc+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNs\n"
+  "aWVudDwva2V5PgoJCQkJCTxzdHJpbmc+Y29tLmFwcGxlLnByaW50LnBtLlBvc3RTY3JpcHQ8L3N\n"
+  "0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5PgoJCQ\n"
+  "kJCTxkYXRlPjIwMDMtMDctMDFUMTc6NDk6MzZaPC9kYXRlPgoJCQkJCTxrZXk+Y29tLmFwcGxlL\n"
+  "nByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4xPC9pbnRlZ2VyPgoJ\n"
+  "CQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcmludC5\n"
+  "QYXBlckluZm8uUE1VbmFkanVzdGVkUGFnZVJlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb2\n"
+  "0uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUuc\n"
+  "HJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5nPgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNr\n"
+  "ZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJheT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmF\n"
+  "wcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYWdlUmVjdDwva2V5PgoJCQkJCTxhcn\n"
+  "JheT4KCQkJCQkJPHJlYWw+MC4wPC9yZWFsPgoJCQkJCQk8cmVhbD4wLjA8L3JlYWw+CgkJCQkJC\n"
+  "TxyZWFsPjczNDwvcmVhbD4KCQkJCQkJPHJlYWw+NTc2PC9yZWFsPgoJCQkJCTwvYXJyYXk+CgkJ\n"
+  "CQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNsaWVudDwva2V5PgoJCQkJCTxzdHJpbmc\n"
+  "+Y29tLmFwcGxlLnByaW50aW5nbWFuYWdlcjwvc3RyaW5nPgoJCQkJCTxrZXk+Y29tLmFwcGxlLn\n"
+  "ByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPGRhdGU+MjAwNy0wMS0zMFQyMjowODo0M\n"
+  "Vo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LnN0YXRlRmxhZzwva2V5\n"
+  "PgoJCQkJCTxpbnRlZ2VyPjA8L2ludGVnZXI+CgkJCQk8L2RpY3Q+CgkJCTwvYXJyYXk+CgkJPC9\n"
+  "kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYWRqdXN0ZWRQYXBlcl\n"
+  "JlY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V0LmNyZWF0b\n"
+  "3I8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5n\n"
+  "PgoJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuaXRlbUFycmF5PC9rZXk+CgkJCTxhcnJ\n"
+  "heT4KCQkJCTxkaWN0PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LlBhcGVySW5mby5QTVVuYW\n"
+  "RqdXN0ZWRQYXBlclJlY3Q8L2tleT4KCQkJCQk8YXJyYXk+CgkJCQkJCTxyZWFsPi0xODwvcmVhb\n"
+  "D4KCQkJCQkJPHJlYWw+LTE4PC9yZWFsPgoJCQkJCQk8cmVhbD43NzQ8L3JlYWw+CgkJCQkJCTxy\n"
+  "ZWFsPjU5NDwvcmVhbD4KCQkJCQk8L2FycmF5PgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnR\n"
+  "pY2tldC5jbGllbnQ8L2tleT4KCQkJCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmludGluZ21hbmFnZX\n"
+  "I8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQubW9kRGF0ZTwva2V5P\n"
+  "goJCQkJCTxkYXRlPjIwMDctMDEtMzBUMjI6MDg6NDFaPC9kYXRlPgoJCQkJCTxrZXk+Y29tLmFw\n"
+  "cGxlLnByaW50LnRpY2tldC5zdGF0ZUZsYWc8L2tleT4KCQkJCQk8aW50ZWdlcj4wPC9pbnRlZ2V\n"
+  "yPgoJCQkJPC9kaWN0PgoJCQk8L2FycmF5PgoJCTwvZGljdD4KCQk8a2V5PmNvbS5hcHBsZS5wcm\n"
+  "ludC5QYXBlckluZm8ucHBkLlBNUGFwZXJOYW1lPC9rZXk+CgkJPGRpY3Q+CgkJCTxrZXk+Y29tL\n"
+  "mFwcGxlLnByaW50LnRpY2tldC5jcmVhdG9yPC9rZXk+CgkJCTxzdHJpbmc+Y29tLmFwcGxlLnBy\n"
+  "aW50LnBtLlBvc3RTY3JpcHQ8L3N0cmluZz4KCQkJPGtleT5jb20uYXBwbGUucHJpbnQudGlja2V\n"
+  "0Lml0ZW1BcnJheTwva2V5PgoJCQk8YXJyYXk+CgkJCQk8ZGljdD4KCQkJCQk8a2V5PmNvbS5hcH\n"
+  "BsZS5wcmludC5QYXBlckluZm8ucHBkLlBNUGFwZXJOYW1lPC9rZXk+CgkJCQkJPHN0cmluZz5VU\n"
+  "yBMZXR0ZXI8L3N0cmluZz4KCQkJCQk8a2V5PmNvbS5hcHBsZS5wcmludC50aWNrZXQuY2xpZW50\n"
+  "PC9rZXk+CgkJCQkJPHN0cmluZz5jb20uYXBwbGUucHJpbnQucG0uUG9zdFNjcmlwdDwvc3RyaW5\n"
+  "nPgoJCQkJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2tldC5tb2REYXRlPC9rZXk+CgkJCQkJPG\n"
+  "RhdGU+MjAwMy0wNy0wMVQxNzo0OTozNlo8L2RhdGU+CgkJCQkJPGtleT5jb20uYXBwbGUucHJpb\n"
+  "nQudGlja2V0LnN0YXRlRmxhZzwva2V5PgoJCQkJCTxpbnRlZ2VyPjE8L2ludGVnZXI+CgkJCQk8\n"
+  "L2RpY3Q+CgkJCTwvYXJyYXk+CgkJPC9kaWN0PgoJCTxrZXk+Y29tLmFwcGxlLnByaW50LnRpY2t\n"
+  "ldC5BUElWZXJzaW9uPC9rZXk+CgkJPHN0cmluZz4wMC4yMDwvc3RyaW5nPgoJCTxrZXk+Y29tLm\n"
+  "FwcGxlLnByaW50LnRpY2tldC5wcml2YXRlTG9jazwva2V5PgoJCTxmYWxzZS8+CgkJPGtleT5jb\n"
+  "20uYXBwbGUucHJpbnQudGlja2V0LnR5cGU8L2tleT4KCQk8c3RyaW5nPmNvbS5hcHBsZS5wcmlu\n"
+  "dC5QYXBlckluZm9UaWNrZXQ8L3N0cmluZz4KCTwvZGljdD4KCTxrZXk+Y29tLmFwcGxlLnByaW5\n"
+  "0LnRpY2tldC5BUElWZXJzaW9uPC9rZXk+Cgk8c3RyaW5nPjAwLjIwPC9zdHJpbmc+Cgk8a2V5Pm\n"
+  "NvbS5hcHBsZS5wcmludC50aWNrZXQucHJpdmF0ZUxvY2s8L2tleT4KCTxmYWxzZS8+Cgk8a2V5P\n"
+  "mNvbS5hcHBsZS5wcmludC50aWNrZXQudHlwZTwva2V5PgoJPHN0cmluZz5jb20uYXBwbGUucHJp\n"
+  "bnQuUGFnZUZvcm1hdFRpY2tldDwvc3RyaW5nPgo8L2RpY3Q+CjwvcGxpc3Q+CjhCSU0D6QAAAAA\n"
+  "AeAADAAAASABIAAAAAALeAkD/7v/uAwYCUgNnBSgD/AACAAAASABIAAAAAALYAigAAQAAAGQAAA\n"
+  "ABAAMDAwAAAAF//wABAAEAAAAAAAAAAAAAAABoCAAZAZAAAAAAACAAAAAAAAAAAAAAAAAAAAAAA\n"
+  "AAAAAAAAAAAADhCSU0D7QAAAAAAEABIAAAAAQABAEgAAAABAAE4QklNBCYAAAAAAA4AAAAAAAAA\n"
+  "AAAAP4AAADhCSU0EDQAAAAAABAAAAB44QklNBBkAAAAAAAQAAAAeOEJJTQPzAAAAAAAJAAAAAAA\n"
+  "AAAABADhCSU0ECgAAAAAAAQAAOEJJTScQAAAAAAAKAAEAAAAAAAAAAThCSU0D9QAAAAAASAAvZm\n"
+  "YAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAAAQA1AAAAA\n"
+  "QAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAAAP//\n"
+  "//////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA///\n"
+  "//////////////////////////wPoAAA4QklNBAgAAAAAABAAAAABAAACQAAAAkAAAAAAOEJJTQ\n"
+  "QeAAAAAAAEAAAAADhCSU0EGgAAAAADRQAAAAYAAAAAAAAAAAAAAGQAAABkAAAACABEAFMAQwAwA\n"
+  "DIAMwAyADUAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAGQAAABkAAAAAAAAAAAA\n"
+  "AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAAAAAABudWxsAAAAAgAAAAZib3VuZHN\n"
+  "PYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAA\n"
+  "AAQnRvbWxvbmcAAABkAAAAAFJnaHRsb25nAAAAZAAAAAZzbGljZXNWbExzAAAAAU9iamMAAAABA\n"
+  "AAAAAAFc2xpY2UAAAASAAAAB3NsaWNlSURsb25nAAAAAAAAAAdncm91cElEbG9uZwAAAAAAAAAG\n"
+  "b3JpZ2luZW51bQAAAAxFU2xpY2VPcmlnaW4AAAANYXV0b0dlbmVyYXRlZAAAAABUeXBlZW51bQA\n"
+  "AAApFU2xpY2VUeXBlAAAAAEltZyAAAAAGYm91bmRzT2JqYwAAAAEAAAAAAABSY3QxAAAABAAAAA\n"
+  "BUb3AgbG9uZwAAAAAAAAAATGVmdGxvbmcAAAAAAAAAAEJ0b21sb25nAAAAZAAAAABSZ2h0bG9uZ\n"
+  "wAAAGQAAAADdXJsVEVYVAAAAAEAAAAAAABudWxsVEVYVAAAAAEAAAAAAABNc2dlVEVYVAAAAAEA\n"
+  "AAAAAAZhbHRUYWdURVhUAAAAAQAAAAAADmNlbGxUZXh0SXNIVE1MYm9vbAEAAAAIY2VsbFRleHR\n"
+  "URVhUAAAAAQAAAAAACWhvcnpBbGlnbmVudW0AAAAPRVNsaWNlSG9yekFsaWduAAAAB2RlZmF1bH\n"
+  "QAAAAJdmVydEFsaWduZW51bQAAAA9FU2xpY2VWZXJ0QWxpZ24AAAAHZGVmYXVsdAAAAAtiZ0Nvb\n"
+  "G9yVHlwZWVudW0AAAARRVNsaWNlQkdDb2xvclR5cGUAAAAATm9uZQAAAAl0b3BPdXRzZXRsb25n\n"
+  "AAAAAAAAAApsZWZ0T3V0c2V0bG9uZwAAAAAAAAAMYm90dG9tT3V0c2V0bG9uZwAAAAAAAAALcml\n"
+  "naHRPdXRzZXRsb25nAAAAAAA4QklNBBEAAAAAAAEBADhCSU0EFAAAAAAABAAAAAE4QklNBAwAAA\n"
+  "AACfkAAAABAAAAZAAAAGQAAAEsAAB1MAAACd0AGAAB/9j/4AAQSkZJRgABAgEASABIAAD/7QAMQ\n"
+  "WRvYmVfQ00AAv/uAA5BZG9iZQBkgAAAAAH/2wCEAAwICAgJCAwJCQwRCwoLERUPDAwPFRgTExUT\n"
+  "ExgRDAwMDAwMEQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBDQsLDQ4NEA4OEBQODg4UFA4\n"
+  "ODg4UEQwMDAwMEREMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDP/AABEIAGQAZA\n"
+  "MBIgACEQEDEQH/3QAEAAf/xAE/AAABBQEBAQEBAQAAAAAAAAADAAECBAUGBwgJCgsBAAEFAQEBA\n"
+  "QEBAAAAAAAAAAEAAgMEBQYHCAkKCxAAAQQBAwIEAgUHBggFAwwzAQACEQMEIRIxBUFRYRMicYEy\n"
+  "BhSRobFCIyQVUsFiMzRygtFDByWSU/Dh8WNzNRaisoMmRJNUZEXCo3Q2F9JV4mXys4TD03Xj80Y\n"
+  "nlKSFtJXE1OT0pbXF1eX1VmZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3EQACAgECBAQDBAUGBwcGBT\n"
+  "UBAAIRAyExEgRBUWFxIhMFMoGRFKGxQiPBUtHwMyRi4XKCkkNTFWNzNPElBhaisoMHJjXC0kSTV\n"
+  "KMXZEVVNnRl4vKzhMPTdePzRpSkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2JzdHV2d3h5ent8f/\n"
+  "2gAMAwEAAhEDEQA/APLtso1NRc0vP0Rok8NYyPEfijOG2ljBoAJPxKFppZtbS4Rz38kV+OPRDge\n"
+  "T89EPHBfvLjtb3P8A30K/j47cgsrYNxGpPYJpK8RtyXUlvPfsobV0GV0uippLiX3EaMb2/rKgMB\n"
+  "1ghoiNST4BESCjjLmxqmKtvxiXQ0cd0q8E2bjIDWjk9z5I8QW8JaoHcdkUePZJtZD9p8YU/Rsc/\n"
+  "wBNjS5zjDWjUk+SSKYaJLYq+qWeYGQ5lBPLJ3OA8wz2/wDSWni/U3H2AXW2l2oloa0f9LcjSLeU\n"
+  "hJdb/wAyqd387Zt+DZ5SSpVh/9DzO6dw7gGPuVn6ft/kyPkqwlxjw1Rnh24QNWjUeR5TSuDc6bg\n"
+  "fatpsJZQ3sNC4rWfkVYpbi4LAb3aANEkFLp7GHGYxuhAj4K/hYVNDjYGzZ++eSSoSbLZjGgwxul\n"
+  "XNrPqO35FukdmzyXOQeqtqwqRg4o/SOAN9ng3/AMzW02txZ9I+ZHKr241UOcWDaz3uLtSSPEpWu\n"
+  "rR5XPeylmyNr4BIPPCyH2Oc6T8kXNvddkPe/VzjJPxQAJMKeIoNScrPk2MbfddXUNXvcGtPx0Xb\n"
+  "dJ6NXjOD2Dfdw6w9v5LFW+q/1WLA3Ly9LSJaz91p/wDRjl2lOLWwAMbEJErWjRgESYieVdZhsMF\n"
+  "wMt08ldrx/vVivHaOdSgCoud9krmElpba93ASTlr/AP/R83ohr97voiJV/Fq9QvsI+mdPgs1thc\n"
+  "BWO5C38CoOY1g78qOejLiGvknxLAyGtExp5K9uzGt9RrNw7DhRfQKKx6bZIGgPj4rPycLqWVtIs\n"
+  "JGu5skDyTBRZtQNrb1fU8xrtpBaO4MLQxcx1sNuEjt5rMGJR9noY5hF7Wxa8aAnxVvDb6bgHH2z\n"
+  "omk0e64ajUUXnev9Idi5rrWAux7SXNd4E/muS+rHSjm9VbPtZjj1CSJBI+g3+0uh69b+iDG/QcD\n"
+  "u0nQCeFP6l0MZhWX/AJ1xM+QafY1TQlY1a+WABsdXp8Sp27aBH+vZaVbC0ADlVcASwtdOolp8Ct\n"
+  "BjmtGv0uI8EmJmxkIjWkmPEKLSPiidxIgJKRbDPCSN5pJyH//S87uw/suZ6c72iC13kVs9PdDmk\n"
+  "KllVziV3cuafc7yP0QjYFh26cqM6hsxAjIj6u6xzbDHh3R663AaceH+5BwdruVp2PqZUA0a9yo6\n"
+  "DPQajscnXb8YQdzC8H909joiZttoxoBIa4gGOQ3uqh+z1RuD2Ds4j2n+39FNKaFMevS/p5LPpSA\n"
+  "I8/b/ABW70THZXj11VjaIAIHgFl5VdD8AneDMaec6Lb6QAKmu7x+VSw2a3MdF/rF9YKeh4B9OHZ\n"
+  "lpAprPH8p7v5DFwrPrV9YDa645toLjMaFo8mtcEvrhkWZHXbg7T0Q2to8o3f8AfkarEitlVTKnY\n"
+  "ra992QQ2wOfG57H2bg7H2fzbFKA130P6n9dHWemCx5H2mk7LgPH818f8IuhAka6ea8y/wAWrcod\n"
+  "VyrceRhBsPae5J/Qj+sxq9KDpMuMuKBCEntnny/1CSaWxM6pIKf/0/MvtF3pCrefTBnbOi1elP3\n"
+  "Et8Vi+Sv9LuNd4HwTSNGSEqkLerwtwn+SYKtOeS4A8Krh2j1D/K1Vu4B5gaDmVDJtAr7zYYGoRB\n"
+  "QDWQ4c9h/csuyjI3fobnDyJR8fF6ltcTaXRwCkAuAsfMGr1TGFNdTmEwLWS2dIK6npLgK2T4Lle\n"
+  "pUZxoc9+6K4eR5NO5bPT73NoqIfoILT/JcFJDZr8zGiNXnvrfiur6/Y8tht7WvaexgbXf8AUrFt\n"
+  "8IExyvRusYDOsYTAIbfWdzHRJ8HN/tLj7OgdRZawmreHP2gt9wEfvtH0f7SkDXe7+o+AOn9DquL\n"
+  "f0mV+leQPH6H+axafUvrB07ptJtyshtTZDTEudJ7bWS5V6MmyltVLn7ht2hwECQP+isb60/Vqvr\n"
+  "tbLsa1lObVIJd9Gxv5rXx9F7fzHpIbf/jgfVnd/TLYj6XoOhJcP/zE+sOzd6dW7dt2eo3dH7/9R\n"
+  "JJWj//U8uiGFx76BFZLQ2xvLeVGAWQrFDJbtKBSHd6blNura4H3BbDXB7InVcZXZdh2bmTt7hbO\n"
+  "J1dj2gzCjlFnhPod3WLHB+n3o9ZsAkFVMfMrs7orLmgkHUdkyqZQQWWQbLGlrjMjUeSrfV3Ltsw\n"
+  "30EBzcd5YCedvLETJya66nWOIAaCVnfU/KuZn21CDVa02PngQdHf9LapMfVhzkaAPUUW3M91YaR\n"
+  "3YDJ+WiBmZGazPo9Kttdt2j63E6s/fft/d/NWjXkMra7KtO2qkE6cErHpvsyMmzPu0dY4Bg/dYP\n"
+  "otTpyoaMUI2XUya8tzG/pi0NMtICo/bsut21gdcWclkj5OncxaDrw6kM+9QxQzaWRAGii4pDqzC\n"
+  "MT02aX7WzPU9b7PrG3bvO6P6yStfZm+pHnPySS4590+3jf/V8yb+CsUbp8uyz0kDskbu2dmz9J8\n"
+  "lSt9Ld+gn1O8cKikmxXydbH+3bhsmfwWj/lONYlcwkhL6L4bfpOxn/tD0/wBN/N944Wh9VJm/b/\n"
+  "O+347df+/rl0k+O38GLJ83X/CfTOt7v2dV6P8AMbx6njHb/pKuN3pN2+IXnaSjybr8e31fUqd+0\n"
+  "Sj487DHMryZJMXjq+sfpPX84SXk6SSX/9kAOEJJTQQhAAAAAABVAAAAAQEAAAAPAEEAZABvAGIA\n"
+  "ZQAgAFAAaABvAHQAbwBzAGgAbwBwAAAAEwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAA\n"
+  "gADcALgAwAAAAAQA4QklNBAYAAAAAAAcABQAAAAEBAP/hFWdodHRwOi8vbnMuYWRvYmUuY29tL3\n"
+  "hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0n77u/JyBpZD0nVzVNME1wQ2VoaUh6cmVTek5UY3prY\n"
+  "zlkJz8+Cjw/YWRvYmUteGFwLWZpbHRlcnMgZXNjPSJDUiI/Pgo8eDp4YXBtZXRhIHhtbG5zOng9\n"
+  "J2Fkb2JlOm5zOm1ldGEvJyB4OnhhcHRrPSdYTVAgdG9vbGtpdCAyLjguMi0zMywgZnJhbWV3b3J\n"
+  "rIDEuNSc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi\n"
+  "1yZGYtc3ludGF4LW5zIycgeG1sbnM6aVg9J2h0dHA6Ly9ucy5hZG9iZS5jb20vaVgvMS4wLyc+C\n"
+  "gogPHJkZjpEZXNjcmlwdGlvbiBhYm91dD0ndXVpZDoyMmQwMmIwYS1iMjQ5LTExZGItOGFmOC05\n"
+  "MWQ1NDAzZjkyZjknCiAgeG1sbnM6cGRmPSdodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvJz4\n"
+  "KICA8IS0tIHBkZjpTdWJqZWN0IGlzIGFsaWFzZWQgLS0+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCi\n"
+  "A8cmRmOkRlc2NyaXB0aW9uIGFib3V0PSd1dWlkOjIyZDAyYjBhLWIyNDktMTFkYi04YWY4LTkxZ\n"
+  "DU0MDNmOTJmOScKICB4bWxuczpwaG90b3Nob3A9J2h0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9z\n"
+  "aG9wLzEuMC8nPgogIDwhLS0gcGhvdG9zaG9wOkNhcHRpb24gaXMgYWxpYXNlZCAtLT4KIDwvcmR\n"
+  "mOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gYWJvdXQ9J3V1aWQ6MjJkMDJiMGEtYj\n"
+  "I0OS0xMWRiLThhZjgtOTFkNTQwM2Y5MmY5JwogIHhtbG5zOnhhcD0naHR0cDovL25zLmFkb2JlL\n"
+  "mNvbS94YXAvMS4wLyc+CiAgPCEtLSB4YXA6RGVzY3JpcHRpb24gaXMgYWxpYXNlZCAtLT4KIDwv\n"
+  "cmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gYWJvdXQ9J3V1aWQ6MjJkMDJiMGE\n"
+  "tYjI0OS0xMWRiLThhZjgtOTFkNTQwM2Y5MmY5JwogIHhtbG5zOnhhcE1NPSdodHRwOi8vbnMuYW\n"
+  "RvYmUuY29tL3hhcC8xLjAvbW0vJz4KICA8eGFwTU06RG9jdW1lbnRJRD5hZG9iZTpkb2NpZDpwa\n"
+  "G90b3Nob3A6MjJkMDJiMDYtYjI0OS0xMWRiLThhZjgtOTFkNTQwM2Y5MmY5PC94YXBNTTpEb2N1\n"
+  "bWVudElEPgogPC9yZGY6RGVzY3JpcHRpb24+CgogPHJkZjpEZXNjcmlwdGlvbiBhYm91dD0ndXV\n"
+  "pZDoyMmQwMmIwYS1iMjQ5LTExZGItOGFmOC05MWQ1NDAzZjkyZjknCiAgeG1sbnM6ZGM9J2h0dH\n"
+  "A6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvJz4KICA8ZGM6ZGVzY3JpcHRpb24+CiAgIDxyZ\n"
+  "GY6QWx0PgogICAgPHJkZjpsaSB4bWw6bGFuZz0neC1kZWZhdWx0Jz4gICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgIDwvcmRmOkFsdD4KICA8L2RjOmRlc2NyaXB0aW9\n"
+  "uPgogPC9yZGY6RGVzY3JpcHRpb24+Cgo8L3JkZjpSREY+CjwveDp4YXBtZXRhPgogICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA\n"
+  "ogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\n"
+  "ICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\n"
+  "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAg\n"
+  "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\n"
+  "gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgIC\n"
+  "AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0ndyc/P\n"
+  "v/uAA5BZG9iZQBkQAAAAAH/2wCEAAQDAwMDAwQDAwQGBAMEBgcFBAQFBwgGBgcGBggKCAkJCQkI\n"
+  "CgoMDAwMDAoMDAwMDAwMDAwMDAwMDAwMDAwMDAwBBAUFCAcIDwoKDxQODg4UFA4ODg4UEQwMDAw\n"
+  "MEREMDAwMDAwRDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDP/AABEIAGQAZAMBEQACEQEDEQ\n"
+  "H/3QAEAA3/xAGiAAAABwEBAQEBAAAAAAAAAAAEBQMCBgEABwgJCgsBAAICAwEBAQEBAAAAAAAAA\n"
+  "AEAAgMEBQYHCAkKCxAAAgEDAwIEAgYHAwQCBgJzAQIDEQQABSESMUFRBhNhInGBFDKRoQcVsUIj\n"
+  "wVLR4TMWYvAkcoLxJUM0U5KismNzwjVEJ5OjszYXVGR0w9LiCCaDCQoYGYSURUaktFbTVSga8uP\n"
+  "zxNTk9GV1hZWltcXV5fVmdoaWprbG1ub2N0dXZ3eHl6e3x9fn9zhIWGh4iJiouMjY6PgpOUlZaX\n"
+  "mJmam5ydnp+So6SlpqeoqaqrrK2ur6EQACAgECAwUFBAUGBAgDA20BAAIRAwQhEjFBBVETYSIGc\n"
+  "YGRMqGx8BTB0eEjQhVSYnLxMyQ0Q4IWklMlomOywgdz0jXiRIMXVJMICQoYGSY2RRonZHRVN/Kj\n"
+  "s8MoKdPj84SUpLTE1OT0ZXWFlaW1xdXl9UZWZnaGlqa2xtbm9kdXZ3eHl6e3x9fn9zhIWGh4iJi\n"
+  "ouMjY6Pg5SVlpeYmZqbnJ2en5KjpKWmp6ipqqusra6vr/2gAMAwEAAhEDEQA/APBnplwPAdR+GB\n"
+  "KY6dYtNG1w39yh4+xb+zIksgEfFaRSSoIx8f7RPRRkSWQimM+lRmwWVXFWYigHxUUVoMiJM+Fj0\n"
+  "tg0RBegLE0Wu+3c+GTBazFCGI7HtSp9slbFYYzyoBsegw2hY1Afl3wqqRqahk+0tDgKpgu4DAUU\n"
+  "+HY+GRS2ePiMKtUB3G+KGuONq//Q8OzpFbW5WnxMop4k9crG5ZnZNJkEOn21utVRYw7HxZtz+OR\n"
+  "vdsrZ2lRtci4aVxFEQA0neg/ZXxJpTITNNuOFss0vSotYNvZ2qGRkPKSTqiU8Sdqk5SZU5Ix8XJ\n"
+  "NNZ8k6bp8TtM73OputUtYq0Unux/hkRkJOzZLCAN2KR+VpbtSkCBaDnIzdlWu59u+XeJTjeASk8\n"
+  "+juZOESEAVqx8BvU/PJibScTrTy09560hkWOGFd2YgFnPQKD19zhOSkxw2l8Vm6XAiYb8gg+k5O\n"
+  "9mnhoon9H3cs5s7WF5pp29OGGMFndyaAKBuTiEEPQLD8h/NDmNdYlttNkYjlbFjcXCr3LLH8II8\n"
+  "C2WUGviZvon/OPWkm3RNSv72SYllMkKxQRV67CQMSKYQAxMkR/wBC56d61P0heel4cYuVOXWvTp\n"
+  "h4Qjjf/9Hw5qBYyISaqjBV+QpvkAzKcki4HomnIxck/wBhtlR2bhunvlDywddMUl4zW+kQ9FQ8X\n"
+  "nfuSewrtmPkycPvc/DhMhvyegXOrWWhmLQPKlsj6xIAiLCoZkY96nv7npmJvI2XOjQFMl0fyRqM\n"
+  "NoxvZvrGt33wlATwiMnVnY1LEdSfuyXF3KIDmUu88w2XlnTl8raAlb2ZFfVL0jdYRtQnxc7BfDC\n"
+  "OaJR7nm3me5tdOtjbMvp3ZRXkV6chVQRX79hmVjgZG+jgZ5jHGhzecXF5LPL6jEjstSSaDM51Ka\n"
+  "6MZ9S1C0sEBe8uZo4YCBXdjxGw60wEWyEqfUHkT8vLXRJFuLdTcaqfhlvWUErtukZ3ABPUjIXTE\n"
+  "m3rGmeV2Tk5UKz/AG/E/wAcgZKya20C3b02kjYtH8AqCygbkUH0nLYlgUb+gbWtPbpXt/n2ybB/\n"
+  "/9Lw4oaVxGd+PxH3qBkGaY3KyiSP01IkiUclH8sg+LKydm6INvZvKsFu+kWtvD8LRoFNRup6moO\n"
+  "aqd277HsGW+XPLmn6XM17FF6l7vW4fd2Zuu+RFls2tmUNrLJb7TSBertGQGqetDkxE0na0pvtHs\n"
+  "QkszWyiGAG5laYlnkeMVHJj8sA5rPk+SvMepTalqlxd3B5zTOXdj/MxqafLpm5xioh5nPK5kpRG\n"
+  "pkcKAST0A6k5NpfUP5K/ki1ssHmHzF+71KRQ8Nud/Qibb/kYw6/yjbrXISlSH07YaHbWyxx2kXE\n"
+  "KACB2zHJtLI7XSelBRvH2xCpvaaTDHXkOTVBPcUG2479RlsdmJVPRtvV+ylenQ0y62FP/9PxRpo\n"
+  "WG5FxKKxKFDA+GVS5NsebLdFsRePc3siVW4f4QR0QVAGYeSXR2unhtZ6s60K6jt+MMSFwtF2+xX\n"
+  "wr7eGUGLlRPQMsE2vxQm7itxKg3VCfT2+nb8cDYaCDtfOXmCCcROrQrUhkkCHYn6emRMqZxjbLd\n"
+  "F1+W/4xajHzjNCtQKMffETWUdngX5p+QZ9A8xS6hbo0ui37NNDPT7DOalHpsCD08Rmyw5ARTpdV\n"
+  "gIPEF35MeRn80ed4S5EdrpKm9kZ15K0iH92hB7Me/tmS60vt/QrCYyekiBdgSTXcjqV9q9MokFD\n"
+  "N7S3aFVVR8RoK9zldqndvAY6nffr/AGYQqLhjdpCoIAZW22HavU/LJBUP9WblX0xTw7fOmWsX/9\n"
+  "Tw7FdvMqWkQ3Z1qfED+mQIbI77PX/LFis9vBajZm2Y+x65rMh3t30Bsze400aVaIbSLk6r8CMRT\n"
+  "l/NmOcllnGDD9Y8uecNfEEiXrMgDGWAyGOOu5WlB+vMrHODTlxZCdjsyFdB006VpVtLasurQxBL\n"
+  "64WiLI4/aFT1ANOXemV5piR2b9NiljB4yyHy9CLOVI5GJhB+CvXY9R8xmINzs5HNZ+Z96BZpbxA\n"
+  "fVJo39UFefwopYgL4nMiMd2qZoIn/AJx00u3t/Lt7qpp9Yv5GLf5MUTERqfbvmzBeezjd9H+VlL\n"
+  "wSQzBqsvOGQD7L12rXsemPNxmXQSxxIPU2nFV4HYqR1xEUWj4ZAxBryr2G+J2VGDZlLrxUH6KZA\n"
+  "Fkqb15VFelfwy+2FP8A/9Xxlf6AdA182Yk9eFeLxSjoVfcfSMo4uIOfkweFOnpvlWYrLEwNFAA+\n"
+  "nMOYdrhFvQLeSO7coBXiK8iKiv07Zj8Ac4QtNrW1njUcKcT+yAR/xGmR4WcsStLpTuPU9IFaEsV\n"
+  "BP3k4m2AgBzSwyQNcIwNTE1aI3wnam9O2Ug7s5Ckk/NDndeVXa2H78MqqV6jmeBp9+ZWKXqDjZ4\n"
+  "+gvVvy30qCy0qzsLRBCnBI2VdgUTqPvOZ7y+Q7pz+bn5q6d+VflZxZlJ/NN4ypptk5qtB9qRwDX\n"
+  "gn/AAx2y2ItpfKFv+eH5qNeTajJ5ovVaVywSqvEtTUKqupAA6D2y0BNPtv/AJx//M5PzL8mJeXT\n"
+  "L+ndPf6rqarSpkAqsnEAAeoN6DpkJRYci9lROSgSUUH9o9K5Tw0ztfSHnXkOtK9q+PHwydq//9b\n"
+  "yxrVoZNBtNSA5zRMPXmH8j0CLXuBmHE+qneamHpEuqYeV7pzFVTRgQK5XMNmnlb1vyyY5QA1OwJ\n"
+  "+eUF2seTOLu5s7azVIVAkpVn/hhnIALG73Yz5jvb1dICqzpDNIqyFD8SxH7R28cxibZCiWOsdJs\n"
+  "PTM6XNstPhnkjIhcHuJBVfvOCiUSn0TfWrTTLjyw8guA/PifTO3xcxxA8a5ZAbimvJP0m3p/kFF\n"
+  "WxhmpWQJ9NW3zZPHz5vlb/nIDVbrWfzO1RJhxGnpDaRL/khA1T7ktmSOTAJhZaAUtLawsbayl8v\n"
+  "xWi3Gpay0cF3HPcFRJJHJMXVrcJ8UaAFG5LWjF8tAYW9H/wCcOo9bTzxrt/owkTyksZW5gkIKvI\n"
+  "7k26nvyReRJHyyBWT7dWQyOWlbnK2526e1O1MqIUFE84uPLkOdK9RXI0E2/wD/1/DA1bURZLY/W\n"
+  "ZDZqwb0eXw7dMgIi7bjllVXsz7yNcfWC0Vd3Ip92Y2UOz0cnsPlwyx8xQ/u24sMxCadoJp9LOXk\n"
+  "VX/uwRUE0BI8cokbLMyoKouHu2MaKGXw7fLDwgoGSkbHpaNZyLLHRSKcFFQQRvUdMlwUFOQyLzr\n"
+  "ztpCaba6fPau4ijv4OURY8AjVFKV7ZZiO+7Vnh6XvXkSWNbW2WTb92KDxIFMzwHlZc3zX+fuizW\n"
+  "f5p3ty8XGDU4YLmCQiisyII3+4rvl8UB5ffEghRGvOm7AbnvWvjk1fen/ONPldPKP5aWOpPCfr2\n"
+  "uE31y6q2wbaMEn+VAMDSdyzrzj+avlHyTp0l/r2rxWFuHWJuIeacu4qFCRgsajfBwsty89/6Gr/\n"
+  "ACa9an+JL/hSnrfoubhXwpXpjwhaL//Q8E1AqtcAZMs8l6i1nqMa1oSVP0VynKLDmaWdSfQXl69\n"
+  "jF1Jv8MhDb5rpB3AO7INRRLhhGp4R05FgaGvTMU8200xS70zVDMRp2pTIOvBmB3PgQP15kxIcnD\n"
+  "LH/EEz0rRvOJhldr9pQtCqyd6VrShGTqw5d4ARv9jHfOGl+ZJNMluLkyenaFbiRdqFYW5nrWuwO\n"
+  "MKB5MdSMRxnhlu9N8p6lLFpti63FUjCtFJTrDKvse2bEDZ4XJ9RZB+YPli2/Mjy5bxoUi1a0YS2\n"
+  "85UOwIXiy9jRu+TBppfOF1+V3m22vrdpNPM8cs/oo0VJlUqQPjValR3+IZNNvtLS9Yu9Mi0/TJr\n"
+  "kyp6QhWVVCIWRATsKBemwwFrDzT87fybs/wA1bW21PRb+DTvNlgGSRp6iC8i3KJJx+y6n7D0Pwm\n"
+  "hxBZXT55/6Fi/Nf0PW+qWXq+t6X1X67F6vD/ftK04V/wBl344U8b//0fBapxheVh9ocV+nviqY2\n"
+  "/qQJDew/bioWHiuQ8m0bbvaPKGtQ6jaxSo9JloCK75gZI0Xb4sgkHo8MouoAvP94BsRmGY7uWJU\n"
+  "gzbypOQpNOvIdK4Nw2WCE2tXulTkjEEbdafgclxMhFBas93dwyQzsWDghlJFONKHJCZtjOFBJfy\n"
+  "j1y9vPL9zpbIs0WkXL2sUjA8hDXlGCRXtt07ZuYvL5KJeo6bfajbkzWkcToR8dqshZ6in2fhNK/\n"
+  "PDTUlXmHVvMdr5o0v9H2kdrqGpfu7m0nkY87Uf7tkKAU4/s03ynLkEBbfihx7dGT6va67LbRMNR\n"
+  "aKOBuUTKgIBXoK1BOYR1M3aQ0mOt9yxUeZNdtJhFapLqMluSXkg5oxJrUMW5KevQ9MmNXXNqOiH\n"
+  "Rr/Hmv8A1r9I/oj95w+r+j9Yf1+NP5+nXtTD+dF8tkfkOlv/0vC3ph7f0/alcVTbS4A8QibuKb5\n"
+  "RI05EBYRFpdX3ly79a2qYCavH/EY7TCYyMD5PSdD8+wXUSn1ArDqOhBzFlipz4ZwWbaV5htbsgF\n"
+  "qg9crMXKErGyYwajFGzxyHlGSePbbwyqg5UZlCaxrFpaWU95LIqrEjMAT4Dp9OShGy1ZslBhv/A\n"
+  "Dj9rd/a+aL+xUK+m38L3d0HrxRo2HFtu5D8c27y8t30raarbWkU+u6g4gsNORn+EcUaSh2Pc0/4\n"
+  "lgtAjezzbT9SutY1i782al8Nxdyotqh6xWybIg+jc5q8s+I27bFDgFPQp9RE+nrag70+L6crrZu\n"
+  "4jajokdv6LW/Dii1Wo61PXKQN3KPK0L+h4/rnD/K5V78a5LhXxd3/0/DMXXtwxVNtL9Xkaf3f7N\n"
+  "etfbKMjdjtkZ9D6ufrlK0+HpX8coF9HJ26sXvfqXrf7i/U+uften/d/wCyrmQL6uOav0pvpP8Ai\n"
+  "b1F+rV59+vH6a5XLhcjH4nRmY/xpxHP0/UptWvT6Mx/RbmjxWK+aP8AFf1M/pCv1Kvxen9inavf\n"
+  "MrFwXtzcLUeLXq5Mv/I3nz1b0v8AjofuKVry9KrUpTanOlf9jmQ68va/zH9b/COn/o7/AI431mP\n"
+  "65SvLh+zWvbl9rMfNfC34K4kmj9T6lD6FKclp/DNYXZx5srsPrHor6nXvkgxTPS/U+rv6dPU5mt\n"
+  "fngFN5ulv+l/pL/Lp/scerHo//2Q==\n";
+
+static std::string gCommandLine;
+
+TEST(Base64, LargeSample) {
+  LOG(LS_VERBOSE) << "Testing specific base64 file";
+
+  char unescaped[64 * 1024];
+
+  // unescape that massive blob above
+  size_t size = Base64Unescape(SpecificTest,
+                            sizeof(SpecificTest),
+                            unescaped,
+                            sizeof(unescaped));
+
+  EXPECT_EQ(size, sizeof(testbase64));
+  EXPECT_EQ(0, memcmp(testbase64, unescaped, sizeof(testbase64)));
+}
+
+bool DecodeTest(const char* encoded, size_t expect_unparsed,
+                const char* decoded, Base64::DecodeFlags flags)
+{
+  std::string result;
+  size_t consumed = 0, encoded_len = strlen(encoded);
+  bool success = Base64::DecodeFromArray(encoded, encoded_len, flags,
+                                         &result, &consumed);
+  size_t unparsed = encoded_len - consumed;
+  EXPECT_EQ(expect_unparsed, unparsed) << "\"" << encoded
+                                       << "\" -> \"" << decoded
+                                       << "\"";
+  EXPECT_STREQ(decoded, result.c_str());
+  return success;
+}
+
+#define Flags(x,y,z) \
+  Base64::DO_PARSE_##x | Base64::DO_PAD_##y | Base64::DO_TERM_##z
+
+TEST(Base64, DecodeParseOptions) {
+  // Trailing whitespace
+  EXPECT_TRUE (DecodeTest("YWJjZA== ", 1, "abcd", Flags(STRICT, YES, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA== ", 0, "abcd", Flags(WHITE,  YES, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA== ", 0, "abcd", Flags(ANY,    YES, CHAR)));
+
+  // Embedded whitespace
+  EXPECT_FALSE(DecodeTest("YWJjZA= =", 3, "abcd", Flags(STRICT, YES, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA= =", 0, "abcd", Flags(WHITE,  YES, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA= =", 0, "abcd", Flags(ANY,    YES, CHAR)));
+
+  // Embedded non-base64 characters
+  EXPECT_FALSE(DecodeTest("YWJjZA=*=", 3, "abcd", Flags(STRICT, YES, CHAR)));
+  EXPECT_FALSE(DecodeTest("YWJjZA=*=", 3, "abcd", Flags(WHITE,  YES, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA=*=", 0, "abcd", Flags(ANY,    YES, CHAR)));
+
+  // Unexpected padding characters
+  EXPECT_FALSE(DecodeTest("YW=JjZA==", 7, "a",    Flags(STRICT, YES, CHAR)));
+  EXPECT_FALSE(DecodeTest("YW=JjZA==", 7, "a",    Flags(WHITE,  YES, CHAR)));
+  EXPECT_TRUE (DecodeTest("YW=JjZA==", 0, "abcd", Flags(ANY,    YES, CHAR)));
+}
+
+TEST(Base64, DecodePadOptions) {
+  // Padding
+  EXPECT_TRUE (DecodeTest("YWJjZA==",  0, "abcd", Flags(STRICT, YES, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA==",  0, "abcd", Flags(STRICT, ANY, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA==",  2, "abcd", Flags(STRICT, NO,  CHAR)));
+
+  // Incomplete padding
+  EXPECT_FALSE(DecodeTest("YWJjZA=",   1, "abcd", Flags(STRICT, YES, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA=",   1, "abcd", Flags(STRICT, ANY, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA=",   1, "abcd", Flags(STRICT, NO,  CHAR)));
+
+  // No padding
+  EXPECT_FALSE(DecodeTest("YWJjZA",    0, "abcd", Flags(STRICT, YES, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA",    0, "abcd", Flags(STRICT, ANY, CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJjZA",    0, "abcd", Flags(STRICT, NO,  CHAR)));
+}
+
+TEST(Base64, DecodeTerminateOptions) {
+  // Complete quantum
+  EXPECT_TRUE (DecodeTest("YWJj",      0, "abc",  Flags(STRICT, NO,  BUFFER)));
+  EXPECT_TRUE (DecodeTest("YWJj",      0, "abc",  Flags(STRICT, NO,  CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJj",      0, "abc",  Flags(STRICT, NO,  ANY)));
+
+  // Complete quantum with trailing data
+  EXPECT_FALSE(DecodeTest("YWJj*",     1, "abc",  Flags(STRICT, NO,  BUFFER)));
+  EXPECT_TRUE (DecodeTest("YWJj*",     1, "abc",  Flags(STRICT, NO,  CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJj*",     1, "abc",  Flags(STRICT, NO,  ANY)));
+
+  // Incomplete quantum
+  EXPECT_FALSE(DecodeTest("YWJ",       0, "ab",   Flags(STRICT, NO,  BUFFER)));
+  EXPECT_FALSE(DecodeTest("YWJ",       0, "ab",   Flags(STRICT, NO,  CHAR)));
+  EXPECT_TRUE (DecodeTest("YWJ",       0, "ab",   Flags(STRICT, NO,  ANY)));
+}
+
+TEST(Base64, GetNextBase64Char) {
+  // The table looks like this:
+  // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+  char next_char;
+  EXPECT_TRUE(Base64::GetNextBase64Char('A', &next_char));
+  EXPECT_EQ('B', next_char);
+  EXPECT_TRUE(Base64::GetNextBase64Char('Z', &next_char));
+  EXPECT_EQ('a', next_char);
+  EXPECT_TRUE(Base64::GetNextBase64Char('/', &next_char));
+  EXPECT_EQ('A', next_char);
+  EXPECT_FALSE(Base64::GetNextBase64Char('&', &next_char));
+  EXPECT_FALSE(Base64::GetNextBase64Char('Z', nullptr));
+}
diff --git a/base/basictypes.h b/base/basictypes.h
index 42ffa5a..87dcdc6 100644
--- a/base/basictypes.h
+++ b/base/basictypes.h
@@ -11,9 +11,60 @@
 #ifndef WEBRTC_BASE_BASICTYPES_H_
 #define WEBRTC_BASE_BASICTYPES_H_
 
+#include <stddef.h>  // for NULL, size_t
+#include <stdint.h>  // for uintptr_t and (u)int_t types.
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/basictypes.h"
+// Detect compiler is for x86 or x64.
+#if defined(__x86_64__) || defined(_M_X64) || \
+    defined(__i386__) || defined(_M_IX86)
+#define CPU_X86 1
+#endif
+
+// Detect compiler is for arm.
+#if defined(__arm__) || defined(_M_ARM)
+#define CPU_ARM 1
+#endif
+
+#if defined(CPU_X86) && defined(CPU_ARM)
+#error CPU_X86 and CPU_ARM both defined.
+#endif
+
+#if !defined(RTC_ARCH_CPU_BIG_ENDIAN) && !defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
+// x86, arm or GCC provided __BYTE_ORDER__ macros
+#if defined(CPU_X86) || defined(CPU_ARM) ||                             \
+  (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#define RTC_ARCH_CPU_LITTLE_ENDIAN
+#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define RTC_ARCH_CPU_BIG_ENDIAN
+#else
+#error RTC_ARCH_CPU_BIG_ENDIAN or RTC_ARCH_CPU_LITTLE_ENDIAN should be defined.
+#endif
+#endif
+
+#if defined(RTC_ARCH_CPU_BIG_ENDIAN) && defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
+#error RTC_ARCH_CPU_BIG_ENDIAN and RTC_ARCH_CPU_LITTLE_ENDIAN both defined.
+#endif
+
+#if defined(WEBRTC_WIN)
+typedef int socklen_t;
+#endif
+
+// The following only works for C++
+#ifdef __cplusplus
+
+#ifndef ALIGNP
+#define ALIGNP(p, t)                                             \
+  (reinterpret_cast<uint8_t*>(((reinterpret_cast<uintptr_t>(p) + \
+  ((t) - 1)) & ~((t) - 1))))
+#endif
+
+#define RTC_IS_ALIGNED(p, a) (!((uintptr_t)(p) & ((a) - 1)))
+
+// Use these to declare and define a static local variable that gets leaked so
+// that its destructors are not called at exit.
+#define RTC_DEFINE_STATIC_LOCAL(type, name, arguments) \
+  static type& name = *new type arguments
+
+#endif  // __cplusplus
 
 #endif  // WEBRTC_BASE_BASICTYPES_H_
diff --git a/base/basictypes_unittest.cc b/base/basictypes_unittest.cc
new file mode 100644
index 0000000..df5ed5e
--- /dev/null
+++ b/base/basictypes_unittest.cc
@@ -0,0 +1,48 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/basictypes.h"
+
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+TEST(BasicTypesTest, Endian) {
+  uint16_t v16 = 0x1234u;
+  uint8_t first_byte = *reinterpret_cast<uint8_t*>(&v16);
+#if defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
+  EXPECT_EQ(0x34u, first_byte);
+#elif defined(RTC_ARCH_CPU_BIG_ENDIAN)
+  EXPECT_EQ(0x12u, first_byte);
+#endif
+}
+
+TEST(BasicTypesTest, SizeOfConstants) {
+  EXPECT_EQ(8u, sizeof(INT64_C(0)));
+  EXPECT_EQ(8u, sizeof(UINT64_C(0)));
+  EXPECT_EQ(8u, sizeof(INT64_C(0x1234567887654321)));
+  EXPECT_EQ(8u, sizeof(UINT64_C(0x8765432112345678)));
+}
+
+// Test CPU_ macros
+#if !defined(CPU_ARM) && defined(__arm__)
+#error expected CPU_ARM to be defined.
+#endif
+#if !defined(CPU_X86) && (defined(WEBRTC_WIN) || defined(WEBRTC_MAC) && !defined(WEBRTC_IOS))
+#error expected CPU_X86 to be defined.
+#endif
+#if !defined(RTC_ARCH_CPU_LITTLE_ENDIAN) && \
+  (defined(WEBRTC_WIN) || defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) || defined(CPU_X86))
+#error expected RTC_ARCH_CPU_LITTLE_ENDIAN to be defined.
+#endif
+
+// TODO(fbarchard): Test all macros in basictypes.h
+
+}  // namespace rtc
diff --git a/base/bind.h b/base/bind.h
index 39d441f..94eb164 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -61,9 +61,224 @@
 #ifndef WEBRTC_BASE_BIND_H_
 #define WEBRTC_BASE_BIND_H_
 
+#include <tuple>
+#include <type_traits>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/bind.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+#include "webrtc/base/template_util.h"
+
+#define NONAME
+
+namespace rtc {
+namespace detail {
+// This is needed because the template parameters in Bind can't be resolved
+// if they're used both as parameters of the function pointer type and as
+// parameters to Bind itself: the function pointer parameters are exact
+// matches to the function prototype, but the parameters to bind have
+// references stripped. This trick allows the compiler to dictate the Bind
+// parameter types rather than deduce them.
+template <class T> struct identity { typedef T type; };
+
+// IsRefCounted<T>::value will be true for types that can be used in
+// rtc::scoped_refptr<T>, i.e. types that implements nullary functions AddRef()
+// and Release(), regardless of their return types. AddRef() and Release() can
+// be defined in T or any superclass of T.
+template <typename T>
+class IsRefCounted {
+  // This is a complex implementation detail done with SFINAE.
+
+  // Define types such that sizeof(Yes) != sizeof(No).
+  struct Yes { char dummy[1]; };
+  struct No { char dummy[2]; };
+  // Define two overloaded template functions with return types of different
+  // size. This way, we can use sizeof() on the return type to determine which
+  // function the compiler would have chosen. One function will be preferred
+  // over the other if it is possible to create it without compiler errors,
+  // otherwise the compiler will simply remove it, and default to the less
+  // preferred function.
+  template <typename R>
+  static Yes test(R* r, decltype(r->AddRef(), r->Release(), 42));
+  template <typename C> static No test(...);
+
+public:
+  // Trick the compiler to tell if it's possible to call AddRef() and Release().
+  static const bool value = sizeof(test<T>((T*)nullptr, 42)) == sizeof(Yes);
+};
+
+// TernaryTypeOperator is a helper class to select a type based on a static bool
+// value.
+template <bool condition, typename IfTrueT, typename IfFalseT>
+struct TernaryTypeOperator {};
+
+template <typename IfTrueT, typename IfFalseT>
+struct TernaryTypeOperator<true, IfTrueT, IfFalseT> {
+  typedef IfTrueT type;
+};
+
+template <typename IfTrueT, typename IfFalseT>
+struct TernaryTypeOperator<false, IfTrueT, IfFalseT> {
+  typedef IfFalseT type;
+};
+
+// PointerType<T>::type will be scoped_refptr<T> for ref counted types, and T*
+// otherwise.
+template <class T>
+struct PointerType {
+  typedef typename TernaryTypeOperator<IsRefCounted<T>::value,
+                                       scoped_refptr<T>,
+                                       T*>::type type;
+};
+
+template <typename T>
+class UnretainedWrapper {
+ public:
+  explicit UnretainedWrapper(T* o) : ptr_(o) {}
+  T* get() const { return ptr_; }
+
+ private:
+  T* ptr_;
+};
+
+}  // namespace detail
+
+template <typename T>
+static inline detail::UnretainedWrapper<T> Unretained(T* o) {
+  return detail::UnretainedWrapper<T>(o);
+}
+
+template <class ObjectT, class MethodT, class R, typename... Args>
+class MethodFunctor {
+ public:
+  MethodFunctor(MethodT method, ObjectT* object, Args... args)
+      : method_(method), object_(object), args_(args...) {}
+  R operator()() const {
+    return CallMethod(typename sequence_generator<sizeof...(Args)>::type());
+  }
+
+ private:
+  // Use sequence_generator (see template_util.h) to expand a MethodFunctor
+  // with 2 arguments to (std::get<0>(args_), std::get<1>(args_)), for
+  // instance.
+  template <int... S>
+  R CallMethod(sequence<S...>) const {
+    return (object_->*method_)(std::get<S>(args_)...);
+  }
+
+  MethodT method_;
+  typename detail::PointerType<ObjectT>::type object_;
+  typename std::tuple<typename std::remove_reference<Args>::type...> args_;
+};
+
+template <class ObjectT, class MethodT, class R, typename... Args>
+class UnretainedMethodFunctor {
+ public:
+  UnretainedMethodFunctor(MethodT method,
+                          detail::UnretainedWrapper<ObjectT> object,
+                          Args... args)
+      : method_(method), object_(object.get()), args_(args...) {}
+  R operator()() const {
+    return CallMethod(typename sequence_generator<sizeof...(Args)>::type());
+  }
+
+ private:
+  // Use sequence_generator (see template_util.h) to expand an
+  // UnretainedMethodFunctor with 2 arguments to (std::get<0>(args_),
+  // std::get<1>(args_)), for instance.
+  template <int... S>
+  R CallMethod(sequence<S...>) const {
+    return (object_->*method_)(std::get<S>(args_)...);
+  }
+
+  MethodT method_;
+  ObjectT* object_;
+  typename std::tuple<typename std::remove_reference<Args>::type...> args_;
+};
+
+template <class FunctorT, class R, typename... Args>
+class Functor {
+ public:
+  Functor(const FunctorT& functor, Args... args)
+      : functor_(functor), args_(args...) {}
+  R operator()() const {
+    return CallFunction(typename sequence_generator<sizeof...(Args)>::type());
+  }
+
+ private:
+  // Use sequence_generator (see template_util.h) to expand a Functor
+  // with 2 arguments to (std::get<0>(args_), std::get<1>(args_)), for
+  // instance.
+  template <int... S>
+  R CallFunction(sequence<S...>) const {
+    return functor_(std::get<S>(args_)...);
+  }
+
+  FunctorT functor_;
+  typename std::tuple<typename std::remove_reference<Args>::type...> args_;
+};
+
+#define FP_T(x) R (ObjectT::*x)(Args...)
+
+template <class ObjectT, class R, typename... Args>
+MethodFunctor<ObjectT, FP_T(NONAME), R, Args...> Bind(
+    FP_T(method),
+    ObjectT* object,
+    typename detail::identity<Args>::type... args) {
+  return MethodFunctor<ObjectT, FP_T(NONAME), R, Args...>(method, object,
+                                                          args...);
+}
+
+template <class ObjectT, class R, typename... Args>
+MethodFunctor<ObjectT, FP_T(NONAME), R, Args...> Bind(
+    FP_T(method),
+    const scoped_refptr<ObjectT>& object,
+    typename detail::identity<Args>::type... args) {
+  return MethodFunctor<ObjectT, FP_T(NONAME), R, Args...>(method, object.get(),
+                                                          args...);
+}
+
+template <class ObjectT, class R, typename... Args>
+UnretainedMethodFunctor<ObjectT, FP_T(NONAME), R, Args...> Bind(
+    FP_T(method),
+    detail::UnretainedWrapper<ObjectT> object,
+    typename detail::identity<Args>::type... args) {
+  return UnretainedMethodFunctor<ObjectT, FP_T(NONAME), R, Args...>(
+      method, object, args...);
+}
+
+#undef FP_T
+#define FP_T(x) R (ObjectT::*x)(Args...) const
+
+template <class ObjectT, class R, typename... Args>
+MethodFunctor<const ObjectT, FP_T(NONAME), R, Args...> Bind(
+    FP_T(method),
+    const ObjectT* object,
+    typename detail::identity<Args>::type... args) {
+  return MethodFunctor<const ObjectT, FP_T(NONAME), R, Args...>(method, object,
+                                                                args...);
+}
+template <class ObjectT, class R, typename... Args>
+UnretainedMethodFunctor<const ObjectT, FP_T(NONAME), R, Args...> Bind(
+    FP_T(method),
+    detail::UnretainedWrapper<const ObjectT> object,
+    typename detail::identity<Args>::type... args) {
+  return UnretainedMethodFunctor<const ObjectT, FP_T(NONAME), R, Args...>(
+      method, object, args...);
+}
+
+#undef FP_T
+#define FP_T(x) R (*x)(Args...)
+
+template <class R, typename... Args>
+Functor<FP_T(NONAME), R, Args...> Bind(
+    FP_T(function),
+    typename detail::identity<Args>::type... args) {
+  return Functor<FP_T(NONAME), R, Args...>(function, args...);
+}
+
+#undef FP_T
+
+}  // namespace rtc
+
+#undef NONAME
 
 #endif  // WEBRTC_BASE_BIND_H_
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc
new file mode 100644
index 0000000..c97a555
--- /dev/null
+++ b/base/bind_unittest.cc
@@ -0,0 +1,222 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <type_traits>
+
+#include "webrtc/base/bind.h"
+#include "webrtc/base/gunit.h"
+
+#include "webrtc/base/refcount.h"
+
+namespace rtc {
+
+namespace {
+
+struct LifeTimeCheck;
+
+struct MethodBindTester {
+  void NullaryVoid() { ++call_count; }
+  int NullaryInt() { ++call_count; return 1; }
+  int NullaryConst() const { ++call_count; return 2; }
+  void UnaryVoid(int dummy) { ++call_count; }
+  template <class T> T Identity(T value) { ++call_count; return value; }
+  int UnaryByPointer(int* value) const {
+    ++call_count;
+    return ++(*value);
+  }
+  int UnaryByRef(const int& value) const {
+    ++call_count;
+    return ++const_cast<int&>(value);
+  }
+  int Multiply(int a, int b) const { ++call_count; return a * b; }
+  void RefArgument(const scoped_refptr<LifeTimeCheck>& object) {
+    EXPECT_TRUE(object.get() != nullptr);
+  }
+
+  mutable int call_count;
+};
+
+struct A { int dummy; };
+struct B: public RefCountInterface { int dummy; };
+struct C: public A, B {};
+struct D {
+  int AddRef();
+};
+struct E: public D {
+  int Release();
+};
+struct F {
+  void AddRef();
+  void Release();
+};
+
+struct LifeTimeCheck {
+  LifeTimeCheck() : ref_count_(0) {}
+  void AddRef() { ++ref_count_; }
+  void Release() { --ref_count_; }
+  void NullaryVoid() {}
+  int ref_count_;
+};
+
+int Return42() { return 42; }
+int Negate(int a) { return -a; }
+int Multiply(int a, int b) { return a * b; }
+
+}  // namespace
+
+// Try to catch any problem with scoped_refptr type deduction in rtc::Bind at
+// compile time.
+#define EXPECT_IS_CAPTURED_AS_PTR(T)                              \
+  static_assert(is_same<detail::PointerType<T>::type, T*>::value, \
+                "PointerType")
+#define EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(T)                        \
+  static_assert(                                                      \
+      is_same<detail::PointerType<T>::type, scoped_refptr<T>>::value, \
+      "PointerType")
+
+EXPECT_IS_CAPTURED_AS_PTR(void);
+EXPECT_IS_CAPTURED_AS_PTR(int);
+EXPECT_IS_CAPTURED_AS_PTR(double);
+EXPECT_IS_CAPTURED_AS_PTR(A);
+EXPECT_IS_CAPTURED_AS_PTR(D);
+EXPECT_IS_CAPTURED_AS_PTR(RefCountInterface*);
+EXPECT_IS_CAPTURED_AS_PTR(
+    decltype(Unretained<RefCountedObject<RefCountInterface>>));
+
+EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountInterface);
+EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(B);
+EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(C);
+EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(E);
+EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(F);
+EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<RefCountInterface>);
+EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<B>);
+EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<C>);
+EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(const RefCountedObject<RefCountInterface>);
+
+TEST(BindTest, BindToMethod) {
+  MethodBindTester object = {0};
+  EXPECT_EQ(0, object.call_count);
+  Bind(&MethodBindTester::NullaryVoid, &object)();
+  EXPECT_EQ(1, object.call_count);
+  EXPECT_EQ(1, Bind(&MethodBindTester::NullaryInt, &object)());
+  EXPECT_EQ(2, object.call_count);
+  EXPECT_EQ(2, Bind(&MethodBindTester::NullaryConst,
+                    static_cast<const MethodBindTester*>(&object))());
+  EXPECT_EQ(3, object.call_count);
+  Bind(&MethodBindTester::UnaryVoid, &object, 5)();
+  EXPECT_EQ(4, object.call_count);
+  EXPECT_EQ(100, Bind(&MethodBindTester::Identity<int>, &object, 100)());
+  EXPECT_EQ(5, object.call_count);
+  const std::string string_value("test string");
+  EXPECT_EQ(string_value, Bind(&MethodBindTester::Identity<std::string>,
+                               &object, string_value)());
+  EXPECT_EQ(6, object.call_count);
+  int value = 11;
+  // Bind binds by value, even if the method signature is by reference, so
+  // "reference" binds require pointers.
+  EXPECT_EQ(12, Bind(&MethodBindTester::UnaryByPointer, &object, &value)());
+  EXPECT_EQ(12, value);
+  EXPECT_EQ(7, object.call_count);
+  // It's possible to bind to a function that takes a const reference, though
+  // the capture will be a copy. See UnaryByRef hackery above where it removes
+  // the const to make sure the underlying storage is, in fact, a copy.
+  EXPECT_EQ(13, Bind(&MethodBindTester::UnaryByRef, &object, value)());
+  // But the original value is unmodified.
+  EXPECT_EQ(12, value);
+  EXPECT_EQ(8, object.call_count);
+  EXPECT_EQ(56, Bind(&MethodBindTester::Multiply, &object, 7, 8)());
+  EXPECT_EQ(9, object.call_count);
+}
+
+TEST(BindTest, BindToFunction) {
+  EXPECT_EQ(42, Bind(&Return42)());
+  EXPECT_EQ(3, Bind(&Negate, -3)());
+  EXPECT_EQ(56, Bind(&Multiply, 8, 7)());
+}
+
+// Test Bind where method object implements RefCountInterface and is passed as a
+// pointer.
+TEST(BindTest, CapturePointerAsScopedRefPtr) {
+  LifeTimeCheck object;
+  EXPECT_EQ(object.ref_count_, 0);
+  scoped_refptr<LifeTimeCheck> scoped_object(&object);
+  EXPECT_EQ(object.ref_count_, 1);
+  {
+    auto functor = Bind(&LifeTimeCheck::NullaryVoid, &object);
+    EXPECT_EQ(object.ref_count_, 2);
+    scoped_object = nullptr;
+    EXPECT_EQ(object.ref_count_, 1);
+  }
+  EXPECT_EQ(object.ref_count_, 0);
+}
+
+// Test Bind where method object implements RefCountInterface and is passed as a
+// scoped_refptr<>.
+TEST(BindTest, CaptureScopedRefPtrAsScopedRefPtr) {
+  LifeTimeCheck object;
+  EXPECT_EQ(object.ref_count_, 0);
+  scoped_refptr<LifeTimeCheck> scoped_object(&object);
+  EXPECT_EQ(object.ref_count_, 1);
+  {
+    auto functor = Bind(&LifeTimeCheck::NullaryVoid, scoped_object);
+    EXPECT_EQ(object.ref_count_, 2);
+    scoped_object = nullptr;
+    EXPECT_EQ(object.ref_count_, 1);
+  }
+  EXPECT_EQ(object.ref_count_, 0);
+}
+
+// Test Bind where method object is captured as scoped_refptr<> and the functor
+// dies while there are references left.
+TEST(BindTest, FunctorReleasesObjectOnDestruction) {
+  LifeTimeCheck object;
+  EXPECT_EQ(object.ref_count_, 0);
+  scoped_refptr<LifeTimeCheck> scoped_object(&object);
+  EXPECT_EQ(object.ref_count_, 1);
+  Bind(&LifeTimeCheck::NullaryVoid, &object)();
+  EXPECT_EQ(object.ref_count_, 1);
+  scoped_object = nullptr;
+  EXPECT_EQ(object.ref_count_, 0);
+}
+
+// Test Bind with scoped_refptr<> argument.
+TEST(BindTest, ScopedRefPointerArgument) {
+  LifeTimeCheck object;
+  EXPECT_EQ(object.ref_count_, 0);
+  scoped_refptr<LifeTimeCheck> scoped_object(&object);
+  EXPECT_EQ(object.ref_count_, 1);
+  {
+    MethodBindTester bind_tester;
+    auto functor =
+        Bind(&MethodBindTester::RefArgument, &bind_tester, scoped_object);
+    EXPECT_EQ(object.ref_count_, 2);
+  }
+  EXPECT_EQ(object.ref_count_, 1);
+  scoped_object = nullptr;
+  EXPECT_EQ(object.ref_count_, 0);
+}
+
+namespace {
+
+const int* Ref(const int& a) { return &a; }
+
+}  // anonymous namespace
+
+// Test Bind with non-scoped_refptr<> reference argument, which should be
+// modified to a non-reference capture.
+TEST(BindTest, RefArgument) {
+  const int x = 42;
+  EXPECT_EQ(&x, Ref(x));
+  // Bind() should make a copy of |x|, i.e. the pointers should be different.
+  auto functor = Bind(&Ref, x);
+  EXPECT_NE(&x, functor());
+}
+
+}  // namespace rtc
diff --git a/base/bitbuffer.cc b/base/bitbuffer.cc
new file mode 100644
index 0000000..4ea2b15
--- /dev/null
+++ b/base/bitbuffer.cc
@@ -0,0 +1,310 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/bitbuffer.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "webrtc/base/checks.h"
+
+namespace {
+
+// Returns the lowest (right-most) |bit_count| bits in |byte|.
+uint8_t LowestBits(uint8_t byte, size_t bit_count) {
+  RTC_DCHECK_LE(bit_count, 8);
+  return byte & ((1 << bit_count) - 1);
+}
+
+// Returns the highest (left-most) |bit_count| bits in |byte|, shifted to the
+// lowest bits (to the right).
+uint8_t HighestBits(uint8_t byte, size_t bit_count) {
+  RTC_DCHECK_LE(bit_count, 8);
+  uint8_t shift = 8 - static_cast<uint8_t>(bit_count);
+  uint8_t mask = 0xFF << shift;
+  return (byte & mask) >> shift;
+}
+
+// Returns the highest byte of |val| in a uint8_t.
+uint8_t HighestByte(uint64_t val) {
+  return static_cast<uint8_t>(val >> 56);
+}
+
+// Returns the result of writing partial data from |source|, of
+// |source_bit_count| size in the highest bits, to |target| at
+// |target_bit_offset| from the highest bit.
+uint8_t WritePartialByte(uint8_t source,
+                         size_t source_bit_count,
+                         uint8_t target,
+                         size_t target_bit_offset) {
+  RTC_DCHECK(target_bit_offset < 8);
+  RTC_DCHECK(source_bit_count < 9);
+  RTC_DCHECK(source_bit_count <= (8 - target_bit_offset));
+  // Generate a mask for just the bits we're going to overwrite, so:
+  uint8_t mask =
+      // The number of bits we want, in the most significant bits...
+      static_cast<uint8_t>(0xFF << (8 - source_bit_count))
+      // ...shifted over to the target offset from the most signficant bit.
+      >> target_bit_offset;
+
+  // We want the target, with the bits we'll overwrite masked off, or'ed with
+  // the bits from the source we want.
+  return (target & ~mask) | (source >> target_bit_offset);
+}
+
+// Counts the number of bits used in the binary representation of val.
+size_t CountBits(uint64_t val) {
+  size_t bit_count = 0;
+  while (val != 0) {
+    bit_count++;
+    val >>= 1;
+  }
+  return bit_count;
+}
+
+}  // namespace
+
+namespace rtc {
+
+BitBuffer::BitBuffer(const uint8_t* bytes, size_t byte_count)
+    : bytes_(bytes), byte_count_(byte_count), byte_offset_(), bit_offset_() {
+  RTC_DCHECK(static_cast<uint64_t>(byte_count_) <=
+             std::numeric_limits<uint32_t>::max());
+}
+
+uint64_t BitBuffer::RemainingBitCount() const {
+  return (static_cast<uint64_t>(byte_count_) - byte_offset_) * 8 - bit_offset_;
+}
+
+bool BitBuffer::ReadUInt8(uint8_t* val) {
+  uint32_t bit_val;
+  if (!ReadBits(&bit_val, sizeof(uint8_t) * 8)) {
+    return false;
+  }
+  RTC_DCHECK(bit_val <= std::numeric_limits<uint8_t>::max());
+  *val = static_cast<uint8_t>(bit_val);
+  return true;
+}
+
+bool BitBuffer::ReadUInt16(uint16_t* val) {
+  uint32_t bit_val;
+  if (!ReadBits(&bit_val, sizeof(uint16_t) * 8)) {
+    return false;
+  }
+  RTC_DCHECK(bit_val <= std::numeric_limits<uint16_t>::max());
+  *val = static_cast<uint16_t>(bit_val);
+  return true;
+}
+
+bool BitBuffer::ReadUInt32(uint32_t* val) {
+  return ReadBits(val, sizeof(uint32_t) * 8);
+}
+
+bool BitBuffer::PeekBits(uint32_t* val, size_t bit_count) {
+  if (!val || bit_count > RemainingBitCount() || bit_count > 32) {
+    return false;
+  }
+  const uint8_t* bytes = bytes_ + byte_offset_;
+  size_t remaining_bits_in_current_byte = 8 - bit_offset_;
+  uint32_t bits = LowestBits(*bytes++, remaining_bits_in_current_byte);
+  // If we're reading fewer bits than what's left in the current byte, just
+  // return the portion of this byte that we need.
+  if (bit_count < remaining_bits_in_current_byte) {
+    *val = HighestBits(bits, bit_offset_ + bit_count);
+    return true;
+  }
+  // Otherwise, subtract what we've read from the bit count and read as many
+  // full bytes as we can into bits.
+  bit_count -= remaining_bits_in_current_byte;
+  while (bit_count >= 8) {
+    bits = (bits << 8) | *bytes++;
+    bit_count -= 8;
+  }
+  // Whatever we have left is smaller than a byte, so grab just the bits we need
+  // and shift them into the lowest bits.
+  if (bit_count > 0) {
+    bits <<= bit_count;
+    bits |= HighestBits(*bytes, bit_count);
+  }
+  *val = bits;
+  return true;
+}
+
+bool BitBuffer::ReadBits(uint32_t* val, size_t bit_count) {
+  return PeekBits(val, bit_count) && ConsumeBits(bit_count);
+}
+
+bool BitBuffer::ConsumeBytes(size_t byte_count) {
+  return ConsumeBits(byte_count * 8);
+}
+
+bool BitBuffer::ConsumeBits(size_t bit_count) {
+  if (bit_count > RemainingBitCount()) {
+    return false;
+  }
+
+  byte_offset_ += (bit_offset_ + bit_count) / 8;
+  bit_offset_ = (bit_offset_ + bit_count) % 8;
+  return true;
+}
+
+bool BitBuffer::ReadExponentialGolomb(uint32_t* val) {
+  if (!val) {
+    return false;
+  }
+  // Store off the current byte/bit offset, in case we want to restore them due
+  // to a failed parse.
+  size_t original_byte_offset = byte_offset_;
+  size_t original_bit_offset = bit_offset_;
+
+  // Count the number of leading 0 bits by peeking/consuming them one at a time.
+  size_t zero_bit_count = 0;
+  uint32_t peeked_bit;
+  while (PeekBits(&peeked_bit, 1) && peeked_bit == 0) {
+    zero_bit_count++;
+    ConsumeBits(1);
+  }
+
+  // We should either be at the end of the stream, or the next bit should be 1.
+  RTC_DCHECK(!PeekBits(&peeked_bit, 1) || peeked_bit == 1);
+
+  // The bit count of the value is the number of zeros + 1. Make sure that many
+  // bits fits in a uint32_t and that we have enough bits left for it, and then
+  // read the value.
+  size_t value_bit_count = zero_bit_count + 1;
+  if (value_bit_count > 32 || !ReadBits(val, value_bit_count)) {
+    RTC_CHECK(Seek(original_byte_offset, original_bit_offset));
+    return false;
+  }
+  *val -= 1;
+  return true;
+}
+
+bool BitBuffer::ReadSignedExponentialGolomb(int32_t* val) {
+  uint32_t unsigned_val;
+  if (!ReadExponentialGolomb(&unsigned_val)) {
+    return false;
+  }
+  if ((unsigned_val & 1) == 0) {
+    *val = -static_cast<int32_t>(unsigned_val / 2);
+  } else {
+    *val = (unsigned_val + 1) / 2;
+  }
+  return true;
+}
+
+void BitBuffer::GetCurrentOffset(
+    size_t* out_byte_offset, size_t* out_bit_offset) {
+  RTC_CHECK(out_byte_offset != nullptr);
+  RTC_CHECK(out_bit_offset != nullptr);
+  *out_byte_offset = byte_offset_;
+  *out_bit_offset = bit_offset_;
+}
+
+bool BitBuffer::Seek(size_t byte_offset, size_t bit_offset) {
+  if (byte_offset > byte_count_ || bit_offset > 7 ||
+      (byte_offset == byte_count_ && bit_offset > 0)) {
+    return false;
+  }
+  byte_offset_ = byte_offset;
+  bit_offset_ = bit_offset;
+  return true;
+}
+
+BitBufferWriter::BitBufferWriter(uint8_t* bytes, size_t byte_count)
+    : BitBuffer(bytes, byte_count), writable_bytes_(bytes) {
+}
+
+bool BitBufferWriter::WriteUInt8(uint8_t val) {
+  return WriteBits(val, sizeof(uint8_t) * 8);
+}
+
+bool BitBufferWriter::WriteUInt16(uint16_t val) {
+  return WriteBits(val, sizeof(uint16_t) * 8);
+}
+
+bool BitBufferWriter::WriteUInt32(uint32_t val) {
+  return WriteBits(val, sizeof(uint32_t) * 8);
+}
+
+bool BitBufferWriter::WriteBits(uint64_t val, size_t bit_count) {
+  if (bit_count > RemainingBitCount()) {
+    return false;
+  }
+  size_t total_bits = bit_count;
+
+  // For simplicity, push the bits we want to read from val to the highest bits.
+  val <<= (sizeof(uint64_t) * 8 - bit_count);
+
+  uint8_t* bytes = writable_bytes_ + byte_offset_;
+
+  // The first byte is relatively special; the bit offset to write to may put us
+  // in the middle of the byte, and the total bit count to write may require we
+  // save the bits at the end of the byte.
+  size_t remaining_bits_in_current_byte = 8 - bit_offset_;
+  size_t bits_in_first_byte =
+      std::min(bit_count, remaining_bits_in_current_byte);
+  *bytes = WritePartialByte(
+      HighestByte(val), bits_in_first_byte, *bytes, bit_offset_);
+  if (bit_count <= remaining_bits_in_current_byte) {
+    // Nothing left to write, so quit early.
+    return ConsumeBits(total_bits);
+  }
+
+  // Subtract what we've written from the bit count, shift it off the value, and
+  // write the remaining full bytes.
+  val <<= bits_in_first_byte;
+  bytes++;
+  bit_count -= bits_in_first_byte;
+  while (bit_count >= 8) {
+    *bytes++ = HighestByte(val);
+    val <<= 8;
+    bit_count -= 8;
+  }
+
+  // Last byte may also be partial, so write the remaining bits from the top of
+  // val.
+  if (bit_count > 0) {
+    *bytes = WritePartialByte(HighestByte(val), bit_count, *bytes, 0);
+  }
+
+  // All done! Consume the bits we've written.
+  return ConsumeBits(total_bits);
+}
+
+bool BitBufferWriter::WriteExponentialGolomb(uint32_t val) {
+  // We don't support reading UINT32_MAX, because it doesn't fit in a uint32_t
+  // when encoded, so don't support writing it either.
+  if (val == std::numeric_limits<uint32_t>::max()) {
+    return false;
+  }
+  uint64_t val_to_encode = static_cast<uint64_t>(val) + 1;
+
+  // We need to write CountBits(val+1) 0s and then val+1. Since val (as a
+  // uint64_t) has leading zeros, we can just write the total golomb encoded
+  // size worth of bits, knowing the value will appear last.
+  return WriteBits(val_to_encode, CountBits(val_to_encode) * 2 - 1);
+}
+
+bool BitBufferWriter::WriteSignedExponentialGolomb(int32_t val) {
+  if (val == 0) {
+    return WriteExponentialGolomb(0);
+  } else if (val > 0) {
+    uint32_t signed_val = val;
+    return WriteExponentialGolomb((signed_val * 2) - 1);
+  } else {
+    if (val == std::numeric_limits<int32_t>::min())
+      return false;  // Not supported, would cause overflow.
+    uint32_t signed_val = -val;
+    return WriteExponentialGolomb(signed_val * 2);
+  }
+}
+
+}  // namespace rtc
diff --git a/base/bitbuffer.h b/base/bitbuffer.h
index 09cba3c..b2baaa9 100644
--- a/base/bitbuffer.h
+++ b/base/bitbuffer.h
@@ -11,9 +11,116 @@
 #ifndef WEBRTC_BASE_BITBUFFER_H_
 #define WEBRTC_BASE_BITBUFFER_H_
 
+#include <stdint.h>  // For integer types.
+#include <stddef.h>  // For size_t.
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/bitbuffer.h"
+#include "webrtc/base/constructormagic.h"
+
+namespace rtc {
+
+// A class, similar to ByteBuffer, that can parse bit-sized data out of a set of
+// bytes. Has a similar API to ByteBuffer, plus methods for reading bit-sized
+// and exponential golomb encoded data. For a writable version, use
+// BitBufferWriter. Unlike ByteBuffer, this class doesn't make a copy of the
+// source bytes, so it can be used on read-only data.
+// Sizes/counts specify bits/bytes, for clarity.
+// Byte order is assumed big-endian/network.
+class BitBuffer {
+ public:
+  BitBuffer(const uint8_t* bytes, size_t byte_count);
+
+  // Gets the current offset, in bytes/bits, from the start of the buffer. The
+  // bit offset is the offset into the current byte, in the range [0,7].
+  void GetCurrentOffset(size_t* out_byte_offset, size_t* out_bit_offset);
+
+  // The remaining bits in the byte buffer.
+  uint64_t RemainingBitCount() const;
+
+  // Reads byte-sized values from the buffer. Returns false if there isn't
+  // enough data left for the specified type.
+  bool ReadUInt8(uint8_t* val);
+  bool ReadUInt16(uint16_t* val);
+  bool ReadUInt32(uint32_t* val);
+
+  // Reads bit-sized values from the buffer. Returns false if there isn't enough
+  // data left for the specified bit count..
+  bool ReadBits(uint32_t* val, size_t bit_count);
+
+  // Peeks bit-sized values from the buffer. Returns false if there isn't enough
+  // data left for the specified number of bits. Doesn't move the current
+  // offset.
+  bool PeekBits(uint32_t* val, size_t bit_count);
+
+  // Reads the exponential golomb encoded value at the current offset.
+  // Exponential golomb values are encoded as:
+  // 1) x = source val + 1
+  // 2) In binary, write [countbits(x) - 1] 0s, then x
+  // To decode, we count the number of leading 0 bits, read that many + 1 bits,
+  // and increment the result by 1.
+  // Returns false if there isn't enough data left for the specified type, or if
+  // the value wouldn't fit in a uint32_t.
+  bool ReadExponentialGolomb(uint32_t* val);
+  // Reads signed exponential golomb values at the current offset. Signed
+  // exponential golomb values are just the unsigned values mapped to the
+  // sequence 0, 1, -1, 2, -2, etc. in order.
+  bool ReadSignedExponentialGolomb(int32_t* val);
+
+  // Moves current position |byte_count| bytes forward. Returns false if
+  // there aren't enough bytes left in the buffer.
+  bool ConsumeBytes(size_t byte_count);
+  // Moves current position |bit_count| bits forward. Returns false if
+  // there aren't enough bits left in the buffer.
+  bool ConsumeBits(size_t bit_count);
+
+  // Sets the current offset to the provied byte/bit offsets. The bit
+  // offset is from the given byte, in the range [0,7].
+  bool Seek(size_t byte_offset, size_t bit_offset);
+
+ protected:
+  const uint8_t* const bytes_;
+  // The total size of |bytes_|.
+  size_t byte_count_;
+  // The current offset, in bytes, from the start of |bytes_|.
+  size_t byte_offset_;
+  // The current offset, in bits, into the current byte.
+  size_t bit_offset_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(BitBuffer);
+};
+
+// A BitBuffer API for write operations. Supports symmetric write APIs to the
+// reading APIs of BitBuffer. Note that the read/write offset is shared with the
+// BitBuffer API, so both reading and writing will consume bytes/bits.
+class BitBufferWriter : public BitBuffer {
+ public:
+  // Constructs a bit buffer for the writable buffer of |bytes|.
+  BitBufferWriter(uint8_t* bytes, size_t byte_count);
+
+  // Writes byte-sized values from the buffer. Returns false if there isn't
+  // enough data left for the specified type.
+  bool WriteUInt8(uint8_t val);
+  bool WriteUInt16(uint16_t val);
+  bool WriteUInt32(uint32_t val);
+
+  // Writes bit-sized values to the buffer. Returns false if there isn't enough
+  // room left for the specified number of bits.
+  bool WriteBits(uint64_t val, size_t bit_count);
+
+  // Writes the exponential golomb encoded version of the supplied value.
+  // Returns false if there isn't enough room left for the value.
+  bool WriteExponentialGolomb(uint32_t val);
+  // Writes the signed exponential golomb version of the supplied value.
+  // Signed exponential golomb values are just the unsigned values mapped to the
+  // sequence 0, 1, -1, 2, -2, etc. in order.
+  bool WriteSignedExponentialGolomb(int32_t val);
+
+ private:
+  // The buffer, as a writable array.
+  uint8_t* const writable_bytes_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(BitBufferWriter);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_BITBUFFER_H_
diff --git a/base/bitbuffer_unittest.cc b/base/bitbuffer_unittest.cc
new file mode 100644
index 0000000..1614299
--- /dev/null
+++ b/base/bitbuffer_unittest.cc
@@ -0,0 +1,329 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/bitbuffer.h"
+#include "webrtc/base/bytebuffer.h"
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+TEST(BitBufferTest, ConsumeBits) {
+  const uint8_t bytes[64] = {0};
+  BitBuffer buffer(bytes, 32);
+  uint64_t total_bits = 32 * 8;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  total_bits -= 3;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  total_bits -= 3;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+  EXPECT_TRUE(buffer.ConsumeBits(15));
+  total_bits -= 15;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+  EXPECT_TRUE(buffer.ConsumeBits(37));
+  total_bits -= 37;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+
+  EXPECT_FALSE(buffer.ConsumeBits(32 * 8));
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+}
+
+TEST(BitBufferTest, ReadBytesAligned) {
+  const uint8_t bytes[] = {0x0A, 0xBC, 0xDE, 0xF1, 0x23, 0x45, 0x67, 0x89};
+  uint8_t val8;
+  uint16_t val16;
+  uint32_t val32;
+  BitBuffer buffer(bytes, 8);
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0x0Au, val8);
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0xBCu, val8);
+  EXPECT_TRUE(buffer.ReadUInt16(&val16));
+  EXPECT_EQ(0xDEF1u, val16);
+  EXPECT_TRUE(buffer.ReadUInt32(&val32));
+  EXPECT_EQ(0x23456789u, val32);
+}
+
+TEST(BitBufferTest, ReadBytesOffset4) {
+  const uint8_t bytes[] = {0x0A, 0xBC, 0xDE, 0xF1, 0x23,
+                           0x45, 0x67, 0x89, 0x0A};
+  uint8_t val8;
+  uint16_t val16;
+  uint32_t val32;
+  BitBuffer buffer(bytes, 9);
+  EXPECT_TRUE(buffer.ConsumeBits(4));
+
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0xABu, val8);
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0xCDu, val8);
+  EXPECT_TRUE(buffer.ReadUInt16(&val16));
+  EXPECT_EQ(0xEF12u, val16);
+  EXPECT_TRUE(buffer.ReadUInt32(&val32));
+  EXPECT_EQ(0x34567890u, val32);
+}
+
+TEST(BitBufferTest, ReadBytesOffset3) {
+  // The pattern we'll check against is counting down from 0b1111. It looks
+  // weird here because it's all offset by 3.
+  // Byte pattern is:
+  //    56701234
+  //  0b00011111,
+  //  0b11011011,
+  //  0b10010111,
+  //  0b01010011,
+  //  0b00001110,
+  //  0b11001010,
+  //  0b10000110,
+  //  0b01000010
+  //       xxxxx <-- last 5 bits unused.
+
+  // The bytes. It almost looks like counting down by two at a time, except the
+  // jump at 5->3->0, since that's when the high bit is turned off.
+  const uint8_t bytes[] = {0x1F, 0xDB, 0x97, 0x53, 0x0E, 0xCA, 0x86, 0x42};
+
+  uint8_t val8;
+  uint16_t val16;
+  uint32_t val32;
+  BitBuffer buffer(bytes, 8);
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0xFEu, val8);
+  EXPECT_TRUE(buffer.ReadUInt16(&val16));
+  EXPECT_EQ(0xDCBAu, val16);
+  EXPECT_TRUE(buffer.ReadUInt32(&val32));
+  EXPECT_EQ(0x98765432u, val32);
+  // 5 bits left unread. Not enough to read a uint8_t.
+  EXPECT_EQ(5u, buffer.RemainingBitCount());
+  EXPECT_FALSE(buffer.ReadUInt8(&val8));
+}
+
+TEST(BitBufferTest, ReadBits) {
+  // Bit values are:
+  //  0b01001101,
+  //  0b00110010
+  const uint8_t bytes[] = {0x4D, 0x32};
+  uint32_t val;
+  BitBuffer buffer(bytes, 2);
+  EXPECT_TRUE(buffer.ReadBits(&val, 3));
+  // 0b010
+  EXPECT_EQ(0x2u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 2));
+  // 0b01
+  EXPECT_EQ(0x1u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 7));
+  // 0b1010011
+  EXPECT_EQ(0x53u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 2));
+  // 0b00
+  EXPECT_EQ(0x0u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 1));
+  // 0b1
+  EXPECT_EQ(0x1u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 1));
+  // 0b0
+  EXPECT_EQ(0x0u, val);
+
+  EXPECT_FALSE(buffer.ReadBits(&val, 1));
+}
+
+TEST(BitBufferTest, SetOffsetValues) {
+  uint8_t bytes[4] = {0};
+  BitBufferWriter buffer(bytes, 4);
+
+  size_t byte_offset, bit_offset;
+  // Bit offsets are [0,7].
+  EXPECT_TRUE(buffer.Seek(0, 0));
+  EXPECT_TRUE(buffer.Seek(0, 7));
+  buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+  EXPECT_EQ(0u, byte_offset);
+  EXPECT_EQ(7u, bit_offset);
+  EXPECT_FALSE(buffer.Seek(0, 8));
+  buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+  EXPECT_EQ(0u, byte_offset);
+  EXPECT_EQ(7u, bit_offset);
+  // Byte offsets are [0,length]. At byte offset length, the bit offset must be
+  // 0.
+  EXPECT_TRUE(buffer.Seek(0, 0));
+  EXPECT_TRUE(buffer.Seek(2, 4));
+  buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+  EXPECT_EQ(2u, byte_offset);
+  EXPECT_EQ(4u, bit_offset);
+  EXPECT_TRUE(buffer.Seek(4, 0));
+  EXPECT_FALSE(buffer.Seek(5, 0));
+  buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+  EXPECT_EQ(4u, byte_offset);
+  EXPECT_EQ(0u, bit_offset);
+  EXPECT_FALSE(buffer.Seek(4, 1));
+
+  // Disable death test on Android because it relies on fork() and doesn't play
+  // nicely.
+#if GTEST_HAS_DEATH_TEST
+#if !defined(WEBRTC_ANDROID)
+  // Passing a null out parameter is death.
+  EXPECT_DEATH(buffer.GetCurrentOffset(&byte_offset, nullptr), "");
+#endif
+#endif
+}
+
+uint64_t GolombEncoded(uint32_t val) {
+  val++;
+  uint32_t bit_counter = val;
+  uint64_t bit_count = 0;
+  while (bit_counter > 0) {
+    bit_count++;
+    bit_counter >>= 1;
+  }
+  return static_cast<uint64_t>(val) << (64 - (bit_count * 2 - 1));
+}
+
+TEST(BitBufferTest, GolombUint32Values) {
+  ByteBufferWriter byteBuffer;
+  byteBuffer.Resize(16);
+  BitBuffer buffer(reinterpret_cast<const uint8_t*>(byteBuffer.Data()),
+                   byteBuffer.Capacity());
+  // Test over the uint32_t range with a large enough step that the test doesn't
+  // take forever. Around 20,000 iterations should do.
+  const int kStep = std::numeric_limits<uint32_t>::max() / 20000;
+  for (uint32_t i = 0; i < std::numeric_limits<uint32_t>::max() - kStep;
+       i += kStep) {
+    uint64_t encoded_val = GolombEncoded(i);
+    byteBuffer.Clear();
+    byteBuffer.WriteUInt64(encoded_val);
+    uint32_t decoded_val;
+    EXPECT_TRUE(buffer.Seek(0, 0));
+    EXPECT_TRUE(buffer.ReadExponentialGolomb(&decoded_val));
+    EXPECT_EQ(i, decoded_val);
+  }
+}
+
+TEST(BitBufferTest, SignedGolombValues) {
+  uint8_t golomb_bits[] = {
+      0x80,  // 1
+      0x40,  // 010
+      0x60,  // 011
+      0x20,  // 00100
+      0x38,  // 00111
+  };
+  int32_t expected[] = {0, 1, -1, 2, -3};
+  for (size_t i = 0; i < sizeof(golomb_bits); ++i) {
+    BitBuffer buffer(&golomb_bits[i], 1);
+    int32_t decoded_val;
+    ASSERT_TRUE(buffer.ReadSignedExponentialGolomb(&decoded_val));
+    EXPECT_EQ(expected[i], decoded_val)
+        << "Mismatch in expected/decoded value for golomb_bits[" << i
+        << "]: " << static_cast<int>(golomb_bits[i]);
+  }
+}
+
+TEST(BitBufferTest, NoGolombOverread) {
+  const uint8_t bytes[] = {0x00, 0xFF, 0xFF};
+  // Make sure the bit buffer correctly enforces byte length on golomb reads.
+  // If it didn't, the above buffer would be valid at 3 bytes.
+  BitBuffer buffer(bytes, 1);
+  uint32_t decoded_val;
+  EXPECT_FALSE(buffer.ReadExponentialGolomb(&decoded_val));
+
+  BitBuffer longer_buffer(bytes, 2);
+  EXPECT_FALSE(longer_buffer.ReadExponentialGolomb(&decoded_val));
+
+  BitBuffer longest_buffer(bytes, 3);
+  EXPECT_TRUE(longest_buffer.ReadExponentialGolomb(&decoded_val));
+  // Golomb should have read 9 bits, so 0x01FF, and since it is golomb, the
+  // result is 0x01FF - 1 = 0x01FE.
+  EXPECT_EQ(0x01FEu, decoded_val);
+}
+
+TEST(BitBufferWriterTest, SymmetricReadWrite) {
+  uint8_t bytes[16] = {0};
+  BitBufferWriter buffer(bytes, 4);
+
+  // Write some bit data at various sizes.
+  EXPECT_TRUE(buffer.WriteBits(0x2u, 3));
+  EXPECT_TRUE(buffer.WriteBits(0x1u, 2));
+  EXPECT_TRUE(buffer.WriteBits(0x53u, 7));
+  EXPECT_TRUE(buffer.WriteBits(0x0u, 2));
+  EXPECT_TRUE(buffer.WriteBits(0x1u, 1));
+  EXPECT_TRUE(buffer.WriteBits(0x1ABCDu, 17));
+  // That should be all that fits in the buffer.
+  EXPECT_FALSE(buffer.WriteBits(1, 1));
+
+  EXPECT_TRUE(buffer.Seek(0, 0));
+  uint32_t val;
+  EXPECT_TRUE(buffer.ReadBits(&val, 3));
+  EXPECT_EQ(0x2u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 2));
+  EXPECT_EQ(0x1u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 7));
+  EXPECT_EQ(0x53u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 2));
+  EXPECT_EQ(0x0u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 1));
+  EXPECT_EQ(0x1u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 17));
+  EXPECT_EQ(0x1ABCDu, val);
+  // And there should be nothing left.
+  EXPECT_FALSE(buffer.ReadBits(&val, 1));
+}
+
+TEST(BitBufferWriterTest, SymmetricBytesMisaligned) {
+  uint8_t bytes[16] = {0};
+  BitBufferWriter buffer(bytes, 16);
+
+  // Offset 3, to get things misaligned.
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  EXPECT_TRUE(buffer.WriteUInt8(0x12u));
+  EXPECT_TRUE(buffer.WriteUInt16(0x3456u));
+  EXPECT_TRUE(buffer.WriteUInt32(0x789ABCDEu));
+
+  buffer.Seek(0, 3);
+  uint8_t val8;
+  uint16_t val16;
+  uint32_t val32;
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0x12u, val8);
+  EXPECT_TRUE(buffer.ReadUInt16(&val16));
+  EXPECT_EQ(0x3456u, val16);
+  EXPECT_TRUE(buffer.ReadUInt32(&val32));
+  EXPECT_EQ(0x789ABCDEu, val32);
+}
+
+TEST(BitBufferWriterTest, SymmetricGolomb) {
+  char test_string[] = "my precious";
+  uint8_t bytes[64] = {0};
+  BitBufferWriter buffer(bytes, 64);
+  for (size_t i = 0; i < arraysize(test_string); ++i) {
+    EXPECT_TRUE(buffer.WriteExponentialGolomb(test_string[i]));
+  }
+  buffer.Seek(0, 0);
+  for (size_t i = 0; i < arraysize(test_string); ++i) {
+    uint32_t val;
+    EXPECT_TRUE(buffer.ReadExponentialGolomb(&val));
+    EXPECT_LE(val, std::numeric_limits<uint8_t>::max());
+    EXPECT_EQ(test_string[i], static_cast<char>(val));
+  }
+}
+
+TEST(BitBufferWriterTest, WriteClearsBits) {
+  uint8_t bytes[] = {0xFF, 0xFF};
+  BitBufferWriter buffer(bytes, 2);
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  EXPECT_TRUE(buffer.WriteBits(0, 1));
+  EXPECT_EQ(0xEFu, bytes[0]);
+  EXPECT_TRUE(buffer.WriteBits(0, 3));
+  EXPECT_EQ(0xE1u, bytes[0]);
+  EXPECT_TRUE(buffer.WriteBits(0, 2));
+  EXPECT_EQ(0xE0u, bytes[0]);
+  EXPECT_EQ(0x7F, bytes[1]);
+}
+
+}  // namespace rtc
diff --git a/base/buffer.h b/base/buffer.h
index 92c85d9..ecc4b23 100644
--- a/base/buffer.h
+++ b/base/buffer.h
@@ -11,8 +11,373 @@
 #ifndef WEBRTC_BASE_BUFFER_H_
 #define WEBRTC_BASE_BUFFER_H_
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/buffer.h"
+#include <algorithm>
+#include <cstring>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "webrtc/base/array_view.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/type_traits.h"
+
+namespace rtc {
+
+namespace internal {
+
+// (Internal; please don't use outside this file.) Determines if elements of
+// type U are compatible with a BufferT<T>. For most types, we just ignore
+// top-level const and forbid top-level volatile and require T and U to be
+// otherwise equal, but all byte-sized integers (notably char, int8_t, and
+// uint8_t) are compatible with each other. (Note: We aim to get rid of this
+// behavior, and treat all types the same.)
+template <typename T, typename U>
+struct BufferCompat {
+  static constexpr bool value =
+      !std::is_volatile<U>::value &&
+      ((std::is_integral<T>::value && sizeof(T) == 1)
+           ? (std::is_integral<U>::value && sizeof(U) == 1)
+           : (std::is_same<T, typename std::remove_const<U>::type>::value));
+};
+
+}  // namespace internal
+
+// Basic buffer class, can be grown and shrunk dynamically.
+// Unlike std::string/vector, does not initialize data when increasing size.
+template <typename T>
+class BufferT {
+  // We want T's destructor and default constructor to be trivial, i.e. perform
+  // no action, so that we don't have to touch the memory we allocate and
+  // deallocate. And we want T to be trivially copyable, so that we can copy T
+  // instances with std::memcpy. This is precisely the definition of a trivial
+  // type.
+  static_assert(std::is_trivial<T>::value, "T must be a trivial type.");
+
+  // This class relies heavily on being able to mutate its data.
+  static_assert(!std::is_const<T>::value, "T may not be const");
+
+ public:
+  using value_type = T;
+
+  // An empty BufferT.
+  BufferT() : size_(0), capacity_(0), data_(nullptr) {
+    RTC_DCHECK(IsConsistent());
+  }
+
+  // Disable copy construction and copy assignment, since copying a buffer is
+  // expensive enough that we want to force the user to be explicit about it.
+  BufferT(const BufferT&) = delete;
+  BufferT& operator=(const BufferT&) = delete;
+
+  BufferT(BufferT&& buf)
+      : size_(buf.size()),
+        capacity_(buf.capacity()),
+        data_(std::move(buf.data_)) {
+    RTC_DCHECK(IsConsistent());
+    buf.OnMovedFrom();
+  }
+
+  // Construct a buffer with the specified number of uninitialized elements.
+  explicit BufferT(size_t size) : BufferT(size, size) {}
+
+  BufferT(size_t size, size_t capacity)
+      : size_(size),
+        capacity_(std::max(size, capacity)),
+        data_(new T[capacity_]) {
+    RTC_DCHECK(IsConsistent());
+  }
+
+  // Construct a buffer and copy the specified number of elements into it.
+  template <typename U,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  BufferT(const U* data, size_t size) : BufferT(data, size, size) {}
+
+  template <typename U,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  BufferT(U* data, size_t size, size_t capacity) : BufferT(size, capacity) {
+    static_assert(sizeof(T) == sizeof(U), "");
+    std::memcpy(data_.get(), data, size * sizeof(U));
+  }
+
+  // Construct a buffer from the contents of an array.
+  template <typename U,
+            size_t N,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  BufferT(U (&array)[N]) : BufferT(array, N) {}
+
+  // Get a pointer to the data. Just .data() will give you a (const) T*, but if
+  // T is a byte-sized integer, you may also use .data<U>() for any other
+  // byte-sized integer U.
+  template <typename U = T,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  const U* data() const {
+    RTC_DCHECK(IsConsistent());
+    return reinterpret_cast<U*>(data_.get());
+  }
+
+  template <typename U = T,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  U* data() {
+    RTC_DCHECK(IsConsistent());
+    return reinterpret_cast<U*>(data_.get());
+  }
+
+  bool empty() const {
+    RTC_DCHECK(IsConsistent());
+    return size_ == 0;
+  }
+
+  size_t size() const {
+    RTC_DCHECK(IsConsistent());
+    return size_;
+  }
+
+  size_t capacity() const {
+    RTC_DCHECK(IsConsistent());
+    return capacity_;
+  }
+
+  BufferT& operator=(BufferT&& buf) {
+    RTC_DCHECK(IsConsistent());
+    RTC_DCHECK(buf.IsConsistent());
+    size_ = buf.size_;
+    capacity_ = buf.capacity_;
+    data_ = std::move(buf.data_);
+    buf.OnMovedFrom();
+    return *this;
+  }
+
+  bool operator==(const BufferT& buf) const {
+    RTC_DCHECK(IsConsistent());
+    if (size_ != buf.size_) {
+      return false;
+    }
+    if (std::is_integral<T>::value) {
+      // Optimization.
+      return std::memcmp(data_.get(), buf.data_.get(), size_ * sizeof(T)) == 0;
+    }
+    for (size_t i = 0; i < size_; ++i) {
+      if (data_[i] != buf.data_[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool operator!=(const BufferT& buf) const { return !(*this == buf); }
+
+  T& operator[](size_t index) {
+    RTC_DCHECK_LT(index, size_);
+    return data()[index];
+  }
+
+  T operator[](size_t index) const {
+    RTC_DCHECK_LT(index, size_);
+    return data()[index];
+  }
+
+  T* begin() { return data(); }
+  T* end() { return data() + size(); }
+  const T* begin() const { return data(); }
+  const T* end() const { return data() + size(); }
+  const T* cbegin() const { return data(); }
+  const T* cend() const { return data() + size(); }
+
+  // The SetData functions replace the contents of the buffer. They accept the
+  // same input types as the constructors.
+  template <typename U,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  void SetData(const U* data, size_t size) {
+    RTC_DCHECK(IsConsistent());
+    size_ = 0;
+    AppendData(data, size);
+  }
+
+  template <typename U,
+            size_t N,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  void SetData(const U (&array)[N]) {
+    SetData(array, N);
+  }
+
+  template <typename W,
+            typename std::enable_if<
+                HasDataAndSize<const W, const T>::value>::type* = nullptr>
+  void SetData(const W& w) {
+    SetData(w.data(), w.size());
+  }
+
+  // Replace the data in the buffer with at most |max_elements| of data, using
+  // the function |setter|, which should have the following signature:
+  //   size_t setter(ArrayView<U> view)
+  // |setter| is given an appropriately typed ArrayView of the area in which to
+  // write the data (i.e. starting at the beginning of the buffer) and should
+  // return the number of elements actually written. This number must be <=
+  // |max_elements|.
+  template <typename U = T,
+            typename F,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  size_t SetData(size_t max_elements, F&& setter) {
+    RTC_DCHECK(IsConsistent());
+    size_ = 0;
+    return AppendData<U>(max_elements, std::forward<F>(setter));
+  }
+
+  // The AppendData functions add data to the end of the buffer. They accept
+  // the same input types as the constructors.
+  template <typename U,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  void AppendData(const U* data, size_t size) {
+    RTC_DCHECK(IsConsistent());
+    const size_t new_size = size_ + size;
+    EnsureCapacityWithHeadroom(new_size, true);
+    static_assert(sizeof(T) == sizeof(U), "");
+    std::memcpy(data_.get() + size_, data, size * sizeof(U));
+    size_ = new_size;
+    RTC_DCHECK(IsConsistent());
+  }
+
+  template <typename U,
+            size_t N,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  void AppendData(const U (&array)[N]) {
+    AppendData(array, N);
+  }
+
+  template <typename W,
+            typename std::enable_if<
+                HasDataAndSize<const W, const T>::value>::type* = nullptr>
+  void AppendData(const W& w) {
+    AppendData(w.data(), w.size());
+  }
+
+  template <typename U,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  void AppendData(const U& item) {
+    AppendData(&item, 1);
+  }
+
+  // Append at most |max_elements| to the end of the buffer, using the function
+  // |setter|, which should have the following signature:
+  //   size_t setter(ArrayView<U> view)
+  // |setter| is given an appropriately typed ArrayView of the area in which to
+  // write the data (i.e. starting at the former end of the buffer) and should
+  // return the number of elements actually written. This number must be <=
+  // |max_elements|.
+  template <typename U = T,
+            typename F,
+            typename std::enable_if<
+                internal::BufferCompat<T, U>::value>::type* = nullptr>
+  size_t AppendData(size_t max_elements, F&& setter) {
+    RTC_DCHECK(IsConsistent());
+    const size_t old_size = size_;
+    SetSize(old_size + max_elements);
+    U* base_ptr = data<U>() + old_size;
+    size_t written_elements = setter(rtc::ArrayView<U>(base_ptr, max_elements));
+
+    RTC_CHECK_LE(written_elements, max_elements);
+    size_ = old_size + written_elements;
+    RTC_DCHECK(IsConsistent());
+    return written_elements;
+  }
+
+  // Sets the size of the buffer. If the new size is smaller than the old, the
+  // buffer contents will be kept but truncated; if the new size is greater,
+  // the existing contents will be kept and the new space will be
+  // uninitialized.
+  void SetSize(size_t size) {
+    EnsureCapacityWithHeadroom(size, true);
+    size_ = size;
+  }
+
+  // Ensure that the buffer size can be increased to at least capacity without
+  // further reallocation. (Of course, this operation might need to reallocate
+  // the buffer.)
+  void EnsureCapacity(size_t capacity) {
+    // Don't allocate extra headroom, since the user is asking for a specific
+    // capacity.
+    EnsureCapacityWithHeadroom(capacity, false);
+  }
+
+  // Resets the buffer to zero size without altering capacity. Works even if the
+  // buffer has been moved from.
+  void Clear() {
+    size_ = 0;
+    RTC_DCHECK(IsConsistent());
+  }
+
+  // Swaps two buffers. Also works for buffers that have been moved from.
+  friend void swap(BufferT& a, BufferT& b) {
+    using std::swap;
+    swap(a.size_, b.size_);
+    swap(a.capacity_, b.capacity_);
+    swap(a.data_, b.data_);
+  }
+
+ private:
+  void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) {
+    RTC_DCHECK(IsConsistent());
+    if (capacity <= capacity_)
+      return;
+
+    // If the caller asks for extra headroom, ensure that the new capacity is
+    // >= 1.5 times the old capacity. Any constant > 1 is sufficient to prevent
+    // quadratic behavior; as to why we pick 1.5 in particular, see
+    // https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md and
+    // http://www.gahcep.com/cpp-internals-stl-vector-part-1/.
+    const size_t new_capacity =
+        extra_headroom ? std::max(capacity, capacity_ + capacity_ / 2)
+                       : capacity;
+
+    std::unique_ptr<T[]> new_data(new T[new_capacity]);
+    std::memcpy(new_data.get(), data_.get(), size_ * sizeof(T));
+    data_ = std::move(new_data);
+    capacity_ = new_capacity;
+    RTC_DCHECK(IsConsistent());
+  }
+
+  // Precondition for all methods except Clear and the destructor.
+  // Postcondition for all methods except move construction and move
+  // assignment, which leave the moved-from object in a possibly inconsistent
+  // state.
+  bool IsConsistent() const {
+    return (data_ || capacity_ == 0) && capacity_ >= size_;
+  }
+
+  // Called when *this has been moved from. Conceptually it's a no-op, but we
+  // can mutate the state slightly to help subsequent sanity checks catch bugs.
+  void OnMovedFrom() {
+#if RTC_DCHECK_IS_ON
+    // Make *this consistent and empty. Shouldn't be necessary, but better safe
+    // than sorry.
+    size_ = 0;
+    capacity_ = 0;
+#else
+    // Ensure that *this is always inconsistent, to provoke bugs.
+    size_ = 1;
+    capacity_ = 0;
+#endif
+  }
+
+  size_t size_;
+  size_t capacity_;
+  std::unique_ptr<T[]> data_;
+};
+
+// By far the most common sort of buffer.
+using Buffer = BufferT<uint8_t>;
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_BUFFER_H_
diff --git a/base/buffer_unittest.cc b/base/buffer_unittest.cc
new file mode 100644
index 0000000..82baedb
--- /dev/null
+++ b/base/buffer_unittest.cc
@@ -0,0 +1,437 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/buffer.h"
+
+#include "webrtc/base/array_view.h"
+#include "webrtc/base/gunit.h"
+
+#include <type_traits>
+#include <utility>
+
+namespace rtc {
+
+namespace {
+
+// clang-format off
+const uint8_t kTestData[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
+                             0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+// clang-format on
+
+void TestBuf(const Buffer& b1, size_t size, size_t capacity) {
+  EXPECT_EQ(b1.size(), size);
+  EXPECT_EQ(b1.capacity(), capacity);
+}
+
+}  // namespace
+
+TEST(BufferTest, TestConstructEmpty) {
+  TestBuf(Buffer(), 0, 0);
+  TestBuf(Buffer(Buffer()), 0, 0);
+  TestBuf(Buffer(0), 0, 0);
+
+  // We can't use a literal 0 for the first argument, because C++ will allow
+  // that to be considered a null pointer, which makes the call ambiguous.
+  TestBuf(Buffer(0 + 0, 10), 0, 10);
+
+  TestBuf(Buffer(kTestData, 0), 0, 0);
+  TestBuf(Buffer(kTestData, 0, 20), 0, 20);
+}
+
+TEST(BufferTest, TestConstructData) {
+  Buffer buf(kTestData, 7);
+  EXPECT_EQ(buf.size(), 7u);
+  EXPECT_EQ(buf.capacity(), 7u);
+  EXPECT_FALSE(buf.empty());
+  EXPECT_EQ(0, memcmp(buf.data(), kTestData, 7));
+}
+
+TEST(BufferTest, TestConstructDataWithCapacity) {
+  Buffer buf(kTestData, 7, 14);
+  EXPECT_EQ(buf.size(), 7u);
+  EXPECT_EQ(buf.capacity(), 14u);
+  EXPECT_FALSE(buf.empty());
+  EXPECT_EQ(0, memcmp(buf.data(), kTestData, 7));
+}
+
+TEST(BufferTest, TestConstructArray) {
+  Buffer buf(kTestData);
+  EXPECT_EQ(buf.size(), 16u);
+  EXPECT_EQ(buf.capacity(), 16u);
+  EXPECT_FALSE(buf.empty());
+  EXPECT_EQ(0, memcmp(buf.data(), kTestData, 16));
+}
+
+TEST(BufferTest, TestSetData) {
+  Buffer buf(kTestData + 4, 7);
+  buf.SetData(kTestData, 9);
+  EXPECT_EQ(buf.size(), 9u);
+  EXPECT_EQ(buf.capacity(), 7u * 3 / 2);
+  EXPECT_FALSE(buf.empty());
+  EXPECT_EQ(0, memcmp(buf.data(), kTestData, 9));
+  Buffer buf2;
+  buf2.SetData(buf);
+  EXPECT_EQ(buf.size(), 9u);
+  EXPECT_EQ(buf.capacity(), 7u * 3 / 2);
+  EXPECT_EQ(0, memcmp(buf.data(), kTestData, 9));
+}
+
+TEST(BufferTest, TestAppendData) {
+  Buffer buf(kTestData + 4, 3);
+  buf.AppendData(kTestData + 10, 2);
+  const int8_t exp[] = {0x4, 0x5, 0x6, 0xa, 0xb};
+  EXPECT_EQ(buf, Buffer(exp));
+  Buffer buf2;
+  buf2.AppendData(buf);
+  buf2.AppendData(rtc::ArrayView<uint8_t>(buf));
+  const int8_t exp2[] = {0x4, 0x5, 0x6, 0xa, 0xb, 0x4, 0x5, 0x6, 0xa, 0xb};
+  EXPECT_EQ(buf2, Buffer(exp2));
+}
+
+TEST(BufferTest, TestSetAndAppendWithUnknownArg) {
+  struct TestDataContainer {
+    size_t size() const { return 3; }
+    const uint8_t* data() const { return kTestData; }
+  };
+  Buffer buf;
+  buf.SetData(TestDataContainer());
+  EXPECT_EQ(3u, buf.size());
+  EXPECT_EQ(Buffer(kTestData, 3), buf);
+  buf.AppendData(TestDataContainer());
+  EXPECT_EQ(6u, buf.size());
+  EXPECT_EQ(0, memcmp(buf.data(), kTestData, 3));
+  EXPECT_EQ(0, memcmp(buf.data() + 3, kTestData, 3));
+}
+
+TEST(BufferTest, TestSetSizeSmaller) {
+  Buffer buf;
+  buf.SetData(kTestData, 15);
+  buf.SetSize(10);
+  EXPECT_EQ(buf.size(), 10u);
+  EXPECT_EQ(buf.capacity(), 15u);  // Hasn't shrunk.
+  EXPECT_FALSE(buf.empty());
+  EXPECT_EQ(buf, Buffer(kTestData, 10));
+}
+
+TEST(BufferTest, TestSetSizeLarger) {
+  Buffer buf;
+  buf.SetData(kTestData, 15);
+  EXPECT_EQ(buf.size(), 15u);
+  EXPECT_EQ(buf.capacity(), 15u);
+  EXPECT_FALSE(buf.empty());
+  buf.SetSize(20);
+  EXPECT_EQ(buf.size(), 20u);
+  EXPECT_EQ(buf.capacity(), 15u * 3 / 2);  // Has grown.
+  EXPECT_FALSE(buf.empty());
+  EXPECT_EQ(0, memcmp(buf.data(), kTestData, 15));
+}
+
+TEST(BufferTest, TestEnsureCapacitySmaller) {
+  Buffer buf(kTestData);
+  const char* data = buf.data<char>();
+  buf.EnsureCapacity(4);
+  EXPECT_EQ(buf.capacity(), 16u);     // Hasn't shrunk.
+  EXPECT_EQ(buf.data<char>(), data);  // No reallocation.
+  EXPECT_FALSE(buf.empty());
+  EXPECT_EQ(buf, Buffer(kTestData));
+}
+
+TEST(BufferTest, TestEnsureCapacityLarger) {
+  Buffer buf(kTestData, 5);
+  buf.EnsureCapacity(10);
+  const int8_t* data = buf.data<int8_t>();
+  EXPECT_EQ(buf.capacity(), 10u);
+  buf.AppendData(kTestData + 5, 5);
+  EXPECT_EQ(buf.data<int8_t>(), data);  // No reallocation.
+  EXPECT_FALSE(buf.empty());
+  EXPECT_EQ(buf, Buffer(kTestData, 10));
+}
+
+TEST(BufferTest, TestMoveConstruct) {
+  Buffer buf1(kTestData, 3, 40);
+  const uint8_t* data = buf1.data();
+  Buffer buf2(std::move(buf1));
+  EXPECT_EQ(buf2.size(), 3u);
+  EXPECT_EQ(buf2.capacity(), 40u);
+  EXPECT_EQ(buf2.data(), data);
+  EXPECT_FALSE(buf2.empty());
+  buf1.Clear();
+  EXPECT_EQ(buf1.size(), 0u);
+  EXPECT_EQ(buf1.capacity(), 0u);
+  EXPECT_EQ(buf1.data(), nullptr);
+  EXPECT_TRUE(buf1.empty());
+}
+
+TEST(BufferTest, TestMoveAssign) {
+  Buffer buf1(kTestData, 3, 40);
+  const uint8_t* data = buf1.data();
+  Buffer buf2(kTestData);
+  buf2 = std::move(buf1);
+  EXPECT_EQ(buf2.size(), 3u);
+  EXPECT_EQ(buf2.capacity(), 40u);
+  EXPECT_EQ(buf2.data(), data);
+  EXPECT_FALSE(buf2.empty());
+  buf1.Clear();
+  EXPECT_EQ(buf1.size(), 0u);
+  EXPECT_EQ(buf1.capacity(), 0u);
+  EXPECT_EQ(buf1.data(), nullptr);
+  EXPECT_TRUE(buf1.empty());
+}
+
+TEST(BufferTest, TestSwap) {
+  Buffer buf1(kTestData, 3);
+  Buffer buf2(kTestData, 6, 40);
+  uint8_t* data1 = buf1.data();
+  uint8_t* data2 = buf2.data();
+  using std::swap;
+  swap(buf1, buf2);
+  EXPECT_EQ(buf1.size(), 6u);
+  EXPECT_EQ(buf1.capacity(), 40u);
+  EXPECT_EQ(buf1.data(), data2);
+  EXPECT_FALSE(buf1.empty());
+  EXPECT_EQ(buf2.size(), 3u);
+  EXPECT_EQ(buf2.capacity(), 3u);
+  EXPECT_EQ(buf2.data(), data1);
+  EXPECT_FALSE(buf2.empty());
+}
+
+TEST(BufferTest, TestClear) {
+  Buffer buf;
+  buf.SetData(kTestData, 15);
+  EXPECT_EQ(buf.size(), 15u);
+  EXPECT_EQ(buf.capacity(), 15u);
+  EXPECT_FALSE(buf.empty());
+  const char *data = buf.data<char>();
+  buf.Clear();
+  EXPECT_EQ(buf.size(), 0u);
+  EXPECT_EQ(buf.capacity(), 15u);  // Hasn't shrunk.
+  EXPECT_EQ(buf.data<char>(), data); // No reallocation.
+  EXPECT_TRUE(buf.empty());
+}
+
+TEST(BufferTest, TestLambdaSetAppend) {
+  auto setter = [] (rtc::ArrayView<uint8_t> av) {
+    for (int i = 0; i != 15; ++i)
+      av[i] = kTestData[i];
+    return 15;
+  };
+
+  Buffer buf1;
+  buf1.SetData(kTestData, 15);
+  buf1.AppendData(kTestData, 15);
+
+  Buffer buf2;
+  EXPECT_EQ(buf2.SetData(15, setter), 15u);
+  EXPECT_EQ(buf2.AppendData(15, setter), 15u);
+  EXPECT_EQ(buf1, buf2);
+  EXPECT_EQ(buf1.capacity(), buf2.capacity());
+  EXPECT_FALSE(buf1.empty());
+  EXPECT_FALSE(buf2.empty());
+}
+
+TEST(BufferTest, TestLambdaSetAppendSigned) {
+  auto setter = [] (rtc::ArrayView<int8_t> av) {
+    for (int i = 0; i != 15; ++i)
+      av[i] = kTestData[i];
+    return 15;
+  };
+
+  Buffer buf1;
+  buf1.SetData(kTestData, 15);
+  buf1.AppendData(kTestData, 15);
+
+  Buffer buf2;
+  EXPECT_EQ(buf2.SetData<int8_t>(15, setter), 15u);
+  EXPECT_EQ(buf2.AppendData<int8_t>(15, setter), 15u);
+  EXPECT_EQ(buf1, buf2);
+  EXPECT_EQ(buf1.capacity(), buf2.capacity());
+  EXPECT_FALSE(buf1.empty());
+  EXPECT_FALSE(buf2.empty());
+}
+
+TEST(BufferTest, TestLambdaAppendEmpty) {
+  auto setter = [] (rtc::ArrayView<uint8_t> av) {
+    for (int i = 0; i != 15; ++i)
+      av[i] = kTestData[i];
+    return 15;
+  };
+
+  Buffer buf1;
+  buf1.SetData(kTestData, 15);
+
+  Buffer buf2;
+  EXPECT_EQ(buf2.AppendData(15, setter), 15u);
+  EXPECT_EQ(buf1, buf2);
+  EXPECT_EQ(buf1.capacity(), buf2.capacity());
+  EXPECT_FALSE(buf1.empty());
+  EXPECT_FALSE(buf2.empty());
+}
+
+TEST(BufferTest, TestLambdaAppendPartial) {
+  auto setter = [] (rtc::ArrayView<uint8_t> av) {
+    for (int i = 0; i != 7; ++i)
+      av[i] = kTestData[i];
+    return 7;
+  };
+
+  Buffer buf;
+  EXPECT_EQ(buf.AppendData(15, setter), 7u);
+  EXPECT_EQ(buf.size(), 7u);            // Size is exactly what we wrote.
+  EXPECT_GE(buf.capacity(), 7u);        // Capacity is valid.
+  EXPECT_NE(buf.data<char>(), nullptr); // Data is actually stored.
+  EXPECT_FALSE(buf.empty());
+}
+
+TEST(BufferTest, TestMutableLambdaSetAppend) {
+  uint8_t magic_number = 17;
+  auto setter = [magic_number] (rtc::ArrayView<uint8_t> av) mutable {
+    for (int i = 0; i != 15; ++i) {
+      av[i] = magic_number;
+      ++magic_number;
+    }
+    return 15;
+  };
+
+  EXPECT_EQ(magic_number, 17);
+
+  Buffer buf;
+  EXPECT_EQ(buf.SetData(15, setter), 15u);
+  EXPECT_EQ(buf.AppendData(15, setter), 15u);
+  EXPECT_EQ(buf.size(), 30u);           // Size is exactly what we wrote.
+  EXPECT_GE(buf.capacity(), 30u);       // Capacity is valid.
+  EXPECT_NE(buf.data<char>(), nullptr); // Data is actually stored.
+  EXPECT_FALSE(buf.empty());
+
+  for (uint8_t i = 0; i != buf.size(); ++i) {
+    EXPECT_EQ(buf.data()[i], magic_number + i);
+  }
+}
+
+TEST(BufferTest, TestBracketRead) {
+  Buffer buf(kTestData, 7);
+  EXPECT_EQ(buf.size(), 7u);
+  EXPECT_EQ(buf.capacity(), 7u);
+  EXPECT_NE(buf.data(), nullptr);
+  EXPECT_FALSE(buf.empty());
+
+  for (size_t i = 0; i != 7u; ++i) {
+    EXPECT_EQ(buf[i], kTestData[i]);
+  }
+}
+
+TEST(BufferTest, TestBracketReadConst) {
+  Buffer buf(kTestData, 7);
+  EXPECT_EQ(buf.size(), 7u);
+  EXPECT_EQ(buf.capacity(), 7u);
+  EXPECT_NE(buf.data(), nullptr);
+  EXPECT_FALSE(buf.empty());
+
+  const Buffer& cbuf = buf;
+
+  for (size_t i = 0; i != 7u; ++i) {
+    EXPECT_EQ(cbuf[i], kTestData[i]);
+  }
+}
+
+TEST(BufferTest, TestBracketWrite) {
+  Buffer buf(7);
+  EXPECT_EQ(buf.size(), 7u);
+  EXPECT_EQ(buf.capacity(), 7u);
+  EXPECT_NE(buf.data(), nullptr);
+  EXPECT_FALSE(buf.empty());
+
+  for (size_t i = 0; i != 7u; ++i) {
+    buf[i] = kTestData[i];
+  }
+
+  for (size_t i = 0; i != 7u; ++i) {
+    EXPECT_EQ(buf[i], kTestData[i]);
+  }
+}
+
+TEST(BufferTest, TestBeginEnd) {
+  const Buffer cbuf(kTestData);
+  Buffer buf(kTestData);
+  auto b1 = cbuf.begin();
+  for (auto& x : buf) {
+    EXPECT_EQ(*b1, x);
+    ++b1;
+    ++x;
+  }
+  EXPECT_EQ(cbuf.end(), b1);
+  auto b2 = buf.begin();
+  for (auto& y : cbuf) {
+    EXPECT_EQ(*b2, y + 1);
+    ++b2;
+  }
+  EXPECT_EQ(buf.end(), b2);
+}
+
+TEST(BufferTest, TestInt16) {
+  static constexpr int16_t test_data[] = {14, 15, 16, 17, 18};
+  BufferT<int16_t> buf(test_data);
+  EXPECT_EQ(buf.size(), 5u);
+  EXPECT_EQ(buf.capacity(), 5u);
+  EXPECT_NE(buf.data(), nullptr);
+  EXPECT_FALSE(buf.empty());
+  for (size_t i = 0; i != buf.size(); ++i) {
+    EXPECT_EQ(test_data[i], buf[i]);
+  }
+  BufferT<int16_t> buf2(test_data);
+  EXPECT_EQ(buf, buf2);
+  buf2[0] = 9;
+  EXPECT_NE(buf, buf2);
+}
+
+TEST(BufferTest, TestFloat) {
+  static constexpr float test_data[] = {14, 15, 16, 17, 18};
+  BufferT<float> buf;
+  EXPECT_EQ(buf.size(), 0u);
+  EXPECT_EQ(buf.capacity(), 0u);
+  EXPECT_EQ(buf.data(), nullptr);
+  EXPECT_TRUE(buf.empty());
+  buf.SetData(test_data);
+  EXPECT_EQ(buf.size(), 5u);
+  EXPECT_EQ(buf.capacity(), 5u);
+  EXPECT_NE(buf.data(), nullptr);
+  EXPECT_FALSE(buf.empty());
+  float* p1 = buf.data();
+  while (buf.data() == p1) {
+    buf.AppendData(test_data);
+  }
+  EXPECT_EQ(buf.size(), buf.capacity());
+  EXPECT_GT(buf.size(), 5u);
+  EXPECT_EQ(buf.size() % 5, 0u);
+  EXPECT_NE(buf.data(), nullptr);
+  for (size_t i = 0; i != buf.size(); ++i) {
+    EXPECT_EQ(test_data[i % 5], buf[i]);
+  }
+}
+
+TEST(BufferTest, TestStruct) {
+  struct BloodStone {
+    bool blood;
+    const char* stone;
+  };
+  BufferT<BloodStone> buf(4);
+  EXPECT_EQ(buf.size(), 4u);
+  EXPECT_EQ(buf.capacity(), 4u);
+  EXPECT_NE(buf.data(), nullptr);
+  EXPECT_FALSE(buf.empty());
+  BufferT<BloodStone*> buf2(4);
+  for (size_t i = 0; i < buf2.size(); ++i) {
+    buf2[i] = &buf[i];
+  }
+  static const char kObsidian[] = "obsidian";
+  buf2[2]->stone = kObsidian;
+  EXPECT_EQ(kObsidian, buf[2].stone);
+}
+
+}  // namespace rtc
diff --git a/base/bufferqueue.cc b/base/bufferqueue.cc
new file mode 100644
index 0000000..6e5439d
--- /dev/null
+++ b/base/bufferqueue.cc
@@ -0,0 +1,94 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/bufferqueue.h"
+
+#include <algorithm>
+
+namespace rtc {
+
+BufferQueue::BufferQueue(size_t capacity, size_t default_size)
+    : capacity_(capacity), default_size_(default_size) {
+}
+
+BufferQueue::~BufferQueue() {
+  CritScope cs(&crit_);
+
+  for (Buffer* buffer : queue_) {
+    delete buffer;
+  }
+  for (Buffer* buffer : free_list_) {
+    delete buffer;
+  }
+}
+
+size_t BufferQueue::size() const {
+  CritScope cs(&crit_);
+  return queue_.size();
+}
+
+void BufferQueue::Clear() {
+  CritScope cs(&crit_);
+  while (!queue_.empty()) {
+    free_list_.push_back(queue_.front());
+    queue_.pop_front();
+  }
+}
+
+bool BufferQueue::ReadFront(void* buffer, size_t bytes, size_t* bytes_read) {
+  CritScope cs(&crit_);
+  if (queue_.empty()) {
+    return false;
+  }
+
+  bool was_writable = queue_.size() < capacity_;
+  Buffer* packet = queue_.front();
+  queue_.pop_front();
+
+  bytes = std::min(bytes, packet->size());
+  memcpy(buffer, packet->data(), bytes);
+  if (bytes_read) {
+    *bytes_read = bytes;
+  }
+  free_list_.push_back(packet);
+  if (!was_writable) {
+    NotifyWritableForTest();
+  }
+  return true;
+}
+
+bool BufferQueue::WriteBack(const void* buffer, size_t bytes,
+                            size_t* bytes_written) {
+  CritScope cs(&crit_);
+  if (queue_.size() == capacity_) {
+    return false;
+  }
+
+  bool was_readable = !queue_.empty();
+  Buffer* packet;
+  if (!free_list_.empty()) {
+    packet = free_list_.back();
+    free_list_.pop_back();
+  } else {
+    packet = new Buffer(bytes, default_size_);
+  }
+
+  packet->SetData(static_cast<const uint8_t*>(buffer), bytes);
+  if (bytes_written) {
+    *bytes_written = bytes;
+  }
+  queue_.push_back(packet);
+  if (!was_readable) {
+    NotifyReadableForTest();
+  }
+  return true;
+}
+
+}  // namespace rtc
diff --git a/base/bufferqueue.h b/base/bufferqueue.h
index 3142ae3..bc9fc84 100644
--- a/base/bufferqueue.h
+++ b/base/bufferqueue.h
@@ -11,9 +11,51 @@
 #ifndef WEBRTC_BASE_BUFFERQUEUE_H_
 #define WEBRTC_BASE_BUFFERQUEUE_H_
 
+#include <deque>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/bufferqueue.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/criticalsection.h"
+
+namespace rtc {
+
+class BufferQueue {
+ public:
+  // Creates a buffer queue with a given capacity and default buffer size.
+  BufferQueue(size_t capacity, size_t default_size);
+  virtual ~BufferQueue();
+
+  // Return number of queued buffers.
+  size_t size() const;
+
+  // Clear the BufferQueue by moving all Buffers from |queue_| to |free_list_|.
+  void Clear();
+
+  // ReadFront will only read one buffer at a time and will truncate buffers
+  // that don't fit in the passed memory.
+  // Returns true unless no data could be returned.
+  bool ReadFront(void* data, size_t bytes, size_t* bytes_read);
+
+  // WriteBack always writes either the complete memory or nothing.
+  // Returns true unless no data could be written.
+  bool WriteBack(const void* data, size_t bytes, size_t* bytes_written);
+
+ protected:
+  // These methods are called when the state of the queue changes.
+  virtual void NotifyReadableForTest() {}
+  virtual void NotifyWritableForTest() {}
+
+ private:
+  size_t capacity_;
+  size_t default_size_;
+  CriticalSection crit_;
+  std::deque<Buffer*> queue_ GUARDED_BY(crit_);
+  std::vector<Buffer*> free_list_ GUARDED_BY(crit_);
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(BufferQueue);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_BUFFERQUEUE_H_
diff --git a/base/bufferqueue_unittest.cc b/base/bufferqueue_unittest.cc
new file mode 100644
index 0000000..07084c4
--- /dev/null
+++ b/base/bufferqueue_unittest.cc
@@ -0,0 +1,86 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/bufferqueue.h"
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+TEST(BufferQueueTest, TestAll) {
+  const size_t kSize = 16;
+  const char in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
+  char out[kSize * 2];
+  size_t bytes;
+  BufferQueue queue1(1, kSize);
+  BufferQueue queue2(2, kSize);
+
+  // The queue is initially empty.
+  EXPECT_EQ(0u, queue1.size());
+  EXPECT_FALSE(queue1.ReadFront(out, kSize, &bytes));
+
+  // A write should succeed.
+  EXPECT_TRUE(queue1.WriteBack(in, kSize, &bytes));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(1u, queue1.size());
+
+  // The queue is full now (only one buffer allowed).
+  EXPECT_FALSE(queue1.WriteBack(in, kSize, &bytes));
+  EXPECT_EQ(1u, queue1.size());
+
+  // Reading previously written buffer.
+  EXPECT_TRUE(queue1.ReadFront(out, kSize, &bytes));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize));
+
+  // The queue is empty again now.
+  EXPECT_FALSE(queue1.ReadFront(out, kSize, &bytes));
+  EXPECT_EQ(0u, queue1.size());
+
+  // Reading only returns available data.
+  EXPECT_TRUE(queue1.WriteBack(in, kSize, &bytes));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(1u, queue1.size());
+  EXPECT_TRUE(queue1.ReadFront(out, kSize * 2, &bytes));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize));
+  EXPECT_EQ(0u, queue1.size());
+
+  // Reading maintains buffer boundaries.
+  EXPECT_TRUE(queue2.WriteBack(in, kSize / 2, &bytes));
+  EXPECT_EQ(1u, queue2.size());
+  EXPECT_TRUE(queue2.WriteBack(in + kSize / 2, kSize / 2, &bytes));
+  EXPECT_EQ(2u, queue2.size());
+  EXPECT_TRUE(queue2.ReadFront(out, kSize, &bytes));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize / 2));
+  EXPECT_EQ(1u, queue2.size());
+  EXPECT_TRUE(queue2.ReadFront(out, kSize, &bytes));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 2));
+  EXPECT_EQ(0u, queue2.size());
+
+  // Reading truncates buffers.
+  EXPECT_TRUE(queue2.WriteBack(in, kSize / 2, &bytes));
+  EXPECT_EQ(1u, queue2.size());
+  EXPECT_TRUE(queue2.WriteBack(in + kSize / 2, kSize / 2, &bytes));
+  EXPECT_EQ(2u, queue2.size());
+  // Read first packet partially in too-small buffer.
+  EXPECT_TRUE(queue2.ReadFront(out, kSize / 4, &bytes));
+  EXPECT_EQ(kSize / 4, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize / 4));
+  EXPECT_EQ(1u, queue2.size());
+  // Remainder of first packet is truncated, reading starts with next packet.
+  EXPECT_TRUE(queue2.ReadFront(out, kSize, &bytes));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 2));
+  EXPECT_EQ(0u, queue2.size());
+}
+
+}  // namespace rtc
diff --git a/base/bytebuffer.cc b/base/bytebuffer.cc
new file mode 100644
index 0000000..859c123
--- /dev/null
+++ b/base/bytebuffer.cc
@@ -0,0 +1,283 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/bytebuffer.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/byteorder.h"
+
+namespace rtc {
+
+static const int DEFAULT_SIZE = 4096;
+
+ByteBufferWriter::ByteBufferWriter()
+    : ByteBuffer(ORDER_NETWORK) {
+  Construct(nullptr, DEFAULT_SIZE);
+}
+
+ByteBufferWriter::ByteBufferWriter(ByteOrder byte_order)
+    : ByteBuffer(byte_order) {
+  Construct(nullptr, DEFAULT_SIZE);
+}
+
+ByteBufferWriter::ByteBufferWriter(const char* bytes, size_t len)
+    : ByteBuffer(ORDER_NETWORK) {
+  Construct(bytes, len);
+}
+
+ByteBufferWriter::ByteBufferWriter(const char* bytes, size_t len,
+                                   ByteOrder byte_order)
+    : ByteBuffer(byte_order) {
+  Construct(bytes, len);
+}
+
+void ByteBufferWriter::Construct(const char* bytes, size_t len) {
+  size_ = len;
+  bytes_ = new char[size_];
+
+  if (bytes) {
+    end_ = len;
+    memcpy(bytes_, bytes, end_);
+  } else {
+    end_ = 0;
+  }
+}
+
+ByteBufferWriter::~ByteBufferWriter() {
+  delete[] bytes_;
+}
+
+void ByteBufferWriter::WriteUInt8(uint8_t val) {
+  WriteBytes(reinterpret_cast<const char*>(&val), 1);
+}
+
+void ByteBufferWriter::WriteUInt16(uint16_t val) {
+  uint16_t v = (Order() == ORDER_NETWORK) ? HostToNetwork16(val) : val;
+  WriteBytes(reinterpret_cast<const char*>(&v), 2);
+}
+
+void ByteBufferWriter::WriteUInt24(uint32_t val) {
+  uint32_t v = (Order() == ORDER_NETWORK) ? HostToNetwork32(val) : val;
+  char* start = reinterpret_cast<char*>(&v);
+  if (Order() == ORDER_NETWORK || IsHostBigEndian()) {
+    ++start;
+  }
+  WriteBytes(start, 3);
+}
+
+void ByteBufferWriter::WriteUInt32(uint32_t val) {
+  uint32_t v = (Order() == ORDER_NETWORK) ? HostToNetwork32(val) : val;
+  WriteBytes(reinterpret_cast<const char*>(&v), 4);
+}
+
+void ByteBufferWriter::WriteUInt64(uint64_t val) {
+  uint64_t v = (Order() == ORDER_NETWORK) ? HostToNetwork64(val) : val;
+  WriteBytes(reinterpret_cast<const char*>(&v), 8);
+}
+
+// Serializes an unsigned varint in the format described by
+// https://developers.google.com/protocol-buffers/docs/encoding#varints
+// with the caveat that integers are 64-bit, not 128-bit.
+void ByteBufferWriter::WriteUVarint(uint64_t val) {
+  while (val >= 0x80) {
+    // Write 7 bits at a time, then set the msb to a continuation byte (msb=1).
+    char byte = static_cast<char>(val) | 0x80;
+    WriteBytes(&byte, 1);
+    val >>= 7;
+  }
+  char last_byte = static_cast<char>(val);
+  WriteBytes(&last_byte, 1);
+}
+
+void ByteBufferWriter::WriteString(const std::string& val) {
+  WriteBytes(val.c_str(), val.size());
+}
+
+void ByteBufferWriter::WriteBytes(const char* val, size_t len) {
+  memcpy(ReserveWriteBuffer(len), val, len);
+}
+
+char* ByteBufferWriter::ReserveWriteBuffer(size_t len) {
+  if (Length() + len > Capacity())
+    Resize(Length() + len);
+
+  char* start = bytes_ + end_;
+  end_ += len;
+  return start;
+}
+
+void ByteBufferWriter::Resize(size_t size) {
+  size_t len = std::min(end_, size);
+  if (size > size_) {
+    // Reallocate a larger buffer.
+    size_ = std::max(size, 3 * size_ / 2);
+    char* new_bytes = new char[size_];
+    memcpy(new_bytes, bytes_, len);
+    delete [] bytes_;
+    bytes_ = new_bytes;
+  }
+  end_ = len;
+}
+
+void ByteBufferWriter::Clear() {
+  memset(bytes_, 0, size_);
+  end_ = 0;
+}
+
+
+ByteBufferReader::ByteBufferReader(const char* bytes, size_t len)
+    : ByteBuffer(ORDER_NETWORK) {
+  Construct(bytes, len);
+}
+
+ByteBufferReader::ByteBufferReader(const char* bytes, size_t len,
+                                   ByteOrder byte_order)
+    : ByteBuffer(byte_order) {
+  Construct(bytes, len);
+}
+
+ByteBufferReader::ByteBufferReader(const char* bytes)
+    : ByteBuffer(ORDER_NETWORK) {
+  Construct(bytes, strlen(bytes));
+}
+
+ByteBufferReader::ByteBufferReader(const Buffer& buf)
+    : ByteBuffer(ORDER_NETWORK) {
+  Construct(buf.data<char>(), buf.size());
+}
+
+ByteBufferReader::ByteBufferReader(const ByteBufferWriter& buf)
+    : ByteBuffer(buf.Order()) {
+  Construct(buf.Data(), buf.Length());
+}
+
+void ByteBufferReader::Construct(const char* bytes, size_t len) {
+  bytes_ = bytes;
+  size_ = len;
+  start_ = 0;
+  end_ = len;
+}
+
+bool ByteBufferReader::ReadUInt8(uint8_t* val) {
+  if (!val) return false;
+
+  return ReadBytes(reinterpret_cast<char*>(val), 1);
+}
+
+bool ByteBufferReader::ReadUInt16(uint16_t* val) {
+  if (!val) return false;
+
+  uint16_t v;
+  if (!ReadBytes(reinterpret_cast<char*>(&v), 2)) {
+    return false;
+  } else {
+    *val = (Order() == ORDER_NETWORK) ? NetworkToHost16(v) : v;
+    return true;
+  }
+}
+
+bool ByteBufferReader::ReadUInt24(uint32_t* val) {
+  if (!val) return false;
+
+  uint32_t v = 0;
+  char* read_into = reinterpret_cast<char*>(&v);
+  if (Order() == ORDER_NETWORK || IsHostBigEndian()) {
+    ++read_into;
+  }
+
+  if (!ReadBytes(read_into, 3)) {
+    return false;
+  } else {
+    *val = (Order() == ORDER_NETWORK) ? NetworkToHost32(v) : v;
+    return true;
+  }
+}
+
+bool ByteBufferReader::ReadUInt32(uint32_t* val) {
+  if (!val) return false;
+
+  uint32_t v;
+  if (!ReadBytes(reinterpret_cast<char*>(&v), 4)) {
+    return false;
+  } else {
+    *val = (Order() == ORDER_NETWORK) ? NetworkToHost32(v) : v;
+    return true;
+  }
+}
+
+bool ByteBufferReader::ReadUInt64(uint64_t* val) {
+  if (!val) return false;
+
+  uint64_t v;
+  if (!ReadBytes(reinterpret_cast<char*>(&v), 8)) {
+    return false;
+  } else {
+    *val = (Order() == ORDER_NETWORK) ? NetworkToHost64(v) : v;
+    return true;
+  }
+}
+
+bool ByteBufferReader::ReadUVarint(uint64_t* val) {
+  if (!val) {
+    return false;
+  }
+  // Integers are deserialized 7 bits at a time, with each byte having a
+  // continuation byte (msb=1) if there are more bytes to be read.
+  uint64_t v = 0;
+  for (int i = 0; i < 64; i += 7) {
+    char byte;
+    if (!ReadBytes(&byte, 1)) {
+      return false;
+    }
+    // Read the first 7 bits of the byte, then offset by bits read so far.
+    v |= (static_cast<uint64_t>(byte) & 0x7F) << i;
+    // True if the msb is not a continuation byte.
+    if (static_cast<uint64_t>(byte) < 0x80) {
+      *val = v;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool ByteBufferReader::ReadString(std::string* val, size_t len) {
+  if (!val) return false;
+
+  if (len > Length()) {
+    return false;
+  } else {
+    val->append(bytes_ + start_, len);
+    start_ += len;
+    return true;
+  }
+}
+
+bool ByteBufferReader::ReadBytes(char* val, size_t len) {
+  if (len > Length()) {
+    return false;
+  } else {
+    memcpy(val, bytes_ + start_, len);
+    start_ += len;
+    return true;
+  }
+}
+
+bool ByteBufferReader::Consume(size_t size) {
+  if (size > Length())
+    return false;
+  start_ += size;
+  return true;
+}
+
+}  // namespace rtc
diff --git a/base/bytebuffer.h b/base/bytebuffer.h
index 0cc9a12..546c447 100644
--- a/base/bytebuffer.h
+++ b/base/bytebuffer.h
@@ -11,9 +11,129 @@
 #ifndef WEBRTC_BASE_BYTEBUFFER_H_
 #define WEBRTC_BASE_BYTEBUFFER_H_
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/bytebuffer.h"
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/constructormagic.h"
+
+namespace rtc {
+
+class ByteBuffer {
+ public:
+  enum ByteOrder {
+    ORDER_NETWORK = 0,  // Default, use network byte order (big endian).
+    ORDER_HOST,         // Use the native order of the host.
+  };
+
+  explicit ByteBuffer(ByteOrder byte_order) : byte_order_(byte_order) {}
+
+  ByteOrder Order() const { return byte_order_; }
+
+ private:
+  ByteOrder byte_order_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(ByteBuffer);
+};
+
+class ByteBufferWriter : public ByteBuffer {
+ public:
+  // |byte_order| defines order of bytes in the buffer.
+  ByteBufferWriter();
+  explicit ByteBufferWriter(ByteOrder byte_order);
+  ByteBufferWriter(const char* bytes, size_t len);
+  ByteBufferWriter(const char* bytes, size_t len, ByteOrder byte_order);
+
+  ~ByteBufferWriter();
+
+  const char* Data() const { return bytes_; }
+  size_t Length() const { return end_; }
+  size_t Capacity() const { return size_; }
+
+  // Write value to the buffer. Resizes the buffer when it is
+  // neccessary.
+  void WriteUInt8(uint8_t val);
+  void WriteUInt16(uint16_t val);
+  void WriteUInt24(uint32_t val);
+  void WriteUInt32(uint32_t val);
+  void WriteUInt64(uint64_t val);
+  void WriteUVarint(uint64_t val);
+  void WriteString(const std::string& val);
+  void WriteBytes(const char* val, size_t len);
+
+  // Reserves the given number of bytes and returns a char* that can be written
+  // into. Useful for functions that require a char* buffer and not a
+  // ByteBufferWriter.
+  char* ReserveWriteBuffer(size_t len);
+
+  // Resize the buffer to the specified |size|.
+  void Resize(size_t size);
+
+  // Clears the contents of the buffer. After this, Length() will be 0.
+  void Clear();
+
+ private:
+  void Construct(const char* bytes, size_t size);
+
+  char* bytes_;
+  size_t size_;
+  size_t end_;
+
+  // There are sensible ways to define these, but they aren't needed in our code
+  // base.
+  RTC_DISALLOW_COPY_AND_ASSIGN(ByteBufferWriter);
+};
+
+// The ByteBufferReader references the passed data, i.e. the pointer must be
+// valid during the lifetime of the reader.
+class ByteBufferReader : public ByteBuffer {
+ public:
+  ByteBufferReader(const char* bytes, size_t len);
+  ByteBufferReader(const char* bytes, size_t len, ByteOrder byte_order);
+
+  // Initializes buffer from a zero-terminated string.
+  explicit ByteBufferReader(const char* bytes);
+
+  explicit ByteBufferReader(const Buffer& buf);
+
+  explicit ByteBufferReader(const ByteBufferWriter& buf);
+
+  // Returns start of unprocessed data.
+  const char* Data() const { return bytes_ + start_; }
+  // Returns number of unprocessed bytes.
+  size_t Length() const { return end_ - start_; }
+
+  // Read a next value from the buffer. Return false if there isn't
+  // enough data left for the specified type.
+  bool ReadUInt8(uint8_t* val);
+  bool ReadUInt16(uint16_t* val);
+  bool ReadUInt24(uint32_t* val);
+  bool ReadUInt32(uint32_t* val);
+  bool ReadUInt64(uint64_t* val);
+  bool ReadUVarint(uint64_t* val);
+  bool ReadBytes(char* val, size_t len);
+
+  // Appends next |len| bytes from the buffer to |val|. Returns false
+  // if there is less than |len| bytes left.
+  bool ReadString(std::string* val, size_t len);
+
+  // Moves current position |size| bytes forward. Returns false if
+  // there is less than |size| bytes left in the buffer. Consume doesn't
+  // permanently remove data, so remembered read positions are still valid
+  // after this call.
+  bool Consume(size_t size);
+
+ private:
+  void Construct(const char* bytes, size_t size);
+
+  const char* bytes_;
+  size_t size_;
+  size_t start_;
+  size_t end_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(ByteBufferReader);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_BYTEBUFFER_H_
diff --git a/base/bytebuffer_unittest.cc b/base/bytebuffer_unittest.cc
new file mode 100644
index 0000000..597f5f5
--- /dev/null
+++ b/base/bytebuffer_unittest.cc
@@ -0,0 +1,258 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/bytebuffer.h"
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+TEST(ByteBufferTest, TestByteOrder) {
+  uint16_t n16 = 1;
+  uint32_t n32 = 1;
+  uint64_t n64 = 1;
+
+  EXPECT_EQ(n16, NetworkToHost16(HostToNetwork16(n16)));
+  EXPECT_EQ(n32, NetworkToHost32(HostToNetwork32(n32)));
+  EXPECT_EQ(n64, NetworkToHost64(HostToNetwork64(n64)));
+
+  if (IsHostBigEndian()) {
+    // The host is the network (big) endian.
+    EXPECT_EQ(n16, HostToNetwork16(n16));
+    EXPECT_EQ(n32, HostToNetwork32(n32));
+    EXPECT_EQ(n64, HostToNetwork64(n64));
+
+    // GetBE converts big endian to little endian here.
+    EXPECT_EQ(n16 >> 8, GetBE16(&n16));
+    EXPECT_EQ(n32 >> 24, GetBE32(&n32));
+    EXPECT_EQ(n64 >> 56, GetBE64(&n64));
+  } else {
+    // The host is little endian.
+    EXPECT_NE(n16, HostToNetwork16(n16));
+    EXPECT_NE(n32, HostToNetwork32(n32));
+    EXPECT_NE(n64, HostToNetwork64(n64));
+
+    // GetBE converts little endian to big endian here.
+    EXPECT_EQ(GetBE16(&n16), HostToNetwork16(n16));
+    EXPECT_EQ(GetBE32(&n32), HostToNetwork32(n32));
+    EXPECT_EQ(GetBE64(&n64), HostToNetwork64(n64));
+
+    // GetBE converts little endian to big endian here.
+    EXPECT_EQ(n16 << 8, GetBE16(&n16));
+    EXPECT_EQ(n32 << 24, GetBE32(&n32));
+    EXPECT_EQ(n64 << 56, GetBE64(&n64));
+  }
+}
+
+TEST(ByteBufferTest, TestBufferLength) {
+  ByteBufferWriter buffer;
+  size_t size = 0;
+  EXPECT_EQ(size, buffer.Length());
+
+  buffer.WriteUInt8(1);
+  ++size;
+  EXPECT_EQ(size, buffer.Length());
+
+  buffer.WriteUInt16(1);
+  size += 2;
+  EXPECT_EQ(size, buffer.Length());
+
+  buffer.WriteUInt24(1);
+  size += 3;
+  EXPECT_EQ(size, buffer.Length());
+
+  buffer.WriteUInt32(1);
+  size += 4;
+  EXPECT_EQ(size, buffer.Length());
+
+  buffer.WriteUInt64(1);
+  size += 8;
+  EXPECT_EQ(size, buffer.Length());
+}
+
+TEST(ByteBufferTest, TestReadWriteBuffer) {
+  ByteBufferWriter::ByteOrder orders[2] = { ByteBufferWriter::ORDER_HOST,
+                                            ByteBufferWriter::ORDER_NETWORK };
+  for (size_t i = 0; i < arraysize(orders); i++) {
+    ByteBufferWriter buffer(orders[i]);
+    EXPECT_EQ(orders[i], buffer.Order());
+    ByteBufferReader read_buf(nullptr, 0, orders[i]);
+    EXPECT_EQ(orders[i], read_buf.Order());
+    uint8_t ru8;
+    EXPECT_FALSE(read_buf.ReadUInt8(&ru8));
+
+    // Write and read uint8_t.
+    uint8_t wu8 = 1;
+    buffer.WriteUInt8(wu8);
+    ByteBufferReader read_buf1(buffer.Data(), buffer.Length(), orders[i]);
+    EXPECT_TRUE(read_buf1.ReadUInt8(&ru8));
+    EXPECT_EQ(wu8, ru8);
+    EXPECT_EQ(0U, read_buf1.Length());
+    buffer.Clear();
+
+    // Write and read uint16_t.
+    uint16_t wu16 = (1 << 8) + 1;
+    buffer.WriteUInt16(wu16);
+    ByteBufferReader read_buf2(buffer.Data(), buffer.Length(), orders[i]);
+    uint16_t ru16;
+    EXPECT_TRUE(read_buf2.ReadUInt16(&ru16));
+    EXPECT_EQ(wu16, ru16);
+    EXPECT_EQ(0U, read_buf2.Length());
+    buffer.Clear();
+
+    // Write and read uint24.
+    uint32_t wu24 = (3 << 16) + (2 << 8) + 1;
+    buffer.WriteUInt24(wu24);
+    ByteBufferReader read_buf3(buffer.Data(), buffer.Length(), orders[i]);
+    uint32_t ru24;
+    EXPECT_TRUE(read_buf3.ReadUInt24(&ru24));
+    EXPECT_EQ(wu24, ru24);
+    EXPECT_EQ(0U, read_buf3.Length());
+    buffer.Clear();
+
+    // Write and read uint32_t.
+    uint32_t wu32 = (4 << 24) + (3 << 16) + (2 << 8) + 1;
+    buffer.WriteUInt32(wu32);
+    ByteBufferReader read_buf4(buffer.Data(), buffer.Length(), orders[i]);
+    uint32_t ru32;
+    EXPECT_TRUE(read_buf4.ReadUInt32(&ru32));
+    EXPECT_EQ(wu32, ru32);
+    EXPECT_EQ(0U, read_buf3.Length());
+    buffer.Clear();
+
+    // Write and read uint64_t.
+    uint32_t another32 = (8 << 24) + (7 << 16) + (6 << 8) + 5;
+    uint64_t wu64 = (static_cast<uint64_t>(another32) << 32) + wu32;
+    buffer.WriteUInt64(wu64);
+    ByteBufferReader read_buf5(buffer.Data(), buffer.Length(), orders[i]);
+    uint64_t ru64;
+    EXPECT_TRUE(read_buf5.ReadUInt64(&ru64));
+    EXPECT_EQ(wu64, ru64);
+    EXPECT_EQ(0U, read_buf5.Length());
+    buffer.Clear();
+
+    // Write and read string.
+    std::string write_string("hello");
+    buffer.WriteString(write_string);
+    ByteBufferReader read_buf6(buffer.Data(), buffer.Length(), orders[i]);
+    std::string read_string;
+    EXPECT_TRUE(read_buf6.ReadString(&read_string, write_string.size()));
+    EXPECT_EQ(write_string, read_string);
+    EXPECT_EQ(0U, read_buf6.Length());
+    buffer.Clear();
+
+    // Write and read bytes
+    char write_bytes[] = "foo";
+    buffer.WriteBytes(write_bytes, 3);
+    ByteBufferReader read_buf7(buffer.Data(), buffer.Length(), orders[i]);
+    char read_bytes[3];
+    EXPECT_TRUE(read_buf7.ReadBytes(read_bytes, 3));
+    for (int i = 0; i < 3; ++i) {
+      EXPECT_EQ(write_bytes[i], read_bytes[i]);
+    }
+    EXPECT_EQ(0U, read_buf7.Length());
+    buffer.Clear();
+
+    // Write and read reserved buffer space
+    char* write_dst = buffer.ReserveWriteBuffer(3);
+    memcpy(write_dst, write_bytes, 3);
+    ByteBufferReader read_buf8(buffer.Data(), buffer.Length(), orders[i]);
+    memset(read_bytes, 0, 3);
+    EXPECT_TRUE(read_buf8.ReadBytes(read_bytes, 3));
+    for (int i = 0; i < 3; ++i) {
+      EXPECT_EQ(write_bytes[i], read_bytes[i]);
+    }
+    EXPECT_EQ(0U, read_buf8.Length());
+    buffer.Clear();
+
+    // Write and read in order.
+    buffer.WriteUInt8(wu8);
+    buffer.WriteUInt16(wu16);
+    buffer.WriteUInt24(wu24);
+    buffer.WriteUInt32(wu32);
+    buffer.WriteUInt64(wu64);
+    ByteBufferReader read_buf9(buffer.Data(), buffer.Length(), orders[i]);
+    EXPECT_TRUE(read_buf9.ReadUInt8(&ru8));
+    EXPECT_EQ(wu8, ru8);
+    EXPECT_TRUE(read_buf9.ReadUInt16(&ru16));
+    EXPECT_EQ(wu16, ru16);
+    EXPECT_TRUE(read_buf9.ReadUInt24(&ru24));
+    EXPECT_EQ(wu24, ru24);
+    EXPECT_TRUE(read_buf9.ReadUInt32(&ru32));
+    EXPECT_EQ(wu32, ru32);
+    EXPECT_TRUE(read_buf9.ReadUInt64(&ru64));
+    EXPECT_EQ(wu64, ru64);
+    EXPECT_EQ(0U, read_buf9.Length());
+    buffer.Clear();
+  }
+}
+
+TEST(ByteBufferTest, TestReadWriteUVarint) {
+  ByteBufferWriter::ByteOrder orders[2] = {ByteBufferWriter::ORDER_HOST,
+                                           ByteBufferWriter::ORDER_NETWORK};
+  for (ByteBufferWriter::ByteOrder& order : orders) {
+    ByteBufferWriter write_buffer(order);
+    size_t size = 0;
+    EXPECT_EQ(size, write_buffer.Length());
+
+    write_buffer.WriteUVarint(1u);
+    ++size;
+    EXPECT_EQ(size, write_buffer.Length());
+
+    write_buffer.WriteUVarint(2u);
+    ++size;
+    EXPECT_EQ(size, write_buffer.Length());
+
+    write_buffer.WriteUVarint(27u);
+    ++size;
+    EXPECT_EQ(size, write_buffer.Length());
+
+    write_buffer.WriteUVarint(149u);
+    size += 2;
+    EXPECT_EQ(size, write_buffer.Length());
+
+    write_buffer.WriteUVarint(68719476736u);
+    size += 6;
+    EXPECT_EQ(size, write_buffer.Length());
+
+    ByteBufferReader read_buffer(write_buffer.Data(), write_buffer.Length(),
+                                 order);
+    EXPECT_EQ(size, read_buffer.Length());
+    uint64_t val1, val2, val3, val4, val5;
+
+    ASSERT_TRUE(read_buffer.ReadUVarint(&val1));
+    EXPECT_EQ(1u, val1);
+    --size;
+    EXPECT_EQ(size, read_buffer.Length());
+
+    ASSERT_TRUE(read_buffer.ReadUVarint(&val2));
+    EXPECT_EQ(2u, val2);
+    --size;
+    EXPECT_EQ(size, read_buffer.Length());
+
+    ASSERT_TRUE(read_buffer.ReadUVarint(&val3));
+    EXPECT_EQ(27u, val3);
+    --size;
+    EXPECT_EQ(size, read_buffer.Length());
+
+    ASSERT_TRUE(read_buffer.ReadUVarint(&val4));
+    EXPECT_EQ(149u, val4);
+    size -= 2;
+    EXPECT_EQ(size, read_buffer.Length());
+
+    ASSERT_TRUE(read_buffer.ReadUVarint(&val5));
+    EXPECT_EQ(68719476736u, val5);
+    size -= 6;
+    EXPECT_EQ(size, read_buffer.Length());
+  }
+}
+
+}  // namespace rtc
diff --git a/base/byteorder.h b/base/byteorder.h
index 28cbaa5..d0cfa5e 100644
--- a/base/byteorder.h
+++ b/base/byteorder.h
@@ -11,9 +11,168 @@
 #ifndef WEBRTC_BASE_BYTEORDER_H_
 #define WEBRTC_BASE_BYTEORDER_H_
 
+#if defined(WEBRTC_POSIX) && !defined(__native_client__)
+#include <arpa/inet.h>
+#endif
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/byteorder.h"
+#include "webrtc/base/basictypes.h"
+
+#if defined(WEBRTC_MAC)
+#include <libkern/OSByteOrder.h>
+
+#define htobe16(v) OSSwapHostToBigInt16(v)
+#define htobe32(v) OSSwapHostToBigInt32(v)
+#define htobe64(v) OSSwapHostToBigInt64(v)
+#define be16toh(v) OSSwapBigToHostInt16(v)
+#define be32toh(v) OSSwapBigToHostInt32(v)
+#define be64toh(v) OSSwapBigToHostInt64(v)
+
+#define htole16(v) OSSwapHostToLittleInt16(v)
+#define htole32(v) OSSwapHostToLittleInt32(v)
+#define htole64(v) OSSwapHostToLittleInt64(v)
+#define le16toh(v) OSSwapLittleToHostInt16(v)
+#define le32toh(v) OSSwapLittleToHostInt32(v)
+#define le64toh(v) OSSwapLittleToHostInt64(v)
+#elif defined(WEBRTC_WIN) || defined(__native_client__)
+
+#if defined(WEBRTC_WIN)
+#include <stdlib.h>
+#include <winsock2.h>
+#else
+#include <netinet/in.h>
+#endif
+
+#define htobe16(v) htons(v)
+#define htobe32(v) htonl(v)
+#define be16toh(v) ntohs(v)
+#define be32toh(v) ntohl(v)
+#if defined(WEBRTC_WIN)
+#define htobe64(v) htonll(v)
+#define be64toh(v) ntohll(v)
+#endif
+
+#if defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
+#define htole16(v) (v)
+#define htole32(v) (v)
+#define htole64(v) (v)
+#define le16toh(v) (v)
+#define le32toh(v) (v)
+#define le64toh(v) (v)
+#if defined(__native_client__)
+#define htobe64(v) __builtin_bswap64(v)
+#define be64toh(v) __builtin_bswap64(v)
+#endif
+#elif defined(RTC_ARCH_CPU_BIG_ENDIAN)
+#define htole16(v) __builtin_bswap16(v)
+#define htole32(v) __builtin_bswap32(v)
+#define htole64(v) __builtin_bswap64(v)
+#define le16toh(v) __builtin_bswap16(v)
+#define le32toh(v) __builtin_bswap32(v)
+#define le64toh(v) __builtin_bswap64(v)
+#if defined(__native_client__)
+#define htobe64(v) (v)
+#define be64toh(v) (v)
+#endif
+#else
+#error RTC_ARCH_CPU_BIG_ENDIAN or RTC_ARCH_CPU_LITTLE_ENDIAN must be defined.
+#endif  // defined(RTC_ARCH_CPU_LITTLE_ENDIAN)
+#elif defined(WEBRTC_POSIX)
+#include <endian.h>
+#endif
+
+namespace rtc {
+
+// Reading and writing of little and big-endian numbers from memory
+
+inline void Set8(void* memory, size_t offset, uint8_t v) {
+  static_cast<uint8_t*>(memory)[offset] = v;
+}
+
+inline uint8_t Get8(const void* memory, size_t offset) {
+  return static_cast<const uint8_t*>(memory)[offset];
+}
+
+inline void SetBE16(void* memory, uint16_t v) {
+  *static_cast<uint16_t*>(memory) = htobe16(v);
+}
+
+inline void SetBE32(void* memory, uint32_t v) {
+  *static_cast<uint32_t*>(memory) = htobe32(v);
+}
+
+inline void SetBE64(void* memory, uint64_t v) {
+  *static_cast<uint64_t*>(memory) = htobe64(v);
+}
+
+inline uint16_t GetBE16(const void* memory) {
+  return be16toh(*static_cast<const uint16_t*>(memory));
+}
+
+inline uint32_t GetBE32(const void* memory) {
+  return be32toh(*static_cast<const uint32_t*>(memory));
+}
+
+inline uint64_t GetBE64(const void* memory) {
+  return be64toh(*static_cast<const uint64_t*>(memory));
+}
+
+inline void SetLE16(void* memory, uint16_t v) {
+  *static_cast<uint16_t*>(memory) = htole16(v);
+}
+
+inline void SetLE32(void* memory, uint32_t v) {
+  *static_cast<uint32_t*>(memory) = htole32(v);
+}
+
+inline void SetLE64(void* memory, uint64_t v) {
+  *static_cast<uint64_t*>(memory) = htole64(v);
+}
+
+inline uint16_t GetLE16(const void* memory) {
+  return le16toh(*static_cast<const uint16_t*>(memory));
+}
+
+inline uint32_t GetLE32(const void* memory) {
+  return le32toh(*static_cast<const uint32_t*>(memory));
+}
+
+inline uint64_t GetLE64(const void* memory) {
+  return le64toh(*static_cast<const uint64_t*>(memory));
+}
+
+// Check if the current host is big endian.
+inline bool IsHostBigEndian() {
+#if defined(RTC_ARCH_CPU_BIG_ENDIAN)
+  return true;
+#else
+  return false;
+#endif
+}
+
+inline uint16_t HostToNetwork16(uint16_t n) {
+  return htobe16(n);
+}
+
+inline uint32_t HostToNetwork32(uint32_t n) {
+  return htobe32(n);
+}
+
+inline uint64_t HostToNetwork64(uint64_t n) {
+  return htobe64(n);
+}
+
+inline uint16_t NetworkToHost16(uint16_t n) {
+  return be16toh(n);
+}
+
+inline uint32_t NetworkToHost32(uint32_t n) {
+  return be32toh(n);
+}
+
+inline uint64_t NetworkToHost64(uint64_t n) {
+  return be64toh(n);
+}
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_BYTEORDER_H_
diff --git a/base/byteorder_unittest.cc b/base/byteorder_unittest.cc
new file mode 100644
index 0000000..6d9b44f
--- /dev/null
+++ b/base/byteorder_unittest.cc
@@ -0,0 +1,83 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <stdint.h>
+
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+// Test memory set functions put values into memory in expected order.
+TEST(ByteOrderTest, TestSet) {
+  uint8_t buf[8] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u};
+  Set8(buf, 0, 0xfb);
+  Set8(buf, 1, 0x12);
+  EXPECT_EQ(0xfb, buf[0]);
+  EXPECT_EQ(0x12, buf[1]);
+  SetBE16(buf, 0x1234);
+  EXPECT_EQ(0x12, buf[0]);
+  EXPECT_EQ(0x34, buf[1]);
+  SetLE16(buf, 0x1234);
+  EXPECT_EQ(0x34, buf[0]);
+  EXPECT_EQ(0x12, buf[1]);
+  SetBE32(buf, 0x12345678);
+  EXPECT_EQ(0x12, buf[0]);
+  EXPECT_EQ(0x34, buf[1]);
+  EXPECT_EQ(0x56, buf[2]);
+  EXPECT_EQ(0x78, buf[3]);
+  SetLE32(buf, 0x12345678);
+  EXPECT_EQ(0x78, buf[0]);
+  EXPECT_EQ(0x56, buf[1]);
+  EXPECT_EQ(0x34, buf[2]);
+  EXPECT_EQ(0x12, buf[3]);
+  SetBE64(buf, UINT64_C(0x0123456789abcdef));
+  EXPECT_EQ(0x01, buf[0]);
+  EXPECT_EQ(0x23, buf[1]);
+  EXPECT_EQ(0x45, buf[2]);
+  EXPECT_EQ(0x67, buf[3]);
+  EXPECT_EQ(0x89, buf[4]);
+  EXPECT_EQ(0xab, buf[5]);
+  EXPECT_EQ(0xcd, buf[6]);
+  EXPECT_EQ(0xef, buf[7]);
+  SetLE64(buf, UINT64_C(0x0123456789abcdef));
+  EXPECT_EQ(0xef, buf[0]);
+  EXPECT_EQ(0xcd, buf[1]);
+  EXPECT_EQ(0xab, buf[2]);
+  EXPECT_EQ(0x89, buf[3]);
+  EXPECT_EQ(0x67, buf[4]);
+  EXPECT_EQ(0x45, buf[5]);
+  EXPECT_EQ(0x23, buf[6]);
+  EXPECT_EQ(0x01, buf[7]);
+}
+
+// Test memory get functions get values from memory in expected order.
+TEST(ByteOrderTest, TestGet) {
+  uint8_t buf[8];
+  buf[0] = 0x01u;
+  buf[1] = 0x23u;
+  buf[2] = 0x45u;
+  buf[3] = 0x67u;
+  buf[4] = 0x89u;
+  buf[5] = 0xabu;
+  buf[6] = 0xcdu;
+  buf[7] = 0xefu;
+  EXPECT_EQ(0x01u, Get8(buf, 0));
+  EXPECT_EQ(0x23u, Get8(buf, 1));
+  EXPECT_EQ(0x0123u, GetBE16(buf));
+  EXPECT_EQ(0x2301u, GetLE16(buf));
+  EXPECT_EQ(0x01234567u, GetBE32(buf));
+  EXPECT_EQ(0x67452301u, GetLE32(buf));
+  EXPECT_EQ(UINT64_C(0x0123456789abcdef), GetBE64(buf));
+  EXPECT_EQ(UINT64_C(0xefcdab8967452301), GetLE64(buf));
+}
+
+}  // namespace rtc
+
diff --git a/base/callback.h b/base/callback.h
index 4da1e6d..7ffdcd7 100644
--- a/base/callback.h
+++ b/base/callback.h
@@ -62,9 +62,199 @@
 #ifndef WEBRTC_BASE_CALLBACK_H_
 #define WEBRTC_BASE_CALLBACK_H_
 
+#include "webrtc/base/refcount.h"
+#include "webrtc/base/scoped_ref_ptr.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/callback.h"
+namespace rtc {
+
+template <class R>
+class Callback0 {
+ public:
+  // Default copy operations are appropriate for this class.
+  Callback0() {}
+  template <class T> Callback0(const T& functor)
+      : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
+  R operator()() {
+    if (empty())
+      return R();
+    return helper_->Run();
+  }
+  bool empty() const { return !helper_; }
+
+ private:
+  struct Helper : RefCountInterface {
+    virtual ~Helper() {}
+    virtual R Run() = 0;
+  };
+  template <class T> struct HelperImpl : Helper {
+    explicit HelperImpl(const T& functor) : functor_(functor) {}
+    virtual R Run() {
+      return functor_();
+    }
+    T functor_;
+  };
+  scoped_refptr<Helper> helper_;
+};
+
+template <class R,
+          class P1>
+class Callback1 {
+ public:
+  // Default copy operations are appropriate for this class.
+  Callback1() {}
+  template <class T> Callback1(const T& functor)
+      : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
+  R operator()(P1 p1) {
+    if (empty())
+      return R();
+    return helper_->Run(p1);
+  }
+  bool empty() const { return !helper_; }
+
+ private:
+  struct Helper : RefCountInterface {
+    virtual ~Helper() {}
+    virtual R Run(P1 p1) = 0;
+  };
+  template <class T> struct HelperImpl : Helper {
+    explicit HelperImpl(const T& functor) : functor_(functor) {}
+    virtual R Run(P1 p1) {
+      return functor_(p1);
+    }
+    T functor_;
+  };
+  scoped_refptr<Helper> helper_;
+};
+
+template <class R,
+          class P1,
+          class P2>
+class Callback2 {
+ public:
+  // Default copy operations are appropriate for this class.
+  Callback2() {}
+  template <class T> Callback2(const T& functor)
+      : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
+  R operator()(P1 p1, P2 p2) {
+    if (empty())
+      return R();
+    return helper_->Run(p1, p2);
+  }
+  bool empty() const { return !helper_; }
+
+ private:
+  struct Helper : RefCountInterface {
+    virtual ~Helper() {}
+    virtual R Run(P1 p1, P2 p2) = 0;
+  };
+  template <class T> struct HelperImpl : Helper {
+    explicit HelperImpl(const T& functor) : functor_(functor) {}
+    virtual R Run(P1 p1, P2 p2) {
+      return functor_(p1, p2);
+    }
+    T functor_;
+  };
+  scoped_refptr<Helper> helper_;
+};
+
+template <class R,
+          class P1,
+          class P2,
+          class P3>
+class Callback3 {
+ public:
+  // Default copy operations are appropriate for this class.
+  Callback3() {}
+  template <class T> Callback3(const T& functor)
+      : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
+  R operator()(P1 p1, P2 p2, P3 p3) {
+    if (empty())
+      return R();
+    return helper_->Run(p1, p2, p3);
+  }
+  bool empty() const { return !helper_; }
+
+ private:
+  struct Helper : RefCountInterface {
+    virtual ~Helper() {}
+    virtual R Run(P1 p1, P2 p2, P3 p3) = 0;
+  };
+  template <class T> struct HelperImpl : Helper {
+    explicit HelperImpl(const T& functor) : functor_(functor) {}
+    virtual R Run(P1 p1, P2 p2, P3 p3) {
+      return functor_(p1, p2, p3);
+    }
+    T functor_;
+  };
+  scoped_refptr<Helper> helper_;
+};
+
+template <class R,
+          class P1,
+          class P2,
+          class P3,
+          class P4>
+class Callback4 {
+ public:
+  // Default copy operations are appropriate for this class.
+  Callback4() {}
+  template <class T> Callback4(const T& functor)
+      : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
+  R operator()(P1 p1, P2 p2, P3 p3, P4 p4) {
+    if (empty())
+      return R();
+    return helper_->Run(p1, p2, p3, p4);
+  }
+  bool empty() const { return !helper_; }
+
+ private:
+  struct Helper : RefCountInterface {
+    virtual ~Helper() {}
+    virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) = 0;
+  };
+  template <class T> struct HelperImpl : Helper {
+    explicit HelperImpl(const T& functor) : functor_(functor) {}
+    virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4) {
+      return functor_(p1, p2, p3, p4);
+    }
+    T functor_;
+  };
+  scoped_refptr<Helper> helper_;
+};
+
+template <class R,
+          class P1,
+          class P2,
+          class P3,
+          class P4,
+          class P5>
+class Callback5 {
+ public:
+  // Default copy operations are appropriate for this class.
+  Callback5() {}
+  template <class T> Callback5(const T& functor)
+      : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
+  R operator()(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
+    if (empty())
+      return R();
+    return helper_->Run(p1, p2, p3, p4, p5);
+  }
+  bool empty() const { return !helper_; }
+
+ private:
+  struct Helper : RefCountInterface {
+    virtual ~Helper() {}
+    virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) = 0;
+  };
+  template <class T> struct HelperImpl : Helper {
+    explicit HelperImpl(const T& functor) : functor_(functor) {}
+    virtual R Run(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
+      return functor_(p1, p2, p3, p4, p5);
+    }
+    T functor_;
+  };
+  scoped_refptr<Helper> helper_;
+};
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_CALLBACK_H_
diff --git a/base/callback.h.pump b/base/callback.h.pump
new file mode 100644
index 0000000..86957df
--- /dev/null
+++ b/base/callback.h.pump
@@ -0,0 +1,103 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+// To generate callback.h from callback.h.pump, execute:
+// /home/build/google3/third_party/gtest/scripts/pump.py callback.h.pump
+
+// Callbacks are callable object containers. They can hold a function pointer
+// or a function object and behave like a value type. Internally, data is
+// reference-counted, making copies and pass-by-value inexpensive.
+//
+// Callbacks are typed using template arguments.  The format is:
+//   CallbackN<ReturnType, ParamType1, ..., ParamTypeN>
+// where N is the number of arguments supplied to the callable object.
+// Callbacks are invoked using operator(), just like a function or a function
+// object. Default-constructed callbacks are "empty," and executing an empty
+// callback does nothing. A callback can be made empty by assigning it from
+// a default-constructed callback.
+//
+// Callbacks are similar in purpose to std::function (which isn't available on
+// all platforms we support) and a lightweight alternative to sigslots. Since
+// they effectively hide the type of the object they call, they're useful in
+// breaking dependencies between objects that need to interact with one another.
+// Notably, they can hold the results of Bind(), std::bind*, etc, without needing
+// to know the resulting object type of those calls.
+//
+// Sigslots, on the other hand, provide a fuller feature set, such as multiple
+// subscriptions to a signal, optional thread-safety, and lifetime tracking of
+// slots. When these features are needed, choose sigslots.
+//
+// Example:
+//   int sqr(int x) { return x * x; }
+//   struct AddK {
+//     int k;
+//     int operator()(int x) const { return x + k; }
+//   } add_k = {5};
+//
+//   Callback1<int, int> my_callback;
+//   cout << my_callback.empty() << endl;  // true
+//
+//   my_callback = Callback1<int, int>(&sqr);
+//   cout << my_callback.empty() << endl;  // false
+//   cout << my_callback(3) << endl;  // 9
+//
+//   my_callback = Callback1<int, int>(add_k);
+//   cout << my_callback(10) << endl;  // 15
+//
+//   my_callback = Callback1<int, int>();
+//   cout << my_callback.empty() << endl;  // true
+
+#ifndef WEBRTC_BASE_CALLBACK_H_
+#define WEBRTC_BASE_CALLBACK_H_
+
+#include "webrtc/base/refcount.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+
+namespace rtc {
+
+$var n = 5
+$range i 0..n
+$for i [[
+$range j 1..i
+
+template <class R$for j [[,
+          class P$j]]>
+class Callback$i {
+ public:
+  // Default copy operations are appropriate for this class.
+  Callback$i() {}
+  template <class T> Callback$i(const T& functor)
+      : helper_(new RefCountedObject< HelperImpl<T> >(functor)) {}
+  R operator()($for j , [[P$j p$j]]) {
+    if (empty())
+      return R();
+    return helper_->Run($for j , [[p$j]]);
+  }
+  bool empty() const { return !helper_; }
+
+ private:
+  struct Helper : RefCountInterface {
+    virtual ~Helper() {}
+    virtual R Run($for j , [[P$j p$j]]) = 0;
+  };
+  template <class T> struct HelperImpl : Helper {
+    explicit HelperImpl(const T& functor) : functor_(functor) {}
+    virtual R Run($for j , [[P$j p$j]]) {
+      return functor_($for j , [[p$j]]);
+    }
+    T functor_;
+  };
+  scoped_refptr<Helper> helper_;
+};
+
+]]
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_CALLBACK_H_
diff --git a/base/callback_unittest.cc b/base/callback_unittest.cc
new file mode 100644
index 0000000..aba1e0c
--- /dev/null
+++ b/base/callback_unittest.cc
@@ -0,0 +1,140 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/bind.h"
+#include "webrtc/base/callback.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/keep_ref_until_done.h"
+#include "webrtc/base/refcount.h"
+
+namespace rtc {
+
+namespace {
+
+void f() {}
+int g() { return 42; }
+int h(int x) { return x * x; }
+void i(int& x) { x *= x; }  // NOLINT: Testing refs
+
+struct BindTester {
+  int a() { return 24; }
+  int b(int x) const { return x * x; }
+};
+
+class RefCountedBindTester : public RefCountInterface {
+ public:
+  RefCountedBindTester() : count_(0) {}
+  int AddRef() const override {
+    return ++count_;
+  }
+  int Release() const override {
+    return --count_;
+  }
+  int RefCount() const { return count_; }
+
+ private:
+  mutable int count_;
+};
+
+}  // namespace
+
+TEST(CallbackTest, VoidReturn) {
+  Callback0<void> cb;
+  EXPECT_TRUE(cb.empty());
+  cb();  // Executing an empty callback should not crash.
+  cb = Callback0<void>(&f);
+  EXPECT_FALSE(cb.empty());
+  cb();
+}
+
+TEST(CallbackTest, IntReturn) {
+  Callback0<int> cb;
+  EXPECT_TRUE(cb.empty());
+  cb = Callback0<int>(&g);
+  EXPECT_FALSE(cb.empty());
+  EXPECT_EQ(42, cb());
+  EXPECT_EQ(42, cb());
+}
+
+TEST(CallbackTest, OneParam) {
+  Callback1<int, int> cb1(&h);
+  EXPECT_FALSE(cb1.empty());
+  EXPECT_EQ(9, cb1(-3));
+  EXPECT_EQ(100, cb1(10));
+
+  // Try clearing a callback.
+  cb1 = Callback1<int, int>();
+  EXPECT_TRUE(cb1.empty());
+
+  // Try a callback with a ref parameter.
+  Callback1<void, int&> cb2(&i);
+  int x = 3;
+  cb2(x);
+  EXPECT_EQ(9, x);
+  cb2(x);
+  EXPECT_EQ(81, x);
+}
+
+TEST(CallbackTest, WithBind) {
+  BindTester t;
+  Callback0<int> cb1 = Bind(&BindTester::a, &t);
+  EXPECT_EQ(24, cb1());
+  EXPECT_EQ(24, cb1());
+  cb1 = Bind(&BindTester::b, &t, 10);
+  EXPECT_EQ(100, cb1());
+  EXPECT_EQ(100, cb1());
+  cb1 = Bind(&BindTester::b, &t, 5);
+  EXPECT_EQ(25, cb1());
+  EXPECT_EQ(25, cb1());
+}
+
+TEST(KeepRefUntilDoneTest, simple) {
+  RefCountedBindTester t;
+  EXPECT_EQ(0, t.RefCount());
+  {
+    Callback0<void> cb = KeepRefUntilDone(&t);
+    EXPECT_EQ(1, t.RefCount());
+    cb();
+    EXPECT_EQ(1, t.RefCount());
+    cb();
+    EXPECT_EQ(1, t.RefCount());
+  }
+  EXPECT_EQ(0, t.RefCount());
+}
+
+TEST(KeepRefUntilDoneTest, copy) {
+  RefCountedBindTester t;
+  EXPECT_EQ(0, t.RefCount());
+  Callback0<void> cb2;
+  {
+    Callback0<void> cb = KeepRefUntilDone(&t);
+    EXPECT_EQ(1, t.RefCount());
+    cb2 = cb;
+  }
+  EXPECT_EQ(1, t.RefCount());
+  cb2 = Callback0<void>();
+  EXPECT_EQ(0, t.RefCount());
+}
+
+TEST(KeepRefUntilDoneTest, scopedref) {
+  RefCountedBindTester t;
+  EXPECT_EQ(0, t.RefCount());
+  {
+    scoped_refptr<RefCountedBindTester> t_scoped_ref(&t);
+    Callback0<void> cb = KeepRefUntilDone(t_scoped_ref);
+    t_scoped_ref = nullptr;
+    EXPECT_EQ(1, t.RefCount());
+    cb();
+    EXPECT_EQ(1, t.RefCount());
+  }
+  EXPECT_EQ(0, t.RefCount());
+}
+
+}  // namespace rtc
diff --git a/base/checks.cc b/base/checks.cc
new file mode 100644
index 0000000..32c2f58
--- /dev/null
+++ b/base/checks.cc
@@ -0,0 +1,141 @@
+/*
+ *  Copyright 2006 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Most of this was borrowed (with minor modifications) from V8's and Chromium's
+// src/base/logging.cc.
+
+// Use the C++ version to provide __GLIBCXX__.
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+#if defined(__GLIBCXX__) && !defined(__UCLIBC__)
+#include <cxxabi.h>
+#include <execinfo.h>
+#endif
+
+#if defined(WEBRTC_ANDROID)
+#define RTC_LOG_TAG "rtc"
+#include <android/log.h>  // NOLINT
+#endif
+
+#if defined(WEBRTC_WIN)
+#include <windows.h>
+#endif
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+
+#if defined(_MSC_VER)
+// Warning C4722: destructor never returns, potential memory leak.
+// FatalMessage's dtor very intentionally aborts.
+#pragma warning(disable:4722)
+#endif
+
+namespace rtc {
+
+void VPrintError(const char* format, va_list args) {
+#if defined(WEBRTC_ANDROID)
+  __android_log_vprint(ANDROID_LOG_ERROR, RTC_LOG_TAG, format, args);
+#else
+  vfprintf(stderr, format, args);
+#endif
+}
+
+void PrintError(const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  VPrintError(format, args);
+  va_end(args);
+}
+
+// TODO(ajm): This works on Mac (although the parsing fails) but I don't seem
+// to get usable symbols on Linux. This is copied from V8. Chromium has a more
+// advanced stace trace system; also more difficult to copy.
+void DumpBacktrace() {
+#if defined(__GLIBCXX__) && !defined(__UCLIBC__)
+  void* trace[100];
+  int size = backtrace(trace, sizeof(trace) / sizeof(*trace));
+  char** symbols = backtrace_symbols(trace, size);
+  PrintError("\n==== C stack trace ===============================\n\n");
+  if (size == 0) {
+    PrintError("(empty)\n");
+  } else if (symbols == nullptr) {
+    PrintError("(no symbols)\n");
+  } else {
+    for (int i = 1; i < size; ++i) {
+      char mangled[201];
+      if (sscanf(symbols[i], "%*[^(]%*[(]%200[^)+]", mangled) == 1) {  // NOLINT
+        PrintError("%2d: ", i);
+        int status;
+        size_t length;
+        char* demangled =
+            abi::__cxa_demangle(mangled, nullptr, &length, &status);
+        PrintError("%s\n", demangled != nullptr ? demangled : mangled);
+        free(demangled);
+      } else {
+        // If parsing failed, at least print the unparsed symbol.
+        PrintError("%s\n", symbols[i]);
+      }
+    }
+  }
+  free(symbols);
+#endif
+}
+
+FatalMessage::FatalMessage(const char* file, int line) {
+  Init(file, line);
+}
+
+FatalMessage::FatalMessage(const char* file, int line, std::string* result) {
+  Init(file, line);
+  stream_ << "Check failed: " << *result << std::endl << "# ";
+  delete result;
+}
+
+NO_RETURN FatalMessage::~FatalMessage() {
+  fflush(stdout);
+  fflush(stderr);
+  stream_ << std::endl << "#" << std::endl;
+  PrintError(stream_.str().c_str());
+  DumpBacktrace();
+  fflush(stderr);
+  abort();
+}
+
+void FatalMessage::Init(const char* file, int line) {
+  stream_ << std::endl << std::endl
+          << "#" << std::endl
+          << "# Fatal error in " << file << ", line " << line << std::endl
+          << "# last system error: " << LAST_SYSTEM_ERROR << std::endl
+          << "# ";
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Explicit instantiations for commonly used comparisons.
+template std::string* MakeCheckOpString<int, int>(
+    const int&, const int&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned long>(
+    const unsigned long&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned int>(
+    const unsigned long&, const unsigned int&, const char* names);
+template std::string* MakeCheckOpString<unsigned int, unsigned long>(
+    const unsigned int&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<std::string, std::string>(
+    const std::string&, const std::string&, const char* name);
+#endif
+
+}  // namespace rtc
+
+// Function to call from the C version of the RTC_CHECK and RTC_DCHECK macros.
+NO_RETURN void rtc_FatalMessage(const char* file, int line, const char* msg) {
+  rtc::FatalMessage(file, line).stream() << msg;
+}
diff --git a/base/checks.h b/base/checks.h
index f56f157..b86fb15 100644
--- a/base/checks.h
+++ b/base/checks.h
@@ -11,9 +11,279 @@
 #ifndef WEBRTC_BASE_CHECKS_H_
 #define WEBRTC_BASE_CHECKS_H_
 
+#include "webrtc/typedefs.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/checks.h"
+// If you for some reson need to know if DCHECKs are on, test the value of
+// RTC_DCHECK_IS_ON. (Test its value, not if it's defined; it'll always be
+// defined, to either a true or a false value.)
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+#define RTC_DCHECK_IS_ON 1
+#else
+#define RTC_DCHECK_IS_ON 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+NO_RETURN void rtc_FatalMessage(const char* file, int line, const char* msg);
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#ifdef __cplusplus
+// C++ version.
+
+#include <sstream>
+#include <string>
+
+#include "webrtc/base/safe_compare.h"
+
+// The macros here print a message to stderr and abort under various
+// conditions. All will accept additional stream messages. For example:
+// RTC_DCHECK_EQ(foo, bar) << "I'm printed when foo != bar.";
+//
+// - RTC_CHECK(x) is an assertion that x is always true, and that if it isn't,
+//   it's better to terminate the process than to continue. During development,
+//   the reason that it's better to terminate might simply be that the error
+//   handling code isn't in place yet; in production, the reason might be that
+//   the author of the code truly believes that x will always be true, but that
+//   she recognizes that if she is wrong, abrupt and unpleasant process
+//   termination is still better than carrying on with the assumption violated.
+//
+//   RTC_CHECK always evaluates its argument, so it's OK for x to have side
+//   effects.
+//
+// - RTC_DCHECK(x) is the same as RTC_CHECK(x)---an assertion that x is always
+//   true---except that x will only be evaluated in debug builds; in production
+//   builds, x is simply assumed to be true. This is useful if evaluating x is
+//   expensive and the expected cost of failing to detect the violated
+//   assumption is acceptable. You should not handle cases where a production
+//   build fails to spot a violated condition, even those that would result in
+//   crashes. If the code needs to cope with the error, make it cope, but don't
+//   call RTC_DCHECK; if the condition really can't occur, but you'd sleep
+//   better at night knowing that the process will suicide instead of carrying
+//   on in case you were wrong, use RTC_CHECK instead of RTC_DCHECK.
+//
+//   RTC_DCHECK only evaluates its argument in debug builds, so if x has visible
+//   side effects, you need to write e.g.
+//     bool w = x; RTC_DCHECK(w);
+//
+// - RTC_CHECK_EQ, _NE, _GT, ..., and RTC_DCHECK_EQ, _NE, _GT, ... are
+//   specialized variants of RTC_CHECK and RTC_DCHECK that print prettier
+//   messages if the condition doesn't hold. Prefer them to raw RTC_CHECK and
+//   RTC_DCHECK.
+//
+// - FATAL() aborts unconditionally.
+//
+// TODO(ajm): Ideally, checks.h would be combined with logging.h, but
+// consolidation with system_wrappers/logging.h should happen first.
+
+namespace rtc {
+
+// Helper macro which avoids evaluating the arguments to a stream if
+// the condition doesn't hold.
+#define RTC_LAZY_STREAM(stream, condition)                                    \
+  !(condition) ? static_cast<void>(0) : rtc::FatalMessageVoidify() & (stream)
+
+// The actual stream used isn't important. We reference |ignored| in the code
+// but don't evaluate it; this is to avoid "unused variable" warnings (we do so
+// in a particularly convoluted way with an extra ?: because that appears to be
+// the simplest construct that keeps Visual Studio from complaining about
+// condition being unused).
+#define RTC_EAT_STREAM_PARAMETERS(ignored) \
+  (true ? true : ((void)(ignored), true))  \
+      ? static_cast<void>(0)               \
+      : rtc::FatalMessageVoidify() & rtc::FatalMessage("", 0).stream()
+
+// Call RTC_EAT_STREAM_PARAMETERS with an argument that fails to compile if
+// values of the same types as |a| and |b| can't be compared with the given
+// operation, and that would evaluate |a| and |b| if evaluated.
+#define RTC_EAT_STREAM_PARAMETERS_OP(op, a, b) \
+  RTC_EAT_STREAM_PARAMETERS(((void)rtc::Safe##op(a, b)))
+
+// RTC_CHECK dies with a fatal error if condition is not true. It is *not*
+// controlled by NDEBUG or anything else, so the check will be executed
+// regardless of compilation mode.
+//
+// We make sure RTC_CHECK et al. always evaluates their arguments, as
+// doing RTC_CHECK(FunctionWithSideEffect()) is a common idiom.
+#define RTC_CHECK(condition)                                      \
+  RTC_LAZY_STREAM(rtc::FatalMessage(__FILE__, __LINE__).stream(), \
+                  !(condition))                                   \
+      << "Check failed: " #condition << std::endl << "# "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use RTC_CHECK_EQ et al below.
+//
+// TODO(akalin): Rewrite this so that constructs like if (...)
+// RTC_CHECK_EQ(...) else { ... } work properly.
+#define RTC_CHECK_OP(name, op, val1, val2)                                 \
+  if (std::string* _result =                                               \
+          rtc::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \
+    rtc::FatalMessage(__FILE__, __LINE__, _result).stream()
+
+// Build the error message string.  This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline.  Caller
+// takes ownership of the returned string.
+template<class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+  std::ostringstream ss;
+  ss << names << " (" << v1 << " vs. " << v2 << ")";
+  std::string* msg = new std::string(ss.str());
+  return msg;
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
+// in logging.cc.
+extern template std::string* MakeCheckOpString<int, int>(
+    const int&, const int&, const char* names);
+extern template
+std::string* MakeCheckOpString<unsigned long, unsigned long>(
+    const unsigned long&, const unsigned long&, const char* names);
+extern template
+std::string* MakeCheckOpString<unsigned long, unsigned int>(
+    const unsigned long&, const unsigned int&, const char* names);
+extern template
+std::string* MakeCheckOpString<unsigned int, unsigned long>(
+    const unsigned int&, const unsigned long&, const char* names);
+extern template
+std::string* MakeCheckOpString<std::string, std::string>(
+    const std::string&, const std::string&, const char* name);
+#endif
+
+// Helper functions for RTC_CHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+#define DEFINE_RTC_CHECK_OP_IMPL(name)                                       \
+  template <class t1, class t2>                                              \
+  inline std::string* Check##name##Impl(const t1& v1, const t2& v2,          \
+                                        const char* names) {                 \
+    if (rtc::Safe##name(v1, v2))                                             \
+      return nullptr;                                                        \
+    else                                                                     \
+      return rtc::MakeCheckOpString(v1, v2, names);                          \
+  }                                                                          \
+  inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
+    if (rtc::Safe##name(v1, v2))                                             \
+      return nullptr;                                                        \
+    else                                                                     \
+      return rtc::MakeCheckOpString(v1, v2, names);                          \
+  }
+DEFINE_RTC_CHECK_OP_IMPL(Eq)
+DEFINE_RTC_CHECK_OP_IMPL(Ne)
+DEFINE_RTC_CHECK_OP_IMPL(Le)
+DEFINE_RTC_CHECK_OP_IMPL(Lt)
+DEFINE_RTC_CHECK_OP_IMPL(Ge)
+DEFINE_RTC_CHECK_OP_IMPL(Gt)
+#undef DEFINE_RTC_CHECK_OP_IMPL
+
+#define RTC_CHECK_EQ(val1, val2) RTC_CHECK_OP(Eq, ==, val1, val2)
+#define RTC_CHECK_NE(val1, val2) RTC_CHECK_OP(Ne, !=, val1, val2)
+#define RTC_CHECK_LE(val1, val2) RTC_CHECK_OP(Le, <=, val1, val2)
+#define RTC_CHECK_LT(val1, val2) RTC_CHECK_OP(Lt, <, val1, val2)
+#define RTC_CHECK_GE(val1, val2) RTC_CHECK_OP(Ge, >=, val1, val2)
+#define RTC_CHECK_GT(val1, val2) RTC_CHECK_OP(Gt, >, val1, val2)
+
+// The RTC_DCHECK macro is equivalent to RTC_CHECK except that it only generates
+// code in debug builds. It does reference the condition parameter in all cases,
+// though, so callers won't risk getting warnings about unused variables.
+#if RTC_DCHECK_IS_ON
+#define RTC_DCHECK(condition) RTC_CHECK(condition)
+#define RTC_DCHECK_EQ(v1, v2) RTC_CHECK_EQ(v1, v2)
+#define RTC_DCHECK_NE(v1, v2) RTC_CHECK_NE(v1, v2)
+#define RTC_DCHECK_LE(v1, v2) RTC_CHECK_LE(v1, v2)
+#define RTC_DCHECK_LT(v1, v2) RTC_CHECK_LT(v1, v2)
+#define RTC_DCHECK_GE(v1, v2) RTC_CHECK_GE(v1, v2)
+#define RTC_DCHECK_GT(v1, v2) RTC_CHECK_GT(v1, v2)
+#else
+#define RTC_DCHECK(condition) RTC_EAT_STREAM_PARAMETERS(condition)
+#define RTC_DCHECK_EQ(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Eq, v1, v2)
+#define RTC_DCHECK_NE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ne, v1, v2)
+#define RTC_DCHECK_LE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Le, v1, v2)
+#define RTC_DCHECK_LT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Lt, v1, v2)
+#define RTC_DCHECK_GE(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Ge, v1, v2)
+#define RTC_DCHECK_GT(v1, v2) RTC_EAT_STREAM_PARAMETERS_OP(Gt, v1, v2)
+#endif
+
+// This is identical to LogMessageVoidify but in name.
+class FatalMessageVoidify {
+ public:
+  FatalMessageVoidify() { }
+  // This has to be an operator with a precedence lower than << but
+  // higher than ?:
+  void operator&(std::ostream&) { }
+};
+
+#define RTC_UNREACHABLE_CODE_HIT false
+#define RTC_NOTREACHED() RTC_DCHECK(RTC_UNREACHABLE_CODE_HIT)
+
+#define FATAL() rtc::FatalMessage(__FILE__, __LINE__).stream()
+// TODO(ajm): Consider adding RTC_NOTIMPLEMENTED macro when
+// base/logging.h and system_wrappers/logging.h are consolidated such that we
+// can match the Chromium behavior.
+
+// Like a stripped-down LogMessage from logging.h, except that it aborts.
+class FatalMessage {
+ public:
+  FatalMessage(const char* file, int line);
+  // Used for RTC_CHECK_EQ(), etc. Takes ownership of the given string.
+  FatalMessage(const char* file, int line, std::string* result);
+  NO_RETURN ~FatalMessage();
+
+  std::ostream& stream() { return stream_; }
+
+ private:
+  void Init(const char* file, int line);
+
+  std::ostringstream stream_;
+};
+
+// Performs the integer division a/b and returns the result. CHECKs that the
+// remainder is zero.
+template <typename T>
+inline T CheckedDivExact(T a, T b) {
+  RTC_CHECK_EQ(a % b, 0) << a << " is not evenly divisible by " << b;
+  return a / b;
+}
+
+}  // namespace rtc
+
+#else  // __cplusplus not defined
+// C version. Lacks many features compared to the C++ version, but usage
+// guidelines are the same.
+
+#define RTC_CHECK(condition)                                             \
+  do {                                                                   \
+    if (!(condition)) {                                                  \
+      rtc_FatalMessage(__FILE__, __LINE__, "CHECK failed: " #condition); \
+    }                                                                    \
+  } while (0)
+
+#define RTC_CHECK_EQ(a, b) RTC_CHECK((a) == (b))
+#define RTC_CHECK_NE(a, b) RTC_CHECK((a) != (b))
+#define RTC_CHECK_LE(a, b) RTC_CHECK((a) <= (b))
+#define RTC_CHECK_LT(a, b) RTC_CHECK((a) < (b))
+#define RTC_CHECK_GE(a, b) RTC_CHECK((a) >= (b))
+#define RTC_CHECK_GT(a, b) RTC_CHECK((a) > (b))
+
+#define RTC_DCHECK(condition)                                             \
+  do {                                                                    \
+    if (RTC_DCHECK_IS_ON && !(condition)) {                               \
+      rtc_FatalMessage(__FILE__, __LINE__, "DCHECK failed: " #condition); \
+    }                                                                     \
+  } while (0)
+
+#define RTC_DCHECK_EQ(a, b) RTC_DCHECK((a) == (b))
+#define RTC_DCHECK_NE(a, b) RTC_DCHECK((a) != (b))
+#define RTC_DCHECK_LE(a, b) RTC_DCHECK((a) <= (b))
+#define RTC_DCHECK_LT(a, b) RTC_DCHECK((a) < (b))
+#define RTC_DCHECK_GE(a, b) RTC_DCHECK((a) >= (b))
+#define RTC_DCHECK_GT(a, b) RTC_DCHECK((a) > (b))
+
+#endif  // __cplusplus
 
 #endif  // WEBRTC_BASE_CHECKS_H_
diff --git a/base/compile_assert_c.h b/base/compile_assert_c.h
index 934cc9b..4d51de3 100644
--- a/base/compile_assert_c.h
+++ b/base/compile_assert_c.h
@@ -11,8 +11,11 @@
 #ifndef WEBRTC_BASE_COMPILE_ASSERT_C_H_
 #define WEBRTC_BASE_COMPILE_ASSERT_C_H_
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/compile_assert_c.h"
+// Use this macro to verify at compile time that certain restrictions are met.
+// The argument is the boolean expression to evaluate.
+// Example:
+//   RTC_COMPILE_ASSERT(sizeof(foo) < 128);
+// Note: In C++, use static_assert instead!
+#define RTC_COMPILE_ASSERT(expression) switch (0) {case 0: case expression:;}
 
 #endif  // WEBRTC_BASE_COMPILE_ASSERT_C_H_
diff --git a/base/constructormagic.h b/base/constructormagic.h
index 21652c2..6ef7826 100644
--- a/base/constructormagic.h
+++ b/base/constructormagic.h
@@ -11,9 +11,24 @@
 #ifndef WEBRTC_BASE_CONSTRUCTORMAGIC_H_
 #define WEBRTC_BASE_CONSTRUCTORMAGIC_H_
 
+// Put this in the declarations for a class to be unassignable.
+#define RTC_DISALLOW_ASSIGN(TypeName) \
+  void operator=(const TypeName&) = delete
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/constructormagic.h"
+// A macro to disallow the copy constructor and operator= functions. This should
+// be used in the declarations for a class.
+#define RTC_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&) = delete;          \
+  RTC_DISALLOW_ASSIGN(TypeName)
+
+// A macro to disallow all the implicit constructors, namely the default
+// constructor, copy constructor and operator= functions.
+//
+// This should be used in the declarations for a class that wants to prevent
+// anyone from instantiating it. This is especially useful for classes
+// containing only static methods.
+#define RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+  TypeName() = delete;                               \
+  RTC_DISALLOW_COPY_AND_ASSIGN(TypeName)
 
 #endif  // WEBRTC_BASE_CONSTRUCTORMAGIC_H_
diff --git a/base/copyonwritebuffer.cc b/base/copyonwritebuffer.cc
new file mode 100644
index 0000000..e1ba8de
--- /dev/null
+++ b/base/copyonwritebuffer.cc
@@ -0,0 +1,112 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/copyonwritebuffer.h"
+
+namespace rtc {
+
+CopyOnWriteBuffer::CopyOnWriteBuffer() {
+  RTC_DCHECK(IsConsistent());
+}
+
+CopyOnWriteBuffer::CopyOnWriteBuffer(const CopyOnWriteBuffer& buf)
+    : buffer_(buf.buffer_) {
+}
+
+CopyOnWriteBuffer::CopyOnWriteBuffer(CopyOnWriteBuffer&& buf)
+    : buffer_(std::move(buf.buffer_)) {
+}
+
+CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size)
+    : buffer_(size > 0 ? new RefCountedObject<Buffer>(size) : nullptr) {
+  RTC_DCHECK(IsConsistent());
+}
+
+CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size, size_t capacity)
+    : buffer_(size > 0 || capacity > 0
+          ? new RefCountedObject<Buffer>(size, capacity)
+          : nullptr) {
+  RTC_DCHECK(IsConsistent());
+}
+
+CopyOnWriteBuffer::~CopyOnWriteBuffer() = default;
+
+bool CopyOnWriteBuffer::operator==(const CopyOnWriteBuffer& buf) const {
+  // Must either use the same buffer internally or have the same contents.
+  RTC_DCHECK(IsConsistent());
+  RTC_DCHECK(buf.IsConsistent());
+  return buffer_.get() == buf.buffer_.get() ||
+      (buffer_.get() && buf.buffer_.get() &&
+      *buffer_.get() == *buf.buffer_.get());
+}
+
+void CopyOnWriteBuffer::SetSize(size_t size) {
+  RTC_DCHECK(IsConsistent());
+  if (!buffer_) {
+    if (size > 0) {
+      buffer_ = new RefCountedObject<Buffer>(size);
+    }
+    RTC_DCHECK(IsConsistent());
+    return;
+  }
+
+  // Clone data if referenced.
+  if (!buffer_->HasOneRef()) {
+    buffer_ = new RefCountedObject<Buffer>(
+        buffer_->data(),
+        std::min(buffer_->size(), size),
+        std::max(buffer_->capacity(), size));
+  }
+  buffer_->SetSize(size);
+  RTC_DCHECK(IsConsistent());
+}
+
+void CopyOnWriteBuffer::EnsureCapacity(size_t capacity) {
+  RTC_DCHECK(IsConsistent());
+  if (!buffer_) {
+    if (capacity > 0) {
+      buffer_ = new RefCountedObject<Buffer>(0, capacity);
+    }
+    RTC_DCHECK(IsConsistent());
+    return;
+  } else if (capacity <= buffer_->capacity()) {
+    return;
+  }
+
+  CloneDataIfReferenced(std::max(buffer_->capacity(), capacity));
+  buffer_->EnsureCapacity(capacity);
+  RTC_DCHECK(IsConsistent());
+}
+
+void CopyOnWriteBuffer::Clear() {
+  if (!buffer_)
+    return;
+
+  if (buffer_->HasOneRef()) {
+    buffer_->Clear();
+  } else {
+    buffer_ = new RefCountedObject<Buffer>(0, buffer_->capacity());
+  }
+  RTC_DCHECK(IsConsistent());
+}
+
+void CopyOnWriteBuffer::CloneDataIfReferenced(size_t new_capacity) {
+  if (buffer_->HasOneRef()) {
+    return;
+  }
+
+  buffer_ = new RefCountedObject<Buffer>(buffer_->data(), buffer_->size(),
+      new_capacity);
+  RTC_DCHECK(IsConsistent());
+}
+
+
+
+}  // namespace rtc
diff --git a/base/copyonwritebuffer.h b/base/copyonwritebuffer.h
index 6a95b31..fe3f561 100644
--- a/base/copyonwritebuffer.h
+++ b/base/copyonwritebuffer.h
@@ -11,9 +11,231 @@
 #ifndef WEBRTC_BASE_COPYONWRITEBUFFER_H_
 #define WEBRTC_BASE_COPYONWRITEBUFFER_H_
 
+#include <algorithm>
+#include <utility>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/copyonwritebuffer.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/refcount.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+
+namespace rtc {
+
+class CopyOnWriteBuffer {
+ public:
+  // An empty buffer.
+  CopyOnWriteBuffer();
+  // Copy size and contents of an existing buffer.
+  CopyOnWriteBuffer(const CopyOnWriteBuffer& buf);
+  // Move contents from an existing buffer.
+  CopyOnWriteBuffer(CopyOnWriteBuffer&& buf);
+
+  // Construct a buffer with the specified number of uninitialized bytes.
+  explicit CopyOnWriteBuffer(size_t size);
+  CopyOnWriteBuffer(size_t size, size_t capacity);
+
+  // Construct a buffer and copy the specified number of bytes into it. The
+  // source array may be (const) uint8_t*, int8_t*, or char*.
+  template <typename T,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  CopyOnWriteBuffer(const T* data, size_t size)
+      : CopyOnWriteBuffer(data, size, size) {}
+  template <typename T,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  CopyOnWriteBuffer(const T* data, size_t size, size_t capacity)
+      : CopyOnWriteBuffer(size, capacity) {
+    if (buffer_) {
+      std::memcpy(buffer_->data(), data, size);
+    }
+  }
+
+  // Construct a buffer from the contents of an array.
+  template <typename T,
+            size_t N,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  CopyOnWriteBuffer(const T (&array)[N])  // NOLINT: runtime/explicit
+      : CopyOnWriteBuffer(array, N) {}
+
+  ~CopyOnWriteBuffer();
+
+  // Get a pointer to the data. Just .data() will give you a (const) uint8_t*,
+  // but you may also use .data<int8_t>() and .data<char>().
+  template <typename T = uint8_t,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  const T* data() const {
+    return cdata<T>();
+  }
+
+  // Get writable pointer to the data. This will create a copy of the underlying
+  // data if it is shared with other buffers.
+  template <typename T = uint8_t,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  T* data() {
+    RTC_DCHECK(IsConsistent());
+    if (!buffer_) {
+      return nullptr;
+    }
+    CloneDataIfReferenced(buffer_->capacity());
+    return buffer_->data<T>();
+  }
+
+  // Get const pointer to the data. This will not create a copy of the
+  // underlying data if it is shared with other buffers.
+  template <typename T = uint8_t,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  const T* cdata() const {
+    RTC_DCHECK(IsConsistent());
+    if (!buffer_) {
+      return nullptr;
+    }
+    return buffer_->data<T>();
+  }
+
+  size_t size() const {
+    RTC_DCHECK(IsConsistent());
+    return buffer_ ? buffer_->size() : 0;
+  }
+
+  size_t capacity() const {
+    RTC_DCHECK(IsConsistent());
+    return buffer_ ? buffer_->capacity() : 0;
+  }
+
+  CopyOnWriteBuffer& operator=(const CopyOnWriteBuffer& buf) {
+    RTC_DCHECK(IsConsistent());
+    RTC_DCHECK(buf.IsConsistent());
+    if (&buf != this) {
+      buffer_ = buf.buffer_;
+    }
+    return *this;
+  }
+
+  CopyOnWriteBuffer& operator=(CopyOnWriteBuffer&& buf) {
+    RTC_DCHECK(IsConsistent());
+    RTC_DCHECK(buf.IsConsistent());
+    buffer_ = std::move(buf.buffer_);
+    return *this;
+  }
+
+  bool operator==(const CopyOnWriteBuffer& buf) const;
+
+  bool operator!=(const CopyOnWriteBuffer& buf) const {
+    return !(*this == buf);
+  }
+
+  uint8_t& operator[](size_t index) {
+    RTC_DCHECK_LT(index, size());
+    return data()[index];
+  }
+
+  uint8_t operator[](size_t index) const {
+    RTC_DCHECK_LT(index, size());
+    return cdata()[index];
+  }
+
+  // Replace the contents of the buffer. Accepts the same types as the
+  // constructors.
+  template <typename T,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  void SetData(const T* data, size_t size) {
+    RTC_DCHECK(IsConsistent());
+    if (!buffer_) {
+      buffer_ = size > 0 ? new RefCountedObject<Buffer>(data, size) : nullptr;
+    } else if (!buffer_->HasOneRef()) {
+      buffer_ = new RefCountedObject<Buffer>(data, size, buffer_->capacity());
+    } else {
+      buffer_->SetData(data, size);
+    }
+    RTC_DCHECK(IsConsistent());
+  }
+
+  template <typename T,
+            size_t N,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  void SetData(const T (&array)[N]) {
+    SetData(array, N);
+  }
+
+  void SetData(const CopyOnWriteBuffer& buf) {
+    RTC_DCHECK(IsConsistent());
+    RTC_DCHECK(buf.IsConsistent());
+    if (&buf != this) {
+      buffer_ = buf.buffer_;
+    }
+  }
+
+  // Append data to the buffer. Accepts the same types as the constructors.
+  template <typename T,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  void AppendData(const T* data, size_t size) {
+    RTC_DCHECK(IsConsistent());
+    if (!buffer_) {
+      buffer_ = new RefCountedObject<Buffer>(data, size);
+      RTC_DCHECK(IsConsistent());
+      return;
+    }
+
+    CloneDataIfReferenced(std::max(buffer_->capacity(),
+        buffer_->size() + size));
+    buffer_->AppendData(data, size);
+    RTC_DCHECK(IsConsistent());
+  }
+
+  template <typename T,
+            size_t N,
+            typename std::enable_if<
+                internal::BufferCompat<uint8_t, T>::value>::type* = nullptr>
+  void AppendData(const T (&array)[N]) {
+    AppendData(array, N);
+  }
+
+  void AppendData(const CopyOnWriteBuffer& buf) {
+    AppendData(buf.data(), buf.size());
+  }
+
+  // Sets the size of the buffer. If the new size is smaller than the old, the
+  // buffer contents will be kept but truncated; if the new size is greater,
+  // the existing contents will be kept and the new space will be
+  // uninitialized.
+  void SetSize(size_t size);
+
+  // Ensure that the buffer size can be increased to at least capacity without
+  // further reallocation. (Of course, this operation might need to reallocate
+  // the buffer.)
+  void EnsureCapacity(size_t capacity);
+
+  // Resets the buffer to zero size without altering capacity. Works even if the
+  // buffer has been moved from.
+  void Clear();
+
+  // Swaps two buffers.
+  friend void swap(CopyOnWriteBuffer& a, CopyOnWriteBuffer& b) {
+    std::swap(a.buffer_, b.buffer_);
+  }
+
+ private:
+  // Create a copy of the underlying data if it is referenced from other Buffer
+  // objects.
+  void CloneDataIfReferenced(size_t new_capacity);
+
+  // Pre- and postcondition of all methods.
+  bool IsConsistent() const {
+    return (!buffer_ || buffer_->capacity() > 0);
+  }
+
+  // buffer_ is either null, or points to an rtc::Buffer with capacity > 0.
+  scoped_refptr<RefCountedObject<Buffer>> buffer_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_COPYONWRITEBUFFER_H_
diff --git a/base/copyonwritebuffer_unittest.cc b/base/copyonwritebuffer_unittest.cc
new file mode 100644
index 0000000..fefd0c7
--- /dev/null
+++ b/base/copyonwritebuffer_unittest.cc
@@ -0,0 +1,319 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/copyonwritebuffer.h"
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+namespace {
+
+// clang-format off
+const uint8_t kTestData[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
+                             0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+// clang-format on
+
+}  // namespace
+
+void EnsureBuffersShareData(const CopyOnWriteBuffer& buf1,
+                            const CopyOnWriteBuffer& buf2) {
+  // Data is shared between buffers.
+  EXPECT_EQ(buf1.size(), buf2.size());
+  EXPECT_EQ(buf1.capacity(), buf2.capacity());
+  const uint8_t* data1 = buf1.data();
+  const uint8_t* data2 = buf2.data();
+  EXPECT_EQ(data1, data2);
+  EXPECT_EQ(buf1, buf2);
+}
+
+void EnsureBuffersDontShareData(const CopyOnWriteBuffer& buf1,
+                                const CopyOnWriteBuffer& buf2) {
+  // Data is not shared between buffers.
+  const uint8_t* data1 = buf1.cdata();
+  const uint8_t* data2 = buf2.cdata();
+  EXPECT_NE(data1, data2);
+}
+
+TEST(CopyOnWriteBufferTest, TestCreateEmptyData) {
+  CopyOnWriteBuffer buf(static_cast<const uint8_t*>(nullptr), 0);
+  EXPECT_EQ(buf.size(), 0u);
+  EXPECT_EQ(buf.capacity(), 0u);
+  EXPECT_EQ(buf.data(), nullptr);
+}
+
+TEST(CopyOnWriteBufferTest, TestMoveConstruct) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  size_t buf1_size = buf1.size();
+  size_t buf1_capacity = buf1.capacity();
+  const uint8_t* buf1_data = buf1.cdata();
+
+  CopyOnWriteBuffer buf2(std::move(buf1));
+  EXPECT_EQ(buf1.size(), 0u);
+  EXPECT_EQ(buf1.capacity(), 0u);
+  EXPECT_EQ(buf1.data(), nullptr);
+  EXPECT_EQ(buf2.size(), buf1_size);
+  EXPECT_EQ(buf2.capacity(), buf1_capacity);
+  EXPECT_EQ(buf2.data(), buf1_data);
+}
+
+TEST(CopyOnWriteBufferTest, TestMoveAssign) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  size_t buf1_size = buf1.size();
+  size_t buf1_capacity = buf1.capacity();
+  const uint8_t* buf1_data = buf1.cdata();
+
+  CopyOnWriteBuffer buf2;
+  buf2 = std::move(buf1);
+  EXPECT_EQ(buf1.size(), 0u);
+  EXPECT_EQ(buf1.capacity(), 0u);
+  EXPECT_EQ(buf1.data(), nullptr);
+  EXPECT_EQ(buf2.size(), buf1_size);
+  EXPECT_EQ(buf2.capacity(), buf1_capacity);
+  EXPECT_EQ(buf2.data(), buf1_data);
+}
+
+TEST(CopyOnWriteBufferTest, TestSwap) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  size_t buf1_size = buf1.size();
+  size_t buf1_capacity = buf1.capacity();
+  const uint8_t* buf1_data = buf1.cdata();
+
+  CopyOnWriteBuffer buf2(kTestData, 6, 20);
+  size_t buf2_size = buf2.size();
+  size_t buf2_capacity = buf2.capacity();
+  const uint8_t* buf2_data = buf2.cdata();
+
+  std::swap(buf1, buf2);
+  EXPECT_EQ(buf1.size(), buf2_size);
+  EXPECT_EQ(buf1.capacity(), buf2_capacity);
+  EXPECT_EQ(buf1.data(), buf2_data);
+  EXPECT_EQ(buf2.size(), buf1_size);
+  EXPECT_EQ(buf2.capacity(), buf1_capacity);
+  EXPECT_EQ(buf2.data(), buf1_data);
+}
+
+TEST(CopyOnWriteBufferTest, TestAppendData) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2(buf1);
+
+  EnsureBuffersShareData(buf1, buf2);
+
+  // AppendData copies the underlying buffer.
+  buf2.AppendData("foo");
+  EXPECT_EQ(buf2.size(), buf1.size() + 4);  // "foo" + trailing 0x00
+  EXPECT_EQ(buf2.capacity(), buf1.capacity());
+  EXPECT_NE(buf2.data(), buf1.data());
+
+  EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 3));
+  const int8_t exp[] = {0x0, 0x1, 0x2, 'f', 'o', 'o', 0x0};
+  EXPECT_EQ(buf2, CopyOnWriteBuffer(exp));
+}
+
+TEST(CopyOnWriteBufferTest, SetEmptyData) {
+  CopyOnWriteBuffer buf(10);
+
+  buf.SetData<uint8_t>(nullptr, 0);
+
+  EXPECT_EQ(0u, buf.size());
+}
+
+TEST(CopyOnWriteBufferTest, SetDataNoMoreThanCapacityDoesntCauseReallocation) {
+  CopyOnWriteBuffer buf1(3, 10);
+  const uint8_t* const original_allocation = buf1.cdata();
+
+  buf1.SetData(kTestData, 10);
+
+  EXPECT_EQ(original_allocation, buf1.cdata());
+  EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 10));
+}
+
+TEST(CopyOnWriteBufferTest, SetDataMakeReferenceCopy) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2;
+
+  buf2.SetData(buf1);
+
+  EnsureBuffersShareData(buf1, buf2);
+}
+
+TEST(CopyOnWriteBufferTest, SetDataOnSharedKeepsOriginal) {
+  const uint8_t data[] = "foo";
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  const uint8_t* const original_allocation = buf1.cdata();
+  CopyOnWriteBuffer buf2(buf1);
+
+  buf2.SetData(data);
+
+  EnsureBuffersDontShareData(buf1, buf2);
+  EXPECT_EQ(original_allocation, buf1.cdata());
+  EXPECT_EQ(buf1, CopyOnWriteBuffer(kTestData, 3));
+  EXPECT_EQ(buf2, CopyOnWriteBuffer(data));
+}
+
+TEST(CopyOnWriteBufferTest, SetDataOnSharedKeepsCapacity) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2(buf1);
+  EnsureBuffersShareData(buf1, buf2);
+
+  buf2.SetData(kTestData, 2);
+
+  EnsureBuffersDontShareData(buf1, buf2);
+  EXPECT_EQ(2u, buf2.size());
+  EXPECT_EQ(10u, buf2.capacity());
+}
+
+TEST(CopyOnWriteBufferTest, TestEnsureCapacity) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2(buf1);
+
+  // Smaller than existing capacity -> no change and still same contents.
+  buf2.EnsureCapacity(8);
+  EnsureBuffersShareData(buf1, buf2);
+  EXPECT_EQ(buf1.size(), 3u);
+  EXPECT_EQ(buf1.capacity(), 10u);
+  EXPECT_EQ(buf2.size(), 3u);
+  EXPECT_EQ(buf2.capacity(), 10u);
+
+  // Lager than existing capacity -> data is cloned.
+  buf2.EnsureCapacity(16);
+  EnsureBuffersDontShareData(buf1, buf2);
+  EXPECT_EQ(buf1.size(), 3u);
+  EXPECT_EQ(buf1.capacity(), 10u);
+  EXPECT_EQ(buf2.size(), 3u);
+  EXPECT_EQ(buf2.capacity(), 16u);
+  // The size and contents are still the same.
+  EXPECT_EQ(buf1, buf2);
+}
+
+TEST(CopyOnWriteBufferTest, SetSizeDoesntChangeOriginal) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  const uint8_t* const original_allocation = buf1.cdata();
+  CopyOnWriteBuffer buf2(buf1);
+
+  buf2.SetSize(16);
+
+  EnsureBuffersDontShareData(buf1, buf2);
+  EXPECT_EQ(original_allocation, buf1.cdata());
+  EXPECT_EQ(3u, buf1.size());
+  EXPECT_EQ(10u, buf1.capacity());
+}
+
+TEST(CopyOnWriteBufferTest, SetSizeCloneContent) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2(buf1);
+
+  buf2.SetSize(16);
+
+  EXPECT_EQ(buf2.size(), 16u);
+  EXPECT_EQ(0, memcmp(buf2.data(), kTestData, 3));
+}
+
+TEST(CopyOnWriteBufferTest, SetSizeMayIncreaseCapacity) {
+  CopyOnWriteBuffer buf(kTestData, 3, 10);
+
+  buf.SetSize(16);
+
+  EXPECT_EQ(16u, buf.size());
+  EXPECT_EQ(16u, buf.capacity());
+}
+
+TEST(CopyOnWriteBufferTest, SetSizeDoesntDecreaseCapacity) {
+  CopyOnWriteBuffer buf1(kTestData, 5, 10);
+  CopyOnWriteBuffer buf2(buf1);
+
+  buf2.SetSize(2);
+
+  EXPECT_EQ(2u, buf2.size());
+  EXPECT_EQ(10u, buf2.capacity());
+}
+
+TEST(CopyOnWriteBufferTest, ClearDoesntChangeOriginal) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  const uint8_t* const original_allocation = buf1.cdata();
+  CopyOnWriteBuffer buf2(buf1);
+
+  buf2.Clear();
+
+  EnsureBuffersDontShareData(buf1, buf2);
+  EXPECT_EQ(3u, buf1.size());
+  EXPECT_EQ(10u, buf1.capacity());
+  EXPECT_EQ(original_allocation, buf1.cdata());
+  EXPECT_EQ(0u, buf2.size());
+}
+
+TEST(CopyOnWriteBufferTest, ClearDoesntChangeCapacity) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2(buf1);
+
+  buf2.Clear();
+
+  EXPECT_EQ(0u, buf2.size());
+  EXPECT_EQ(10u, buf2.capacity());
+}
+
+TEST(CopyOnWriteBufferTest, TestConstDataAccessor) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2(buf1);
+
+  // .cdata() doesn't clone data.
+  const uint8_t* cdata1 = buf1.cdata();
+  const uint8_t* cdata2 = buf2.cdata();
+  EXPECT_EQ(cdata1, cdata2);
+
+  // Non-const .data() clones data if shared.
+  const uint8_t* data1 = buf1.data();
+  const uint8_t* data2 = buf2.data();
+  EXPECT_NE(data1, data2);
+  // buf1 was cloned above.
+  EXPECT_NE(data1, cdata1);
+  // Therefore buf2 was no longer sharing data and was not cloned.
+  EXPECT_EQ(data2, cdata1);
+}
+
+TEST(CopyOnWriteBufferTest, TestBacketRead) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2(buf1);
+
+  EnsureBuffersShareData(buf1, buf2);
+  // Non-const reads clone the data if shared.
+  for (size_t i = 0; i != 3u; ++i) {
+    EXPECT_EQ(buf1[i], kTestData[i]);
+  }
+  EnsureBuffersDontShareData(buf1, buf2);
+}
+
+TEST(CopyOnWriteBufferTest, TestBacketReadConst) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2(buf1);
+
+  EnsureBuffersShareData(buf1, buf2);
+  const CopyOnWriteBuffer& cbuf1 = buf1;
+  for (size_t i = 0; i != 3u; ++i) {
+    EXPECT_EQ(cbuf1[i], kTestData[i]);
+  }
+  EnsureBuffersShareData(buf1, buf2);
+}
+
+TEST(CopyOnWriteBufferTest, TestBacketWrite) {
+  CopyOnWriteBuffer buf1(kTestData, 3, 10);
+  CopyOnWriteBuffer buf2(buf1);
+
+  EnsureBuffersShareData(buf1, buf2);
+  for (size_t i = 0; i != 3u; ++i) {
+    buf1[i] = kTestData[i] + 1;
+  }
+  EXPECT_EQ(buf1.size(), 3u);
+  EXPECT_EQ(buf1.capacity(), 10u);
+  EXPECT_EQ(buf2.size(), 3u);
+  EXPECT_EQ(buf2.capacity(), 10u);
+  EXPECT_EQ(0, memcmp(buf2.cdata(), kTestData, 3));
+}
+
+}  // namespace rtc
diff --git a/base/cpu_time.cc b/base/cpu_time.cc
new file mode 100644
index 0000000..5fce366
--- /dev/null
+++ b/base/cpu_time.cc
@@ -0,0 +1,114 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/cpu_time.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/timeutils.h"
+
+#if defined(WEBRTC_LINUX)
+#include <time.h>
+#elif defined(WEBRTC_MAC)
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <mach/thread_info.h>
+#include <mach/thread_act.h>
+#include <mach/mach_init.h>
+#include <unistd.h>
+#elif defined(WEBRTC_WIN)
+#include <windows.h>
+#endif
+
+#if defined(WEBRTC_WIN)
+namespace {
+// FILETIME resolution is 100 nanosecs.
+const int64_t kNanosecsPerFiletime = 100;
+}  // namespace
+#endif
+
+namespace rtc {
+
+int64_t GetProcessCpuTimeNanos() {
+#if defined(WEBRTC_LINUX)
+  struct timespec ts;
+  if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) {
+    return ts.tv_sec * kNumNanosecsPerSec + ts.tv_nsec;
+  } else {
+    LOG_ERR(LS_ERROR) << "clock_gettime() failed.";
+  }
+#elif defined(WEBRTC_MAC)
+  struct rusage rusage;
+  if (getrusage(RUSAGE_SELF, &rusage) == 0) {
+    return rusage.ru_utime.tv_sec * kNumNanosecsPerSec +
+           rusage.ru_utime.tv_usec * kNumNanosecsPerMicrosec;
+  } else {
+    LOG_ERR(LS_ERROR) << "getrusage() failed.";
+  }
+#elif defined(WEBRTC_WIN)
+  FILETIME createTime;
+  FILETIME exitTime;
+  FILETIME kernelTime;
+  FILETIME userTime;
+  if (GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime,
+                      &userTime) != 0) {
+    return ((static_cast<uint64_t>(userTime.dwHighDateTime) << 32) +
+            userTime.dwLowDateTime) *
+           kNanosecsPerFiletime;
+  } else {
+    LOG_ERR(LS_ERROR) << "GetProcessTimes() failed.";
+  }
+#else
+  // Not implemented yet.
+  static_assert(
+      false, "GetProcessCpuTimeNanos() platform support not yet implemented.");
+#endif
+  return -1;
+}
+
+int64_t GetThreadCpuTimeNanos() {
+#if defined(WEBRTC_LINUX)
+  struct timespec ts;
+  if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) {
+    return ts.tv_sec * kNumNanosecsPerSec + ts.tv_nsec;
+  } else {
+    LOG_ERR(LS_ERROR) << "clock_gettime() failed.";
+  }
+#elif defined(WEBRTC_MAC)
+  thread_basic_info_data_t info;
+  mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
+  if (thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)&info,
+                  &count) == KERN_SUCCESS) {
+    return info.user_time.seconds * kNumNanosecsPerSec +
+           info.user_time.microseconds * kNumNanosecsPerMicrosec;
+  } else {
+    LOG_ERR(LS_ERROR) << "thread_info() failed.";
+  }
+#elif defined(WEBRTC_WIN)
+  FILETIME createTime;
+  FILETIME exitTime;
+  FILETIME kernelTime;
+  FILETIME userTime;
+  if (GetThreadTimes(GetCurrentThread(), &createTime, &exitTime, &kernelTime,
+                     &userTime) != 0) {
+    return ((static_cast<uint64_t>(userTime.dwHighDateTime) << 32) +
+            userTime.dwLowDateTime) *
+           kNanosecsPerFiletime;
+  } else {
+    LOG_ERR(LS_ERROR) << "GetThreadTimes() failed.";
+  }
+#else
+  // Not implemented yet.
+  static_assert(
+      false, "GetProcessCpuTimeNanos() platform support not yet implemented.");
+#endif
+  return -1;
+}
+
+}  // namespace rtc
diff --git a/base/cpu_time.h b/base/cpu_time.h
index f627790..87e9418 100644
--- a/base/cpu_time.h
+++ b/base/cpu_time.h
@@ -11,9 +11,18 @@
 #ifndef WEBRTC_BASE_CPU_TIME_H_
 #define WEBRTC_BASE_CPU_TIME_H_
 
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/cpu_time.h"
+namespace rtc {
+
+// Returns total CPU time of a current process in nanoseconds.
+// Time base is unknown, therefore use only to calculate deltas.
+int64_t GetProcessCpuTimeNanos();
+
+// Returns total CPU time of a current thread in nanoseconds.
+// Time base is unknown, therefore use only to calculate deltas.
+int64_t GetThreadCpuTimeNanos();
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_CPU_TIME_H_
diff --git a/base/cpu_time_unittest.cc b/base/cpu_time_unittest.cc
new file mode 100644
index 0000000..bfdd7ef
--- /dev/null
+++ b/base/cpu_time_unittest.cc
@@ -0,0 +1,106 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <algorithm>
+#include "webrtc/base/cpu_time.h"
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/test/gtest.h"
+#include "webrtc/system_wrappers/include/cpu_info.h"
+#include "webrtc/system_wrappers/include/sleep.h"
+
+// Only run these tests on non-instrumented builds, because timing on
+// instrumented builds is unreliable, causing the test to be flaky.
+#if defined(THREAD_SANITIZER) || defined(MEMORY_SANITIZER) || \
+    defined(ADDRESS_SANITIZER)
+#define MAYBE_TEST(test_name) DISABLED_##test_name
+#else
+#define MAYBE_TEST(test_name) test_name
+#endif
+
+namespace {
+const int kAllowedErrorMillisecs = 30;
+const int kProcessingTimeMillisecs = 300;
+const int kWorkingThreads = 2;
+
+// Consumes approximately kProcessingTimeMillisecs of CPU time in single thread.
+bool WorkingFunction(void* counter_pointer) {
+  int64_t* counter = reinterpret_cast<int64_t*>(counter_pointer);
+  *counter = 0;
+  int64_t stop_cpu_time =
+      rtc::GetThreadCpuTimeNanos() +
+      kProcessingTimeMillisecs * rtc::kNumNanosecsPerMillisec;
+  while (rtc::GetThreadCpuTimeNanos() < stop_cpu_time) {
+    (*counter)++;
+  }
+  return false;
+}
+}  // namespace
+
+namespace rtc {
+
+// A minimal test which can be run on instrumented builds, so that they're at
+// least exercising the code to check for memory leaks/etc.
+TEST(CpuTimeTest, BasicTest) {
+  int64_t process_start_time_nanos = GetProcessCpuTimeNanos();
+  int64_t thread_start_time_nanos = GetThreadCpuTimeNanos();
+  int64_t process_duration_nanos =
+      GetProcessCpuTimeNanos() - process_start_time_nanos;
+  int64_t thread_duration_nanos =
+      GetThreadCpuTimeNanos() - thread_start_time_nanos;
+  EXPECT_GE(process_duration_nanos, 0);
+  EXPECT_GE(thread_duration_nanos, 0);
+}
+
+TEST(CpuTimeTest, MAYBE_TEST(TwoThreads)) {
+  int64_t process_start_time_nanos = GetProcessCpuTimeNanos();
+  int64_t thread_start_time_nanos = GetThreadCpuTimeNanos();
+  int64_t counter1;
+  int64_t counter2;
+  PlatformThread thread1(WorkingFunction, reinterpret_cast<void*>(&counter1),
+                         "Thread1");
+  PlatformThread thread2(WorkingFunction, reinterpret_cast<void*>(&counter2),
+                         "Thread2");
+  thread1.Start();
+  thread2.Start();
+  thread1.Stop();
+  thread2.Stop();
+
+  EXPECT_GE(counter1, 0);
+  EXPECT_GE(counter2, 0);
+  int64_t process_duration_nanos =
+      GetProcessCpuTimeNanos() - process_start_time_nanos;
+  int64_t thread_duration_nanos =
+      GetThreadCpuTimeNanos() - thread_start_time_nanos;
+  // This thread did almost nothing.
+  // Therefore GetThreadCpuTime is not a wall clock.
+  EXPECT_LE(thread_duration_nanos,
+            kAllowedErrorMillisecs * kNumNanosecsPerMillisec);
+  // Total process time is at least twice working threads' CPU time.
+  // Therefore process and thread times are correctly related.
+  EXPECT_GE(
+      process_duration_nanos,
+      kWorkingThreads * (kProcessingTimeMillisecs - kAllowedErrorMillisecs)
+      * kNumNanosecsPerMillisec);
+}
+
+TEST(CpuTimeTest, MAYBE_TEST(Sleeping)) {
+  int64_t process_start_time_nanos = GetProcessCpuTimeNanos();
+  webrtc::SleepMs(kProcessingTimeMillisecs);
+  int64_t process_duration_nanos =
+      GetProcessCpuTimeNanos() - process_start_time_nanos;
+  // Sleeping should not introduce any additional CPU time.
+  // Therefore GetProcessCpuTime is not a wall clock.
+  EXPECT_LE(process_duration_nanos,
+            kWorkingThreads * kAllowedErrorMillisecs * kNumNanosecsPerMillisec);
+}
+
+}  // namespace rtc
diff --git a/base/crc32.cc b/base/crc32.cc
new file mode 100644
index 0000000..97b8214
--- /dev/null
+++ b/base/crc32.cc
@@ -0,0 +1,52 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/crc32.h"
+
+#include "webrtc/base/arraysize.h"
+
+namespace rtc {
+
+// This implementation is based on the sample implementation in RFC 1952.
+
+// CRC32 polynomial, in reversed form.
+// See RFC 1952, or http://en.wikipedia.org/wiki/Cyclic_redundancy_check
+static const uint32_t kCrc32Polynomial = 0xEDB88320;
+static uint32_t kCrc32Table[256] = {0};
+
+static void EnsureCrc32TableInited() {
+  if (kCrc32Table[arraysize(kCrc32Table) - 1])
+    return;  // already inited
+  for (uint32_t i = 0; i < arraysize(kCrc32Table); ++i) {
+    uint32_t c = i;
+    for (size_t j = 0; j < 8; ++j) {
+      if (c & 1) {
+        c = kCrc32Polynomial ^ (c >> 1);
+      } else {
+        c >>= 1;
+      }
+    }
+    kCrc32Table[i] = c;
+  }
+}
+
+uint32_t UpdateCrc32(uint32_t start, const void* buf, size_t len) {
+  EnsureCrc32TableInited();
+
+  uint32_t c = start ^ 0xFFFFFFFF;
+  const uint8_t* u = static_cast<const uint8_t*>(buf);
+  for (size_t i = 0; i < len; ++i) {
+    c = kCrc32Table[(c ^ u[i]) & 0xFF] ^ (c >> 8);
+  }
+  return c ^ 0xFFFFFFFF;
+}
+
+}  // namespace rtc
+
diff --git a/base/crc32.h b/base/crc32.h
index 6854567..9661876 100644
--- a/base/crc32.h
+++ b/base/crc32.h
@@ -11,9 +11,24 @@
 #ifndef WEBRTC_BASE_CRC32_H_
 #define WEBRTC_BASE_CRC32_H_
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/crc32.h"
+#include "webrtc/base/basictypes.h"
+
+namespace rtc {
+
+// Updates a CRC32 checksum with |len| bytes from |buf|. |initial| holds the
+// checksum result from the previous update; for the first call, it should be 0.
+uint32_t UpdateCrc32(uint32_t initial, const void* buf, size_t len);
+
+// Computes a CRC32 checksum using |len| bytes from |buf|.
+inline uint32_t ComputeCrc32(const void* buf, size_t len) {
+  return UpdateCrc32(0, buf, len);
+}
+inline uint32_t ComputeCrc32(const std::string& str) {
+  return ComputeCrc32(str.c_str(), str.size());
+}
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_CRC32_H_
diff --git a/base/crc32_unittest.cc b/base/crc32_unittest.cc
new file mode 100644
index 0000000..6da5c32
--- /dev/null
+++ b/base/crc32_unittest.cc
@@ -0,0 +1,35 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/crc32.h"
+#include "webrtc/base/gunit.h"
+
+#include <string>
+
+namespace rtc {
+
+TEST(Crc32Test, TestBasic) {
+  EXPECT_EQ(0U, ComputeCrc32(""));
+  EXPECT_EQ(0x352441C2U, ComputeCrc32("abc"));
+  EXPECT_EQ(0x171A3F5FU,
+      ComputeCrc32("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"));
+}
+
+TEST(Crc32Test, TestMultipleUpdates) {
+  std::string input =
+      "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+  uint32_t c = 0;
+  for (size_t i = 0; i < input.size(); ++i) {
+    c = UpdateCrc32(c, &input[i], 1);
+  }
+  EXPECT_EQ(0x171A3F5FU, c);
+}
+
+}  // namespace rtc
diff --git a/base/criticalsection.cc b/base/criticalsection.cc
new file mode 100644
index 0000000..5a7bc76
--- /dev/null
+++ b/base/criticalsection.cc
@@ -0,0 +1,252 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/criticalsection.h"
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/platform_thread.h"
+
+// TODO(tommi): Split this file up to per-platform implementation files.
+
+namespace rtc {
+
+CriticalSection::CriticalSection() {
+#if defined(WEBRTC_WIN)
+  InitializeCriticalSection(&crit_);
+#elif defined(WEBRTC_POSIX)
+# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
+  lock_queue_ = 0;
+  owning_thread_ = 0;
+  recursion_ = 0;
+  semaphore_ = dispatch_semaphore_create(0);
+# else
+  pthread_mutexattr_t mutex_attribute;
+  pthread_mutexattr_init(&mutex_attribute);
+  pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
+  pthread_mutex_init(&mutex_, &mutex_attribute);
+  pthread_mutexattr_destroy(&mutex_attribute);
+# endif
+  CS_DEBUG_CODE(thread_ = 0);
+  CS_DEBUG_CODE(recursion_count_ = 0);
+  RTC_UNUSED(thread_);
+  RTC_UNUSED(recursion_count_);
+#else
+# error Unsupported platform.
+#endif
+}
+
+CriticalSection::~CriticalSection() {
+#if defined(WEBRTC_WIN)
+  DeleteCriticalSection(&crit_);
+#elif defined(WEBRTC_POSIX)
+# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
+  dispatch_release(semaphore_);
+# else
+  pthread_mutex_destroy(&mutex_);
+# endif
+#else
+# error Unsupported platform.
+#endif
+}
+
+void CriticalSection::Enter() const EXCLUSIVE_LOCK_FUNCTION() {
+#if defined(WEBRTC_WIN)
+  EnterCriticalSection(&crit_);
+#elif defined(WEBRTC_POSIX)
+# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
+  int spin = 3000;
+  PlatformThreadRef self = CurrentThreadRef();
+  bool have_lock = false;
+  do {
+    // Instead of calling TryEnter() in this loop, we do two interlocked
+    // operations, first a read-only one in order to avoid affecting the lock
+    // cache-line while spinning, in case another thread is using the lock.
+    if (!IsThreadRefEqual(owning_thread_, self)) {
+      if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
+        if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
+          have_lock = true;
+          break;
+        }
+      }
+    } else {
+      AtomicOps::Increment(&lock_queue_);
+      have_lock = true;
+      break;
+    }
+
+    sched_yield();
+  } while (--spin);
+
+  if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
+    // Owning thread cannot be the current thread since TryEnter() would
+    // have succeeded.
+    RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
+    // Wait for the lock to become available.
+    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
+    RTC_DCHECK(owning_thread_ == 0);
+    RTC_DCHECK(!recursion_);
+  }
+
+  owning_thread_ = self;
+  ++recursion_;
+
+# else
+  pthread_mutex_lock(&mutex_);
+# endif
+
+# if CS_DEBUG_CHECKS
+  if (!recursion_count_) {
+    RTC_DCHECK(!thread_);
+    thread_ = CurrentThreadRef();
+  } else {
+    RTC_DCHECK(CurrentThreadIsOwner());
+  }
+  ++recursion_count_;
+# endif
+#else
+# error Unsupported platform.
+#endif
+}
+
+bool CriticalSection::TryEnter() const EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+#if defined(WEBRTC_WIN)
+  return TryEnterCriticalSection(&crit_) != FALSE;
+#elif defined(WEBRTC_POSIX)
+# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
+  if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
+    if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
+      return false;
+    owning_thread_ = CurrentThreadRef();
+    RTC_DCHECK(!recursion_);
+  } else {
+    AtomicOps::Increment(&lock_queue_);
+  }
+  ++recursion_;
+# else
+  if (pthread_mutex_trylock(&mutex_) != 0)
+    return false;
+# endif
+# if CS_DEBUG_CHECKS
+  if (!recursion_count_) {
+    RTC_DCHECK(!thread_);
+    thread_ = CurrentThreadRef();
+  } else {
+    RTC_DCHECK(CurrentThreadIsOwner());
+  }
+  ++recursion_count_;
+# endif
+  return true;
+#else
+# error Unsupported platform.
+#endif
+}
+
+void CriticalSection::Leave() const UNLOCK_FUNCTION() {
+  RTC_DCHECK(CurrentThreadIsOwner());
+#if defined(WEBRTC_WIN)
+  LeaveCriticalSection(&crit_);
+#elif defined(WEBRTC_POSIX)
+# if CS_DEBUG_CHECKS
+  --recursion_count_;
+  RTC_DCHECK(recursion_count_ >= 0);
+  if (!recursion_count_)
+    thread_ = 0;
+# endif
+# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
+  RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
+  RTC_DCHECK_GE(recursion_, 0);
+  --recursion_;
+  if (!recursion_)
+    owning_thread_ = 0;
+
+  if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
+    dispatch_semaphore_signal(semaphore_);
+# else
+  pthread_mutex_unlock(&mutex_);
+# endif
+#else
+# error Unsupported platform.
+#endif
+}
+
+bool CriticalSection::CurrentThreadIsOwner() const {
+#if defined(WEBRTC_WIN)
+  // OwningThread has type HANDLE but actually contains the Thread ID:
+  // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
+  // Converting through size_t avoids the VS 2015 warning C4312: conversion from
+  // 'type1' to 'type2' of greater size
+  return crit_.OwningThread ==
+         reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
+#elif defined(WEBRTC_POSIX)
+# if CS_DEBUG_CHECKS
+  return IsThreadRefEqual(thread_, CurrentThreadRef());
+# else
+  return true;
+# endif  // CS_DEBUG_CHECKS
+#else
+# error Unsupported platform.
+#endif
+}
+
+CritScope::CritScope(const CriticalSection* cs) : cs_(cs) { cs_->Enter(); }
+CritScope::~CritScope() { cs_->Leave(); }
+
+TryCritScope::TryCritScope(const CriticalSection* cs)
+    : cs_(cs), locked_(cs->TryEnter()) {
+  CS_DEBUG_CODE(lock_was_called_ = false);
+  RTC_UNUSED(lock_was_called_);
+}
+
+TryCritScope::~TryCritScope() {
+  CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_));
+  if (locked_)
+    cs_->Leave();
+}
+
+bool TryCritScope::locked() const {
+  CS_DEBUG_CODE(lock_was_called_ = true);
+  return locked_;
+}
+
+void GlobalLockPod::Lock() {
+#if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC)
+  const struct timespec ts_null = {0};
+#endif
+
+  while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) {
+#if defined(WEBRTC_WIN)
+    ::Sleep(0);
+#elif defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
+    sched_yield();
+#else
+    nanosleep(&ts_null, nullptr);
+#endif
+  }
+}
+
+void GlobalLockPod::Unlock() {
+  int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0);
+  RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
+}
+
+GlobalLock::GlobalLock() {
+  lock_acquired = 0;
+}
+
+GlobalLockScope::GlobalLockScope(GlobalLockPod* lock)
+    : lock_(lock) {
+  lock_->Lock();
+}
+
+GlobalLockScope::~GlobalLockScope() {
+  lock_->Unlock();
+}
+
+}  // namespace rtc
diff --git a/base/criticalsection.h b/base/criticalsection.h
index ab3f542..d18f24f 100644
--- a/base/criticalsection.h
+++ b/base/criticalsection.h
@@ -11,8 +11,146 @@
 #ifndef WEBRTC_BASE_CRITICALSECTION_H_
 #define WEBRTC_BASE_CRITICALSECTION_H_
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/criticalsection.h"
+#include "webrtc/base/atomicops.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/platform_thread_types.h"
+#include "webrtc/base/thread_annotations.h"
+#include "webrtc/typedefs.h"
+
+#if defined(WEBRTC_WIN)
+// Include winsock2.h before including <windows.h> to maintain consistency with
+// win32.h.  We can't include win32.h directly here since it pulls in
+// headers such as basictypes.h which causes problems in Chromium where webrtc
+// exists as two separate projects, webrtc and libjingle.
+#include <winsock2.h>
+#include <windows.h>
+#include <sal.h>  // must come after windows headers.
+#endif  // defined(WEBRTC_WIN)
+
+#if defined(WEBRTC_POSIX)
+#include <pthread.h>
+#endif
+
+// See notes in the 'Performance' unit test for the effects of this flag.
+#define USE_NATIVE_MUTEX_ON_MAC 0
+
+#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
+#include <dispatch/dispatch.h>
+#endif
+
+#define CS_DEBUG_CHECKS RTC_DCHECK_IS_ON
+
+#if CS_DEBUG_CHECKS
+#define CS_DEBUG_CODE(x) x
+#else  // !CS_DEBUG_CHECKS
+#define CS_DEBUG_CODE(x)
+#endif  // !CS_DEBUG_CHECKS
+
+namespace rtc {
+
+// Locking methods (Enter, TryEnter, Leave)are const to permit protecting
+// members inside a const context without requiring mutable CriticalSections
+// everywhere.
+class LOCKABLE CriticalSection {
+ public:
+  CriticalSection();
+  ~CriticalSection();
+
+  void Enter() const EXCLUSIVE_LOCK_FUNCTION();
+  bool TryEnter() const EXCLUSIVE_TRYLOCK_FUNCTION(true);
+  void Leave() const UNLOCK_FUNCTION();
+
+ private:
+  // Use only for RTC_DCHECKing.
+  bool CurrentThreadIsOwner() const;
+
+#if defined(WEBRTC_WIN)
+  mutable CRITICAL_SECTION crit_;
+#elif defined(WEBRTC_POSIX)
+# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
+  // Number of times the lock has been locked + number of threads waiting.
+  // TODO(tommi): We could use this number and subtract the recursion count
+  // to find places where we have multiple threads contending on the same lock.
+  mutable volatile int lock_queue_;
+  // |recursion_| represents the recursion count + 1 for the thread that owns
+  // the lock. Only modified by the thread that owns the lock.
+  mutable int recursion_;
+  // Used to signal a single waiting thread when the lock becomes available.
+  mutable dispatch_semaphore_t semaphore_;
+  // The thread that currently holds the lock. Required to handle recursion.
+  mutable PlatformThreadRef owning_thread_;
+# else
+  mutable pthread_mutex_t mutex_;
+# endif
+  mutable PlatformThreadRef thread_;  // Only used by RTC_DCHECKs.
+  mutable int recursion_count_;       // Only used by RTC_DCHECKs.
+#else  // !defined(WEBRTC_WIN) && !defined(WEBRTC_POSIX)
+# error Unsupported platform.
+#endif
+};
+
+// CritScope, for serializing execution through a scope.
+class SCOPED_LOCKABLE CritScope {
+ public:
+  explicit CritScope(const CriticalSection* cs) EXCLUSIVE_LOCK_FUNCTION(cs);
+  ~CritScope() UNLOCK_FUNCTION();
+ private:
+  const CriticalSection* const cs_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(CritScope);
+};
+
+// Tries to lock a critical section on construction via
+// CriticalSection::TryEnter, and unlocks on destruction if the
+// lock was taken. Never blocks.
+//
+// IMPORTANT: Unlike CritScope, the lock may not be owned by this thread in
+// subsequent code. Users *must* check locked() to determine if the
+// lock was taken. If you're not calling locked(), you're doing it wrong!
+class TryCritScope {
+ public:
+  explicit TryCritScope(const CriticalSection* cs);
+  ~TryCritScope();
+#if defined(WEBRTC_WIN)
+  _Check_return_ bool locked() const;
+#elif defined(WEBRTC_POSIX)
+  bool locked() const __attribute__ ((__warn_unused_result__));
+#else  // !defined(WEBRTC_WIN) && !defined(WEBRTC_POSIX)
+# error Unsupported platform.
+#endif
+ private:
+  const CriticalSection* const cs_;
+  const bool locked_;
+  mutable bool lock_was_called_;  // Only used by RTC_DCHECKs.
+  RTC_DISALLOW_COPY_AND_ASSIGN(TryCritScope);
+};
+
+// A POD lock used to protect global variables. Do NOT use for other purposes.
+// No custom constructor or private data member should be added.
+class LOCKABLE GlobalLockPod {
+ public:
+  void Lock() EXCLUSIVE_LOCK_FUNCTION();
+
+  void Unlock() UNLOCK_FUNCTION();
+
+  volatile int lock_acquired;
+};
+
+class GlobalLock : public GlobalLockPod {
+ public:
+  GlobalLock();
+};
+
+// GlobalLockScope, for serializing execution through a scope.
+class SCOPED_LOCKABLE GlobalLockScope {
+ public:
+  explicit GlobalLockScope(GlobalLockPod* lock) EXCLUSIVE_LOCK_FUNCTION(lock);
+  ~GlobalLockScope() UNLOCK_FUNCTION();
+ private:
+  GlobalLockPod* const lock_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(GlobalLockScope);
+};
+
+} // namespace rtc
 
 #endif // WEBRTC_BASE_CRITICALSECTION_H_
diff --git a/base/criticalsection_unittest.cc b/base/criticalsection_unittest.cc
new file mode 100644
index 0000000..a16f4f2
--- /dev/null
+++ b/base/criticalsection_unittest.cc
@@ -0,0 +1,413 @@
+/*
+ *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <set>
+#include <vector>
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+namespace {
+
+const int kLongTime = 10000;  // 10 seconds
+const int kNumThreads = 16;
+const int kOperationsToRun = 1000;
+
+class UniqueValueVerifier {
+ public:
+  void Verify(const std::vector<int>& values) {
+    for (size_t i = 0; i < values.size(); ++i) {
+      std::pair<std::set<int>::iterator, bool> result =
+          all_values_.insert(values[i]);
+      // Each value should only be taken by one thread, so if this value
+      // has already been added, something went wrong.
+      EXPECT_TRUE(result.second)
+          << " Thread=" << Thread::Current() << " value=" << values[i];
+    }
+  }
+
+  void Finalize() {}
+
+ private:
+  std::set<int> all_values_;
+};
+
+class CompareAndSwapVerifier {
+ public:
+  CompareAndSwapVerifier() : zero_count_(0) {}
+
+  void Verify(const std::vector<int>& values) {
+    for (auto v : values) {
+      if (v == 0) {
+        EXPECT_EQ(0, zero_count_) << "Thread=" << Thread::Current();
+        ++zero_count_;
+      } else {
+        EXPECT_EQ(1, v) << " Thread=" << Thread::Current();
+      }
+    }
+  }
+
+  void Finalize() {
+    EXPECT_EQ(1, zero_count_);
+  }
+ private:
+  int zero_count_;
+};
+
+class RunnerBase : public MessageHandler {
+ public:
+  explicit RunnerBase(int value)
+      : threads_active_(0),
+        start_event_(true, false),
+        done_event_(true, false),
+        shared_value_(value) {}
+
+  bool Run() {
+    // Signal all threads to start.
+    start_event_.Set();
+
+    // Wait for all threads to finish.
+    return done_event_.Wait(kLongTime);
+  }
+
+  void SetExpectedThreadCount(int count) {
+    threads_active_ = count;
+  }
+
+  int shared_value() const { return shared_value_; }
+
+ protected:
+  // Derived classes must override OnMessage, and call BeforeStart and AfterEnd
+  // at the beginning and the end of OnMessage respectively.
+  void BeforeStart() {
+    ASSERT_TRUE(start_event_.Wait(kLongTime));
+  }
+
+  // Returns true if all threads have finished.
+  bool AfterEnd() {
+    if (AtomicOps::Decrement(&threads_active_) == 0) {
+      done_event_.Set();
+      return true;
+    }
+    return false;
+  }
+
+  int threads_active_;
+  Event start_event_;
+  Event done_event_;
+  int shared_value_;
+};
+
+class LOCKABLE CriticalSectionLock {
+ public:
+  void Lock() EXCLUSIVE_LOCK_FUNCTION() {
+    cs_.Enter();
+  }
+  void Unlock() UNLOCK_FUNCTION() {
+    cs_.Leave();
+  }
+
+ private:
+  CriticalSection cs_;
+};
+
+template <class Lock>
+class LockRunner : public RunnerBase {
+ public:
+  LockRunner() : RunnerBase(0) {}
+
+  void OnMessage(Message* msg) override {
+    BeforeStart();
+
+    lock_.Lock();
+
+    EXPECT_EQ(0, shared_value_);
+    int old = shared_value_;
+
+    // Use a loop to increase the chance of race.
+    for (int i = 0; i < kOperationsToRun; ++i) {
+      ++shared_value_;
+    }
+    EXPECT_EQ(old + kOperationsToRun, shared_value_);
+    shared_value_ = 0;
+
+    lock_.Unlock();
+
+    AfterEnd();
+  }
+
+ private:
+  Lock lock_;
+};
+
+template <class Op, class Verifier>
+class AtomicOpRunner : public RunnerBase {
+ public:
+  explicit AtomicOpRunner(int initial_value) : RunnerBase(initial_value) {}
+
+  void OnMessage(Message* msg) override {
+    BeforeStart();
+
+    std::vector<int> values;
+    values.reserve(kOperationsToRun);
+
+    // Generate a bunch of values by updating shared_value_ atomically.
+    for (int i = 0; i < kOperationsToRun; ++i) {
+      values.push_back(Op::AtomicOp(&shared_value_));
+    }
+
+    { // Add them all to the set.
+      CritScope cs(&all_values_crit_);
+      verifier_.Verify(values);
+    }
+
+    if (AfterEnd()) {
+      verifier_.Finalize();
+    }
+  }
+
+ private:
+  CriticalSection all_values_crit_;
+  Verifier verifier_;
+};
+
+struct IncrementOp {
+  static int AtomicOp(int* i) { return AtomicOps::Increment(i); }
+};
+
+struct DecrementOp {
+  static int AtomicOp(int* i) { return AtomicOps::Decrement(i); }
+};
+
+struct CompareAndSwapOp {
+  static int AtomicOp(int* i) { return AtomicOps::CompareAndSwap(i, 0, 1); }
+};
+
+void StartThreads(std::vector<std::unique_ptr<Thread>>* threads,
+                  MessageHandler* handler) {
+  for (int i = 0; i < kNumThreads; ++i) {
+    std::unique_ptr<Thread> thread(new Thread());
+    thread->Start();
+    thread->Post(RTC_FROM_HERE, handler);
+    threads->push_back(std::move(thread));
+  }
+}
+
+}  // namespace
+
+TEST(AtomicOpsTest, Simple) {
+  int value = 0;
+  EXPECT_EQ(1, AtomicOps::Increment(&value));
+  EXPECT_EQ(1, value);
+  EXPECT_EQ(2, AtomicOps::Increment(&value));
+  EXPECT_EQ(2, value);
+  EXPECT_EQ(1, AtomicOps::Decrement(&value));
+  EXPECT_EQ(1, value);
+  EXPECT_EQ(0, AtomicOps::Decrement(&value));
+  EXPECT_EQ(0, value);
+}
+
+TEST(AtomicOpsTest, SimplePtr) {
+  class Foo {};
+  Foo* volatile foo = nullptr;
+  std::unique_ptr<Foo> a(new Foo());
+  std::unique_ptr<Foo> b(new Foo());
+  // Reading the initial value should work as expected.
+  EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == nullptr);
+  // Setting using compare and swap should work.
+  EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
+                  &foo, static_cast<Foo*>(nullptr), a.get()) == nullptr);
+  EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
+  // Setting another value but with the wrong previous pointer should fail
+  // (remain a).
+  EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
+                  &foo, static_cast<Foo*>(nullptr), b.get()) == a.get());
+  EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
+  // Replacing a with b should work.
+  EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(&foo, a.get(), b.get()) ==
+              a.get());
+  EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == b.get());
+}
+
+TEST(AtomicOpsTest, Increment) {
+  // Create and start lots of threads.
+  AtomicOpRunner<IncrementOp, UniqueValueVerifier> runner(0);
+  std::vector<std::unique_ptr<Thread>> threads;
+  StartThreads(&threads, &runner);
+  runner.SetExpectedThreadCount(kNumThreads);
+
+  // Release the hounds!
+  EXPECT_TRUE(runner.Run());
+  EXPECT_EQ(kOperationsToRun * kNumThreads, runner.shared_value());
+}
+
+TEST(AtomicOpsTest, Decrement) {
+  // Create and start lots of threads.
+  AtomicOpRunner<DecrementOp, UniqueValueVerifier> runner(
+      kOperationsToRun * kNumThreads);
+  std::vector<std::unique_ptr<Thread>> threads;
+  StartThreads(&threads, &runner);
+  runner.SetExpectedThreadCount(kNumThreads);
+
+  // Release the hounds!
+  EXPECT_TRUE(runner.Run());
+  EXPECT_EQ(0, runner.shared_value());
+}
+
+TEST(AtomicOpsTest, CompareAndSwap) {
+  // Create and start lots of threads.
+  AtomicOpRunner<CompareAndSwapOp, CompareAndSwapVerifier> runner(0);
+  std::vector<std::unique_ptr<Thread>> threads;
+  StartThreads(&threads, &runner);
+  runner.SetExpectedThreadCount(kNumThreads);
+
+  // Release the hounds!
+  EXPECT_TRUE(runner.Run());
+  EXPECT_EQ(1, runner.shared_value());
+}
+
+TEST(GlobalLockTest, Basic) {
+  // Create and start lots of threads.
+  LockRunner<GlobalLock> runner;
+  std::vector<std::unique_ptr<Thread>> threads;
+  StartThreads(&threads, &runner);
+  runner.SetExpectedThreadCount(kNumThreads);
+
+  // Release the hounds!
+  EXPECT_TRUE(runner.Run());
+  EXPECT_EQ(0, runner.shared_value());
+}
+
+TEST(CriticalSectionTest, Basic) {
+  // Create and start lots of threads.
+  LockRunner<CriticalSectionLock> runner;
+  std::vector<std::unique_ptr<Thread>> threads;
+  StartThreads(&threads, &runner);
+  runner.SetExpectedThreadCount(kNumThreads);
+
+  // Release the hounds!
+  EXPECT_TRUE(runner.Run());
+  EXPECT_EQ(0, runner.shared_value());
+}
+
+class PerfTestData {
+ public:
+  PerfTestData(int expected_count, Event* event)
+      : cache_line_barrier_1_(), cache_line_barrier_2_(),
+        expected_count_(expected_count), event_(event) {
+    cache_line_barrier_1_[0]++;  // Avoid 'is not used'.
+    cache_line_barrier_2_[0]++;  // Avoid 'is not used'.
+  }
+  ~PerfTestData() {}
+
+  void AddToCounter(int add) {
+    rtc::CritScope cs(&lock_);
+    my_counter_ += add;
+    if (my_counter_ == expected_count_)
+      event_->Set();
+  }
+
+  int64_t total() const {
+    // Assume that only one thread is running now.
+    return my_counter_;
+  }
+
+ private:
+  uint8_t cache_line_barrier_1_[64];
+  CriticalSection lock_;
+  uint8_t cache_line_barrier_2_[64];
+  int64_t my_counter_ = 0;
+  const int expected_count_;
+  Event* const event_;
+};
+
+class PerfTestThread {
+ public:
+  PerfTestThread() : thread_(&ThreadFunc, this, "CsPerf") {}
+
+  void Start(PerfTestData* data, int repeats, int id) {
+    RTC_DCHECK(!thread_.IsRunning());
+    RTC_DCHECK(!data_);
+    data_ = data;
+    repeats_ = repeats;
+    my_id_ = id;
+    thread_.Start();
+  }
+
+  void Stop() {
+    RTC_DCHECK(thread_.IsRunning());
+    RTC_DCHECK(data_);
+    thread_.Stop();
+    repeats_ = 0;
+    data_ = nullptr;
+    my_id_ = 0;
+  }
+
+ private:
+  static bool ThreadFunc(void* param) {
+    PerfTestThread* me = static_cast<PerfTestThread*>(param);
+    for (int i = 0; i < me->repeats_; ++i)
+      me->data_->AddToCounter(me->my_id_);
+    return false;
+  }
+
+  PlatformThread thread_;
+  PerfTestData* data_ = nullptr;
+  int repeats_ = 0;
+  int my_id_ = 0;
+};
+
+// Comparison of output of this test as tested on a MacBook Pro Retina, 15-inch,
+// Mid 2014, 2,8 GHz Intel Core i7, 16 GB 1600 MHz DDR3,
+// running OS X El Capitan, 10.11.2.
+//
+// Native mutex implementation:
+// Approximate CPU usage:
+//   System: ~16%
+//   User mode: ~1.3%
+//   Idle: ~82%
+// Unit test output:
+// [       OK ] CriticalSectionTest.Performance (234545 ms)
+//
+// Special partially spin lock based implementation:
+// Approximate CPU usage:
+//   System: ~75%
+//   User mode: ~16%
+//   Idle: ~8%
+// Unit test output:
+// [       OK ] CriticalSectionTest.Performance (2107 ms)
+//
+// The test is disabled by default to avoid unecessarily loading the bots.
+TEST(CriticalSectionTest, DISABLED_Performance) {
+  PerfTestThread threads[8];
+  Event event(false, false);
+
+  static const int kThreadRepeats = 10000000;
+  static const int kExpectedCount = kThreadRepeats * arraysize(threads);
+  PerfTestData test_data(kExpectedCount, &event);
+
+  for (auto& t : threads)
+    t.Start(&test_data, kThreadRepeats, 1);
+
+  event.Wait(Event::kForever);
+
+  for (auto& t : threads)
+    t.Stop();
+}
+
+}  // namespace rtc
diff --git a/base/cryptstring.cc b/base/cryptstring.cc
new file mode 100644
index 0000000..4b2a83f
--- /dev/null
+++ b/base/cryptstring.cc
@@ -0,0 +1,75 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/cryptstring.h"
+
+namespace rtc {
+
+size_t EmptyCryptStringImpl::GetLength() const {
+  return 0;
+}
+
+void EmptyCryptStringImpl::CopyTo(char* dest, bool nullterminate) const {
+  if (nullterminate) {
+    *dest = '\0';
+  }
+}
+
+std::string EmptyCryptStringImpl::UrlEncode() const {
+  return "";
+}
+
+CryptStringImpl* EmptyCryptStringImpl::Copy() const {
+  return new EmptyCryptStringImpl();
+}
+
+void EmptyCryptStringImpl::CopyRawTo(std::vector<unsigned char>* dest) const {
+  dest->clear();
+}
+
+CryptString::CryptString() : impl_(new EmptyCryptStringImpl()) {
+}
+
+CryptString::CryptString(const CryptString& other)
+    : impl_(other.impl_->Copy()) {
+}
+
+CryptString::CryptString(const CryptStringImpl& impl) : impl_(impl.Copy()) {
+}
+
+CryptString::~CryptString() = default;
+
+size_t InsecureCryptStringImpl::GetLength() const {
+  return password_.size();
+}
+
+void InsecureCryptStringImpl::CopyTo(char* dest, bool nullterminate) const {
+  memcpy(dest, password_.data(), password_.size());
+  if (nullterminate)
+    dest[password_.size()] = 0;
+}
+
+std::string InsecureCryptStringImpl::UrlEncode() const {
+  return password_;
+}
+
+CryptStringImpl* InsecureCryptStringImpl::Copy() const {
+  InsecureCryptStringImpl* copy = new InsecureCryptStringImpl;
+  copy->password() = password_;
+  return copy;
+}
+
+void InsecureCryptStringImpl::CopyRawTo(
+    std::vector<unsigned char>* dest) const {
+  dest->resize(password_.size());
+  memcpy(&dest->front(), password_.data(), password_.size());
+}
+
+};  // namespace rtc
diff --git a/base/cryptstring.h b/base/cryptstring.h
index 1a474b4..e1ee309 100644
--- a/base/cryptstring.h
+++ b/base/cryptstring.h
@@ -8,12 +8,160 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_CRYPTSTRING_H_
-#define WEBRTC_BASE_CRYPTSTRING_H_
+#ifndef _WEBRTC_BASE_CRYPTSTRING_H_
+#define _WEBRTC_BASE_CRYPTSTRING_H_
+
+#include <string.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace rtc {
+
+class CryptStringImpl {
+public:
+  virtual ~CryptStringImpl() {}
+  virtual size_t GetLength() const = 0;
+  virtual void CopyTo(char * dest, bool nullterminate) const = 0;
+  virtual std::string UrlEncode() const = 0;
+  virtual CryptStringImpl * Copy() const = 0;
+  virtual void CopyRawTo(std::vector<unsigned char> * dest) const = 0;
+};
+
+class EmptyCryptStringImpl : public CryptStringImpl {
+public:
+  ~EmptyCryptStringImpl() override {}
+  size_t GetLength() const override;
+  void CopyTo(char* dest, bool nullterminate) const override;
+  std::string UrlEncode() const override;
+  CryptStringImpl* Copy() const override;
+  void CopyRawTo(std::vector<unsigned char>* dest) const override;
+};
+
+class CryptString {
+ public:
+  CryptString();
+  size_t GetLength() const { return impl_->GetLength(); }
+  void CopyTo(char * dest, bool nullterminate) const { impl_->CopyTo(dest, nullterminate); }
+  CryptString(const CryptString& other);
+  explicit CryptString(const CryptStringImpl& impl);
+  ~CryptString();
+  CryptString & operator=(const CryptString & other) {
+    if (this != &other) {
+      impl_.reset(other.impl_->Copy());
+    }
+    return *this;
+  }
+  void Clear() { impl_.reset(new EmptyCryptStringImpl()); }
+  std::string UrlEncode() const { return impl_->UrlEncode(); }
+  void CopyRawTo(std::vector<unsigned char> * dest) const {
+    return impl_->CopyRawTo(dest);
+  }
+
+ private:
+  std::unique_ptr<const CryptStringImpl> impl_;
+};
 
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/cryptstring.h"
+// Used for constructing strings where a password is involved and we
+// need to ensure that we zero memory afterwards
+class FormatCryptString {
+public:
+  FormatCryptString() {
+    storage_ = new char[32];
+    capacity_ = 32;
+    length_ = 0;
+    storage_[0] = 0;
+  }
+  
+  void Append(const std::string & text) {
+    Append(text.data(), text.length());
+  }
 
-#endif  // WEBRTC_BASE_CRYPTSTRING_H_
+  void Append(const char * data, size_t length) {
+    EnsureStorage(length_ + length + 1);
+    memcpy(storage_ + length_, data, length);
+    length_ += length;
+    storage_[length_] = '\0';
+  }
+  
+  void Append(const CryptString * password) {
+    size_t len = password->GetLength();
+    EnsureStorage(length_ + len + 1);
+    password->CopyTo(storage_ + length_, true);
+    length_ += len;
+  }
+
+  size_t GetLength() {
+    return length_;
+  }
+
+  const char * GetData() {
+    return storage_;
+  }
+
+
+  // Ensures storage of at least n bytes
+  void EnsureStorage(size_t n) {
+    if (capacity_ >= n) {
+      return;
+    }
+
+    size_t old_capacity = capacity_;
+    char * old_storage = storage_;
+
+    for (;;) {
+      capacity_ *= 2;
+      if (capacity_ >= n)
+        break;
+    }
+
+    storage_ = new char[capacity_];
+
+    if (old_capacity) {
+      memcpy(storage_, old_storage, length_);
+    
+      // zero memory in a way that an optimizer won't optimize it out
+      old_storage[0] = 0;
+      for (size_t i = 1; i < old_capacity; i++) {
+        old_storage[i] = old_storage[i - 1];
+      }
+      delete[] old_storage;
+    }
+  }  
+
+  ~FormatCryptString() {
+    if (capacity_) {
+      storage_[0] = 0;
+      for (size_t i = 1; i < capacity_; i++) {
+        storage_[i] = storage_[i - 1];
+      }
+    }
+    delete[] storage_;
+  }
+private:
+  char * storage_;
+  size_t capacity_;
+  size_t length_;
+};
+
+class InsecureCryptStringImpl : public CryptStringImpl {
+ public:
+  std::string& password() { return password_; }
+  const std::string& password() const { return password_; }
+
+  ~InsecureCryptStringImpl() override = default;
+  size_t GetLength() const override;
+  void CopyTo(char* dest, bool nullterminate) const override;
+  std::string UrlEncode() const override;
+  CryptStringImpl* Copy() const override;
+  void CopyRawTo(std::vector<unsigned char>* dest) const override;
+
+ private:
+  std::string password_;
+};
+
+}
+
+#endif  // _WEBRTC_BASE_CRYPTSTRING_H_
diff --git a/base/deprecation.h b/base/deprecation.h
index d6c5124..ce950f9 100644
--- a/base/deprecation.h
+++ b/base/deprecation.h
@@ -11,9 +11,35 @@
 #ifndef WEBRTC_BASE_DEPRECATION_H_
 #define WEBRTC_BASE_DEPRECATION_H_
 
-
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/deprecation.h"
+// Annotate the declarations of deprecated functions with this to cause a
+// compiler warning when they're used. Like so:
+//
+//   RTC_DEPRECATED std::pony PonyPlz(const std::pony_spec& ps);
+//
+// NOTE 1: The annotation goes on the declaration in the .h file, not the
+// definition in the .cc file!
+//
+// NOTE 2: In order to keep unit testing the deprecated function without
+// getting warnings, do something like this:
+//
+//   std::pony DEPRECATED_PonyPlz(const std::pony_spec& ps);
+//   RTC_DEPRECATED inline std::pony PonyPlz(const std::pony_spec& ps) {
+//     return DEPRECATED_PonyPlz(ps);
+//   }
+//
+// In other words, rename the existing function, and provide an inline wrapper
+// using the original name that calls it. That way, callers who are willing to
+// call it using the DEPRECATED_-prefixed name don't get the warning.
+//
+// TODO(kwiberg): Remove this when we can use [[deprecated]] from C++14.
+#if defined(_MSC_VER)
+// Note: Deprecation warnings seem to fail to trigger on Windows
+// (https://bugs.chromium.org/p/webrtc/issues/detail?id=5368).
+#define RTC_DEPRECATED __declspec(deprecated)
+#elif defined(__GNUC__)
+#define RTC_DEPRECATED __attribute__ ((__deprecated__))
+#else
+#define RTC_DEPRECATED
+#endif
 
 #endif  // WEBRTC_BASE_DEPRECATION_H_
diff --git a/base/dscp.h b/base/dscp.h
index 1cf2756..970ff93 100644
--- a/base/dscp.h
+++ b/base/dscp.h
@@ -11,9 +11,35 @@
 #ifndef WEBRTC_BASE_DSCP_H_
 #define WEBRTC_BASE_DSCP_H_
 
+namespace rtc {
+// Differentiated Services Code Point.
+// See http://tools.ietf.org/html/rfc2474 for details.
+enum DiffServCodePoint {
+  DSCP_NO_CHANGE = -1,
+  DSCP_DEFAULT = 0,  // Same as DSCP_CS0
+  DSCP_CS0  = 0,   // The default
+  DSCP_CS1  = 8,   // Bulk/background traffic
+  DSCP_AF11 = 10,
+  DSCP_AF12 = 12,
+  DSCP_AF13 = 14,
+  DSCP_CS2  = 16,
+  DSCP_AF21 = 18,
+  DSCP_AF22 = 20,
+  DSCP_AF23 = 22,
+  DSCP_CS3  = 24,
+  DSCP_AF31 = 26,
+  DSCP_AF32 = 28,
+  DSCP_AF33 = 30,
+  DSCP_CS4  = 32,
+  DSCP_AF41 = 34,  // Video
+  DSCP_AF42 = 36,  // Video
+  DSCP_AF43 = 38,  // Video
+  DSCP_CS5  = 40,  // Video
+  DSCP_EF   = 46,  // Voice
+  DSCP_CS6  = 48,  // Voice
+  DSCP_CS7  = 56,  // Control messages
+};
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/dscp.h"
+}  // namespace rtc
 
-#endif  // WEBRTC_BASE_DSCP_H_
+ #endif  // WEBRTC_BASE_DSCP_H_
diff --git a/base/event.cc b/base/event.cc
new file mode 100644
index 0000000..27506b3
--- /dev/null
+++ b/base/event.cc
@@ -0,0 +1,136 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/event.h"
+
+#if defined(WEBRTC_WIN)
+#include <windows.h>
+#elif defined(WEBRTC_POSIX)
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+#else
+#error "Must define either WEBRTC_WIN or WEBRTC_POSIX."
+#endif
+
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+#if defined(WEBRTC_WIN)
+
+Event::Event(bool manual_reset, bool initially_signaled) {
+  event_handle_ = ::CreateEvent(nullptr,  // Security attributes.
+                                manual_reset, initially_signaled,
+                                nullptr);  // Name.
+  RTC_CHECK(event_handle_);
+}
+
+Event::~Event() {
+  CloseHandle(event_handle_);
+}
+
+void Event::Set() {
+  SetEvent(event_handle_);
+}
+
+void Event::Reset() {
+  ResetEvent(event_handle_);
+}
+
+bool Event::Wait(int milliseconds) {
+  DWORD ms = (milliseconds == kForever) ? INFINITE : milliseconds;
+  return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0);
+}
+
+#elif defined(WEBRTC_POSIX)
+
+Event::Event(bool manual_reset, bool initially_signaled)
+    : is_manual_reset_(manual_reset),
+      event_status_(initially_signaled) {
+  RTC_CHECK(pthread_mutex_init(&event_mutex_, nullptr) == 0);
+  RTC_CHECK(pthread_cond_init(&event_cond_, nullptr) == 0);
+}
+
+Event::~Event() {
+  pthread_mutex_destroy(&event_mutex_);
+  pthread_cond_destroy(&event_cond_);
+}
+
+void Event::Set() {
+  pthread_mutex_lock(&event_mutex_);
+  event_status_ = true;
+  pthread_cond_broadcast(&event_cond_);
+  pthread_mutex_unlock(&event_mutex_);
+}
+
+void Event::Reset() {
+  pthread_mutex_lock(&event_mutex_);
+  event_status_ = false;
+  pthread_mutex_unlock(&event_mutex_);
+}
+
+bool Event::Wait(int milliseconds) {
+  int error = 0;
+
+  struct timespec ts;
+  if (milliseconds != kForever) {
+    // Converting from seconds and microseconds (1e-6) plus
+    // milliseconds (1e-3) to seconds and nanoseconds (1e-9).
+
+#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
+    // Use relative time version, which tends to be more efficient for
+    // pthread implementations where provided (like on Android).
+    ts.tv_sec = milliseconds / 1000;
+    ts.tv_nsec = (milliseconds % 1000) * 1000000;
+#else
+    struct timeval tv;
+    gettimeofday(&tv, nullptr);
+
+    ts.tv_sec = tv.tv_sec + (milliseconds / 1000);
+    ts.tv_nsec = tv.tv_usec * 1000 + (milliseconds % 1000) * 1000000;
+
+    // Handle overflow.
+    if (ts.tv_nsec >= 1000000000) {
+      ts.tv_sec++;
+      ts.tv_nsec -= 1000000000;
+    }
+#endif
+  }
+
+  pthread_mutex_lock(&event_mutex_);
+  if (milliseconds != kForever) {
+    while (!event_status_ && error == 0) {
+#ifdef HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
+      error = pthread_cond_timedwait_relative_np(
+          &event_cond_, &event_mutex_, &ts);
+#else
+      error = pthread_cond_timedwait(&event_cond_, &event_mutex_, &ts);
+#endif
+    }
+  } else {
+    while (!event_status_ && error == 0)
+      error = pthread_cond_wait(&event_cond_, &event_mutex_);
+  }
+
+  // NOTE(liulk): Exactly one thread will auto-reset this event. All
+  // the other threads will think it's unsignaled.  This seems to be
+  // consistent with auto-reset events in WEBRTC_WIN
+  if (error == 0 && !is_manual_reset_)
+    event_status_ = false;
+
+  pthread_mutex_unlock(&event_mutex_);
+
+  return (error == 0);
+}
+
+#endif
+
+}  // namespace rtc
diff --git a/base/event.h b/base/event.h
index 28ff731..d4b5872 100644
--- a/base/event.h
+++ b/base/event.h
@@ -8,12 +8,47 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_EVENT_H_
-#define WEBRTC_BASE_EVENT_H_
+#ifndef WEBRTC_BASE_EVENT_H__
+#define WEBRTC_BASE_EVENT_H__
 
+#include "webrtc/base/constructormagic.h"
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"  // NOLINT: consider this a system header.
+#elif defined(WEBRTC_POSIX)
+#include <pthread.h>
+#else
+#error "Must define either WEBRTC_WIN or WEBRTC_POSIX."
+#endif
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/event.h"
+namespace rtc {
 
-#endif  // WEBRTC_BASE_EVENT_H_
+class Event {
+ public:
+  static const int kForever = -1;
+
+  Event(bool manual_reset, bool initially_signaled);
+  ~Event();
+
+  void Set();
+  void Reset();
+
+  // Wait for the event to become signaled, for the specified number of
+  // |milliseconds|.  To wait indefinetly, pass kForever.
+  bool Wait(int milliseconds);
+
+ private:
+#if defined(WEBRTC_WIN)
+  HANDLE event_handle_;
+#elif defined(WEBRTC_POSIX)
+  pthread_mutex_t event_mutex_;
+  pthread_cond_t event_cond_;
+  const bool is_manual_reset_;
+  bool event_status_;
+#endif
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Event);
+};
+
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_EVENT_H__
diff --git a/base/event_tracer.cc b/base/event_tracer.cc
new file mode 100644
index 0000000..cb7554a
--- /dev/null
+++ b/base/event_tracer.cc
@@ -0,0 +1,407 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/base/event_tracer.h"
+
+#include <inttypes.h>
+
+#include <string>
+#include <vector>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/base/trace_event.h"
+
+// This is a guesstimate that should be enough in most cases.
+static const size_t kEventLoggerArgsStrBufferInitialSize = 256;
+static const size_t kTraceArgBufferLength = 32;
+
+namespace webrtc {
+
+namespace {
+
+GetCategoryEnabledPtr g_get_category_enabled_ptr = nullptr;
+AddTraceEventPtr g_add_trace_event_ptr = nullptr;
+
+}  // namespace
+
+void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr,
+                      AddTraceEventPtr add_trace_event_ptr) {
+  g_get_category_enabled_ptr = get_category_enabled_ptr;
+  g_add_trace_event_ptr = add_trace_event_ptr;
+}
+
+const unsigned char* EventTracer::GetCategoryEnabled(const char* name) {
+  if (g_get_category_enabled_ptr)
+    return g_get_category_enabled_ptr(name);
+
+  // A string with null terminator means category is disabled.
+  return reinterpret_cast<const unsigned char*>("\0");
+}
+
+// Arguments to this function (phase, etc.) are as defined in
+// webrtc/base/trace_event.h.
+void EventTracer::AddTraceEvent(char phase,
+                                const unsigned char* category_enabled,
+                                const char* name,
+                                unsigned long long id,
+                                int num_args,
+                                const char** arg_names,
+                                const unsigned char* arg_types,
+                                const unsigned long long* arg_values,
+                                unsigned char flags) {
+  if (g_add_trace_event_ptr) {
+    g_add_trace_event_ptr(phase,
+                          category_enabled,
+                          name,
+                          id,
+                          num_args,
+                          arg_names,
+                          arg_types,
+                          arg_values,
+                          flags);
+  }
+}
+
+}  // namespace webrtc
+
+namespace rtc {
+namespace tracing {
+namespace {
+
+static void EventTracingThreadFunc(void* params);
+
+// Atomic-int fast path for avoiding logging when disabled.
+static volatile int g_event_logging_active = 0;
+
+// TODO(pbos): Log metadata for all threads, etc.
+class EventLogger final {
+ public:
+  EventLogger()
+      : logging_thread_(EventTracingThreadFunc,
+                        this,
+                        "EventTracingThread",
+                        kLowPriority),
+        shutdown_event_(false, false) {}
+  ~EventLogger() { RTC_DCHECK(thread_checker_.CalledOnValidThread()); }
+
+  void AddTraceEvent(const char* name,
+                     const unsigned char* category_enabled,
+                     char phase,
+                     int num_args,
+                     const char** arg_names,
+                     const unsigned char* arg_types,
+                     const unsigned long long* arg_values,
+                     uint64_t timestamp,
+                     int pid,
+                     rtc::PlatformThreadId thread_id) {
+    std::vector<TraceArg> args(num_args);
+    for (int i = 0; i < num_args; ++i) {
+      TraceArg& arg = args[i];
+      arg.name = arg_names[i];
+      arg.type = arg_types[i];
+      arg.value.as_uint = arg_values[i];
+
+      // Value is a pointer to a temporary string, so we have to make a copy.
+      if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
+        // Space for the string and for the terminating null character.
+        size_t str_length = strlen(arg.value.as_string) + 1;
+        char* str_copy = new char[str_length];
+        memcpy(str_copy, arg.value.as_string, str_length);
+        arg.value.as_string = str_copy;
+      }
+    }
+    rtc::CritScope lock(&crit_);
+    trace_events_.push_back(
+        {name, category_enabled, phase, args, timestamp, 1, thread_id});
+  }
+
+// The TraceEvent format is documented here:
+// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
+  void Log() {
+    RTC_DCHECK(output_file_);
+    static const int kLoggingIntervalMs = 100;
+    fprintf(output_file_, "{ \"traceEvents\": [\n");
+    bool has_logged_event = false;
+    while (true) {
+      bool shutting_down = shutdown_event_.Wait(kLoggingIntervalMs);
+      std::vector<TraceEvent> events;
+      {
+        rtc::CritScope lock(&crit_);
+        trace_events_.swap(events);
+      }
+      std::string args_str;
+      args_str.reserve(kEventLoggerArgsStrBufferInitialSize);
+      for (TraceEvent& e : events) {
+        args_str.clear();
+        if (!e.args.empty()) {
+          args_str += ", \"args\": {";
+          bool is_first_argument = true;
+          for (TraceArg& arg : e.args) {
+            if (!is_first_argument)
+              args_str += ",";
+            is_first_argument = false;
+            args_str += " \"";
+            args_str += arg.name;
+            args_str += "\": ";
+            args_str += TraceArgValueAsString(arg);
+
+            // Delete our copy of the string.
+            if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
+              delete[] arg.value.as_string;
+              arg.value.as_string = nullptr;
+            }
+          }
+          args_str += " }";
+        }
+        fprintf(output_file_,
+                "%s{ \"name\": \"%s\""
+                ", \"cat\": \"%s\""
+                ", \"ph\": \"%c\""
+                ", \"ts\": %" PRIu64
+                ", \"pid\": %d"
+#if defined(WEBRTC_WIN)
+                ", \"tid\": %lu"
+#else
+                ", \"tid\": %d"
+#endif  // defined(WEBRTC_WIN)
+                "%s"
+                "}\n",
+                has_logged_event ? "," : " ", e.name, e.category_enabled,
+                e.phase, e.timestamp, e.pid, e.tid, args_str.c_str());
+        has_logged_event = true;
+      }
+      if (shutting_down)
+        break;
+    }
+    fprintf(output_file_, "]}\n");
+    if (output_file_owned_)
+      fclose(output_file_);
+    output_file_ = nullptr;
+  }
+
+  void Start(FILE* file, bool owned) {
+    RTC_DCHECK(thread_checker_.CalledOnValidThread());
+    RTC_DCHECK(file);
+    RTC_DCHECK(!output_file_);
+    output_file_ = file;
+    output_file_owned_ = owned;
+    {
+      rtc::CritScope lock(&crit_);
+      // Since the atomic fast-path for adding events to the queue can be
+      // bypassed while the logging thread is shutting down there may be some
+      // stale events in the queue, hence the vector needs to be cleared to not
+      // log events from a previous logging session (which may be days old).
+      trace_events_.clear();
+    }
+    // Enable event logging (fast-path). This should be disabled since starting
+    // shouldn't be done twice.
+    RTC_CHECK_EQ(0,
+                 rtc::AtomicOps::CompareAndSwap(&g_event_logging_active, 0, 1));
+
+    // Finally start, everything should be set up now.
+    logging_thread_.Start();
+    TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Start");
+  }
+
+  void Stop() {
+    RTC_DCHECK(thread_checker_.CalledOnValidThread());
+    TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Stop");
+    // Try to stop. Abort if we're not currently logging.
+    if (rtc::AtomicOps::CompareAndSwap(&g_event_logging_active, 1, 0) == 0)
+      return;
+
+    // Wake up logging thread to finish writing.
+    shutdown_event_.Set();
+    // Join the logging thread.
+    logging_thread_.Stop();
+  }
+
+ private:
+  struct TraceArg {
+    const char* name;
+    unsigned char type;
+    // Copied from webrtc/base/trace_event.h TraceValueUnion.
+    union TraceArgValue {
+      bool as_bool;
+      unsigned long long as_uint;
+      long long as_int;
+      double as_double;
+      const void* as_pointer;
+      const char* as_string;
+    } value;
+
+    // Assert that the size of the union is equal to the size of the as_uint
+    // field since we are assigning to arbitrary types using it.
+    static_assert(sizeof(TraceArgValue) == sizeof(unsigned long long),
+                  "Size of TraceArg value union is not equal to the size of "
+                  "the uint field of that union.");
+  };
+
+  struct TraceEvent {
+    const char* name;
+    const unsigned char* category_enabled;
+    char phase;
+    std::vector<TraceArg> args;
+    uint64_t timestamp;
+    int pid;
+    rtc::PlatformThreadId tid;
+  };
+
+  static std::string TraceArgValueAsString(TraceArg arg) {
+    std::string output;
+
+    if (arg.type == TRACE_VALUE_TYPE_STRING ||
+        arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
+      // Space for every character to be an espaced character + two for
+      // quatation marks.
+      output.reserve(strlen(arg.value.as_string) * 2 + 2);
+      output += '\"';
+      const char* c = arg.value.as_string;
+      do {
+        if (*c == '"' || *c == '\\') {
+          output += '\\';
+          output += *c;
+        } else {
+          output += *c;
+        }
+      } while (*++c);
+      output += '\"';
+    } else {
+      output.resize(kTraceArgBufferLength);
+      size_t print_length = 0;
+      switch (arg.type) {
+        case TRACE_VALUE_TYPE_BOOL:
+          if (arg.value.as_bool) {
+            strcpy(&output[0], "true");
+            print_length = 4;
+          } else {
+            strcpy(&output[0], "false");
+            print_length = 5;
+          }
+          break;
+        case TRACE_VALUE_TYPE_UINT:
+          print_length = sprintfn(&output[0], kTraceArgBufferLength, "%llu",
+                                  arg.value.as_uint);
+          break;
+        case TRACE_VALUE_TYPE_INT:
+          print_length = sprintfn(&output[0], kTraceArgBufferLength, "%lld",
+                                  arg.value.as_int);
+          break;
+        case TRACE_VALUE_TYPE_DOUBLE:
+          print_length = sprintfn(&output[0], kTraceArgBufferLength, "%f",
+                                  arg.value.as_double);
+          break;
+        case TRACE_VALUE_TYPE_POINTER:
+          print_length = sprintfn(&output[0], kTraceArgBufferLength, "\"%p\"",
+                                  arg.value.as_pointer);
+          break;
+      }
+      size_t output_length = print_length < kTraceArgBufferLength
+                                 ? print_length
+                                 : kTraceArgBufferLength - 1;
+      // This will hopefully be very close to nop. On most implementations, it
+      // just writes null byte and sets the length field of the string.
+      output.resize(output_length);
+    }
+
+    return output;
+  }
+
+  rtc::CriticalSection crit_;
+  std::vector<TraceEvent> trace_events_ GUARDED_BY(crit_);
+  rtc::PlatformThread logging_thread_;
+  rtc::Event shutdown_event_;
+  rtc::ThreadChecker thread_checker_;
+  FILE* output_file_ = nullptr;
+  bool output_file_owned_ = false;
+};
+
+static void EventTracingThreadFunc(void* params) {
+  static_cast<EventLogger*>(params)->Log();
+}
+
+static EventLogger* volatile g_event_logger = nullptr;
+static const char* const kDisabledTracePrefix = TRACE_DISABLED_BY_DEFAULT("");
+const unsigned char* InternalGetCategoryEnabled(const char* name) {
+  const char* prefix_ptr = &kDisabledTracePrefix[0];
+  const char* name_ptr = name;
+  // Check whether name contains the default-disabled prefix.
+  while (*prefix_ptr == *name_ptr && *prefix_ptr != '\0') {
+    ++prefix_ptr;
+    ++name_ptr;
+  }
+  return reinterpret_cast<const unsigned char*>(*prefix_ptr == '\0' ? ""
+                                                                    : name);
+}
+
+void InternalAddTraceEvent(char phase,
+                           const unsigned char* category_enabled,
+                           const char* name,
+                           unsigned long long id,
+                           int num_args,
+                           const char** arg_names,
+                           const unsigned char* arg_types,
+                           const unsigned long long* arg_values,
+                           unsigned char flags) {
+  // Fast path for when event tracing is inactive.
+  if (rtc::AtomicOps::AcquireLoad(&g_event_logging_active) == 0)
+    return;
+
+  g_event_logger->AddTraceEvent(name, category_enabled, phase, num_args,
+                                arg_names, arg_types, arg_values,
+                                rtc::TimeMicros(), 1, rtc::CurrentThreadId());
+}
+
+}  // namespace
+
+void SetupInternalTracer() {
+  RTC_CHECK(rtc::AtomicOps::CompareAndSwapPtr(
+                &g_event_logger, static_cast<EventLogger*>(nullptr),
+                new EventLogger()) == nullptr);
+  webrtc::SetupEventTracer(InternalGetCategoryEnabled, InternalAddTraceEvent);
+}
+
+void StartInternalCaptureToFile(FILE* file) {
+  g_event_logger->Start(file, false);
+}
+
+bool StartInternalCapture(const char* filename) {
+  FILE* file = fopen(filename, "w");
+  if (!file) {
+    LOG(LS_ERROR) << "Failed to open trace file '" << filename
+                  << "' for writing.";
+    return false;
+  }
+  g_event_logger->Start(file, true);
+  return true;
+}
+
+void StopInternalCapture() {
+  g_event_logger->Stop();
+}
+
+void ShutdownInternalTracer() {
+  StopInternalCapture();
+  EventLogger* old_logger = rtc::AtomicOps::AcquireLoadPtr(&g_event_logger);
+  RTC_DCHECK(old_logger);
+  RTC_CHECK(rtc::AtomicOps::CompareAndSwapPtr(
+                &g_event_logger, old_logger,
+                static_cast<EventLogger*>(nullptr)) == old_logger);
+  delete old_logger;
+  webrtc::SetupEventTracer(nullptr, nullptr);
+}
+
+}  // namespace tracing
+}  // namespace rtc
diff --git a/base/event_tracer.h b/base/event_tracer.h
index b6da14a..51c8cfd 100644
--- a/base/event_tracer.h
+++ b/base/event_tracer.h
@@ -26,9 +26,60 @@
 #ifndef WEBRTC_BASE_EVENT_TRACER_H_
 #define WEBRTC_BASE_EVENT_TRACER_H_
 
+#include <stdio.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/event_tracer.h"
+namespace webrtc {
+
+typedef const unsigned char* (*GetCategoryEnabledPtr)(const char* name);
+typedef void (*AddTraceEventPtr)(char phase,
+                                 const unsigned char* category_enabled,
+                                 const char* name,
+                                 unsigned long long id,
+                                 int num_args,
+                                 const char** arg_names,
+                                 const unsigned char* arg_types,
+                                 const unsigned long long* arg_values,
+                                 unsigned char flags);
+
+// User of WebRTC can call this method to setup event tracing.
+//
+// This method must be called before any WebRTC methods. Functions
+// provided should be thread-safe.
+void SetupEventTracer(
+    GetCategoryEnabledPtr get_category_enabled_ptr,
+    AddTraceEventPtr add_trace_event_ptr);
+
+// This class defines interface for the event tracing system to call
+// internally. Do not call these methods directly.
+class EventTracer {
+ public:
+  static const unsigned char* GetCategoryEnabled(
+      const char* name);
+
+  static void AddTraceEvent(
+      char phase,
+      const unsigned char* category_enabled,
+      const char* name,
+      unsigned long long id,
+      int num_args,
+      const char** arg_names,
+      const unsigned char* arg_types,
+      const unsigned long long* arg_values,
+      unsigned char flags);
+};
+
+}  // namespace webrtc
+
+namespace rtc {
+namespace tracing {
+// Set up internal event tracer.
+void SetupInternalTracer();
+bool StartInternalCapture(const char* filename);
+void StartInternalCaptureToFile(FILE* file);
+void StopInternalCapture();
+// Make sure we run this, this will tear down the internal tracing.
+void ShutdownInternalTracer();
+}  // namespace tracing
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_EVENT_TRACER_H_
diff --git a/base/event_tracer_unittest.cc b/base/event_tracer_unittest.cc
new file mode 100644
index 0000000..a16c0ef
--- /dev/null
+++ b/base/event_tracer_unittest.cc
@@ -0,0 +1,82 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/event_tracer.h"
+
+#include "webrtc/base/trace_event.h"
+#include "webrtc/system_wrappers/include/static_instance.h"
+#include "webrtc/test/gtest.h"
+
+namespace {
+
+class TestStatistics {
+ public:
+  TestStatistics() : events_logged_(0) {
+  }
+
+  void Reset() {
+    events_logged_ = 0;
+  }
+
+  void Increment() {
+    ++events_logged_;
+  }
+
+  int Count() const { return events_logged_; }
+
+  static TestStatistics* Get() {
+    static TestStatistics* test_stats = nullptr;
+    if (!test_stats)
+      test_stats = new TestStatistics();
+    return test_stats;
+  }
+
+ private:
+  int events_logged_;
+};
+
+static const unsigned char* GetCategoryEnabledHandler(const char* name) {
+  return reinterpret_cast<const unsigned char*>("test");
+}
+
+static void AddTraceEventHandler(char phase,
+                                 const unsigned char* category_enabled,
+                                 const char* name,
+                                 unsigned long long id,
+                                 int num_args,
+                                 const char** arg_names,
+                                 const unsigned char* arg_types,
+                                 const unsigned long long* arg_values,
+                                 unsigned char flags) {
+  TestStatistics::Get()->Increment();
+}
+
+}  // namespace
+
+namespace webrtc {
+
+TEST(EventTracerTest, EventTracerDisabled) {
+  {
+    TRACE_EVENT0("test", "EventTracerDisabled");
+  }
+  EXPECT_FALSE(TestStatistics::Get()->Count());
+  TestStatistics::Get()->Reset();
+}
+
+TEST(EventTracerTest, ScopedTraceEvent) {
+  SetupEventTracer(&GetCategoryEnabledHandler, &AddTraceEventHandler);
+  {
+    TRACE_EVENT0("test", "ScopedTraceEvent");
+  }
+  EXPECT_EQ(2, TestStatistics::Get()->Count());
+  TestStatistics::Get()->Reset();
+}
+
+}  // namespace webrtc
diff --git a/base/event_unittest.cc b/base/event_unittest.cc
new file mode 100644
index 0000000..996ad2f
--- /dev/null
+++ b/base/event_unittest.cc
@@ -0,0 +1,42 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/event.h"
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+TEST(EventTest, InitiallySignaled) {
+  Event event(false, true);
+  ASSERT_TRUE(event.Wait(0));
+}
+
+TEST(EventTest, ManualReset) {
+  Event event(true, false);
+  ASSERT_FALSE(event.Wait(0));
+
+  event.Set();
+  ASSERT_TRUE(event.Wait(0));
+  ASSERT_TRUE(event.Wait(0));
+
+  event.Reset();
+  ASSERT_FALSE(event.Wait(0));
+}
+
+TEST(EventTest, AutoReset) {
+  Event event(false, false);
+  ASSERT_FALSE(event.Wait(0));
+
+  event.Set();
+  ASSERT_TRUE(event.Wait(0));
+  ASSERT_FALSE(event.Wait(0));
+}
+
+}  // namespace rtc
diff --git a/base/fakeclock.cc b/base/fakeclock.cc
new file mode 100644
index 0000000..d95408f
--- /dev/null
+++ b/base/fakeclock.cc
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/fakeclock.h"
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/messagequeue.h"
+
+namespace rtc {
+
+int64_t FakeClock::TimeNanos() const {
+  CritScope cs(&lock_);
+  return time_;
+}
+
+void FakeClock::SetTimeNanos(int64_t nanos) {
+  {
+    CritScope cs(&lock_);
+    RTC_DCHECK(nanos >= time_);
+    time_ = nanos;
+  }
+  // If message queues are waiting in a socket select() with a timeout provided
+  // by the OS, they should wake up and dispatch all messages that are ready.
+  MessageQueueManager::ProcessAllMessageQueues();
+}
+
+void FakeClock::AdvanceTime(TimeDelta delta) {
+  {
+    CritScope cs(&lock_);
+    time_ += delta.ToNanoseconds();
+  }
+  MessageQueueManager::ProcessAllMessageQueues();
+}
+
+ScopedFakeClock::ScopedFakeClock() {
+  prev_clock_ = SetClockForTesting(this);
+}
+
+ScopedFakeClock::~ScopedFakeClock() {
+  SetClockForTesting(prev_clock_);
+}
+
+}  // namespace rtc
diff --git a/base/fakeclock.h b/base/fakeclock.h
index 22d640d..fcdfc0b 100644
--- a/base/fakeclock.h
+++ b/base/fakeclock.h
@@ -11,9 +11,61 @@
 #ifndef WEBRTC_BASE_FAKECLOCK_H_
 #define WEBRTC_BASE_FAKECLOCK_H_
 
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/timedelta.h"
+#include "webrtc/base/timeutils.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/fakeclock.h"
+namespace rtc {
+
+// Fake clock for use with unit tests, which does not tick on its own.
+// Starts at time 0.
+//
+// TODO(deadbeef): Unify with webrtc::SimulatedClock.
+class FakeClock : public ClockInterface {
+ public:
+  ~FakeClock() override {}
+
+  // ClockInterface implementation.
+  int64_t TimeNanos() const override;
+
+  // Methods that can be used by the test to control the time.
+
+  // Should only be used to set a time in the future.
+  void SetTimeNanos(int64_t nanos);
+  void SetTimeMicros(int64_t micros) {
+    SetTimeNanos(kNumNanosecsPerMicrosec * micros);
+  }
+
+  void AdvanceTime(TimeDelta delta);
+  void AdvanceTimeMicros(int64_t micros) {
+    AdvanceTime(rtc::TimeDelta::FromMicroseconds(micros));
+  }
+ private:
+  CriticalSection lock_;
+  int64_t time_ GUARDED_BY(lock_) = 0;
+};
+
+// Helper class that sets itself as the global clock in its constructor and
+// unsets it in its destructor.
+class ScopedFakeClock : public FakeClock {
+ public:
+  ScopedFakeClock();
+  ~ScopedFakeClock() override;
+
+ private:
+  ClockInterface* prev_clock_;
+};
+
+// Helper class to "undo" the fake clock temporarily.
+class ScopedRealClock {
+ public:
+  ScopedRealClock();
+  ~ScopedRealClock();
+
+ private:
+  ClockInterface* prev_clock_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_FAKECLOCK_H_
diff --git a/base/fakenetwork.h b/base/fakenetwork.h
index c2c8e6d..108e738 100644
--- a/base/fakenetwork.h
+++ b/base/fakenetwork.h
@@ -11,9 +11,119 @@
 #ifndef WEBRTC_BASE_FAKENETWORK_H_
 #define WEBRTC_BASE_FAKENETWORK_H_
 
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/fakenetwork.h"
+#include "webrtc/base/network.h"
+#include "webrtc/base/messagehandler.h"
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+const int kFakeIPv4NetworkPrefixLength = 24;
+const int kFakeIPv6NetworkPrefixLength = 64;
+
+// Fake network manager that allows us to manually specify the IPs to use.
+class FakeNetworkManager : public NetworkManagerBase,
+                           public MessageHandler {
+ public:
+  FakeNetworkManager() {}
+
+  typedef std::vector<std::pair<SocketAddress, AdapterType>> IfaceList;
+
+  void AddInterface(const SocketAddress& iface) {
+    // Ensure a unique name for the interface if its name is not given.
+    AddInterface(iface, "test" + rtc::ToString(next_index_++));
+  }
+
+  void AddInterface(const SocketAddress& iface, const std::string& if_name) {
+    AddInterface(iface, if_name, ADAPTER_TYPE_UNKNOWN);
+  }
+
+  void AddInterface(const SocketAddress& iface,
+                    const std::string& if_name,
+                    AdapterType type) {
+    SocketAddress address(if_name, 0);
+    address.SetResolvedIP(iface.ipaddr());
+    ifaces_.push_back(std::make_pair(address, type));
+    DoUpdateNetworks();
+  }
+
+  void RemoveInterface(const SocketAddress& iface) {
+    for (IfaceList::iterator it = ifaces_.begin();
+         it != ifaces_.end(); ++it) {
+      if (it->first.EqualIPs(iface)) {
+        ifaces_.erase(it);
+        break;
+      }
+    }
+    DoUpdateNetworks();
+  }
+
+  virtual void StartUpdating() {
+    ++start_count_;
+    if (start_count_ == 1) {
+      sent_first_update_ = false;
+      rtc::Thread::Current()->Post(RTC_FROM_HERE, this);
+    } else {
+      if (sent_first_update_) {
+        SignalNetworksChanged();
+      }
+    }
+  }
+
+  virtual void StopUpdating() { --start_count_; }
+
+  // MessageHandler interface.
+  virtual void OnMessage(Message* msg) {
+    DoUpdateNetworks();
+  }
+
+  using NetworkManagerBase::set_enumeration_permission;
+  using NetworkManagerBase::set_default_local_addresses;
+
+ private:
+  void DoUpdateNetworks() {
+    if (start_count_ == 0)
+      return;
+    std::vector<Network*> networks;
+    for (IfaceList::iterator it = ifaces_.begin();
+         it != ifaces_.end(); ++it) {
+      int prefix_length = 0;
+      if (it->first.ipaddr().family() == AF_INET) {
+        prefix_length = kFakeIPv4NetworkPrefixLength;
+      } else if (it->first.ipaddr().family() == AF_INET6) {
+        prefix_length = kFakeIPv6NetworkPrefixLength;
+      }
+      IPAddress prefix = TruncateIP(it->first.ipaddr(), prefix_length);
+      std::unique_ptr<Network> net(new Network(it->first.hostname(),
+                                               it->first.hostname(), prefix,
+                                               prefix_length, it->second));
+      net->set_default_local_address_provider(this);
+      net->AddIP(it->first.ipaddr());
+      networks.push_back(net.release());
+    }
+    bool changed;
+    MergeNetworkList(networks, &changed);
+    if (changed || !sent_first_update_) {
+      SignalNetworksChanged();
+      sent_first_update_ = true;
+    }
+  }
+
+  IfaceList ifaces_;
+  int next_index_ = 0;
+  int start_count_ = 0;
+  bool sent_first_update_ = false;
+
+  IPAddress default_local_ipv4_address_;
+  IPAddress default_local_ipv6_address_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_FAKENETWORK_H_
diff --git a/base/fakesslidentity.h b/base/fakesslidentity.h
index da204b2..7065fc0 100644
--- a/base/fakesslidentity.h
+++ b/base/fakesslidentity.h
@@ -11,9 +11,110 @@
 #ifndef WEBRTC_BASE_FAKESSLIDENTITY_H_
 #define WEBRTC_BASE_FAKESSLIDENTITY_H_
 
+#include <algorithm>
+#include <memory>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/fakesslidentity.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/messagedigest.h"
+#include "webrtc/base/sslidentity.h"
+
+namespace rtc {
+
+class FakeSSLCertificate : public rtc::SSLCertificate {
+ public:
+  // SHA-1 is the default digest algorithm because it is available in all build
+  // configurations used for unit testing.
+  explicit FakeSSLCertificate(const std::string& data)
+      : data_(data), digest_algorithm_(DIGEST_SHA_1), expiration_time_(-1) {}
+  explicit FakeSSLCertificate(const std::vector<std::string>& certs)
+      : data_(certs.front()),
+        digest_algorithm_(DIGEST_SHA_1),
+        expiration_time_(-1) {
+    std::vector<std::string>::const_iterator it;
+    // Skip certs[0].
+    for (it = certs.begin() + 1; it != certs.end(); ++it) {
+      certs_.push_back(FakeSSLCertificate(*it));
+    }
+  }
+  FakeSSLCertificate* GetReference() const override {
+    return new FakeSSLCertificate(*this);
+  }
+  std::string ToPEMString() const override {
+    return data_;
+  }
+  void ToDER(Buffer* der_buffer) const override {
+    std::string der_string;
+    RTC_CHECK(SSLIdentity::PemToDer(kPemTypeCertificate, data_, &der_string));
+    der_buffer->SetData(der_string.c_str(), der_string.size());
+  }
+  int64_t CertificateExpirationTime() const override {
+    return expiration_time_;
+  }
+  void SetCertificateExpirationTime(int64_t expiration_time) {
+    expiration_time_ = expiration_time;
+  }
+  void set_digest_algorithm(const std::string& algorithm) {
+    digest_algorithm_ = algorithm;
+  }
+  bool GetSignatureDigestAlgorithm(std::string* algorithm) const override {
+    *algorithm = digest_algorithm_;
+    return true;
+  }
+  bool ComputeDigest(const std::string& algorithm,
+                     unsigned char* digest,
+                     size_t size,
+                     size_t* length) const override {
+    *length = rtc::ComputeDigest(algorithm, data_.c_str(), data_.size(),
+                                       digest, size);
+    return (*length != 0);
+  }
+  std::unique_ptr<SSLCertChain> GetChain() const override {
+    if (certs_.empty())
+      return nullptr;
+    std::vector<SSLCertificate*> new_certs(certs_.size());
+    std::transform(certs_.begin(), certs_.end(), new_certs.begin(), DupCert);
+    std::unique_ptr<SSLCertChain> chain(new SSLCertChain(new_certs));
+    std::for_each(new_certs.begin(), new_certs.end(), DeleteCert);
+    return chain;
+  }
+
+ private:
+  static FakeSSLCertificate* DupCert(FakeSSLCertificate cert) {
+    return cert.GetReference();
+  }
+  static void DeleteCert(SSLCertificate* cert) { delete cert; }
+  std::string data_;
+  std::vector<FakeSSLCertificate> certs_;
+  std::string digest_algorithm_;
+  // Expiration time in seconds relative to epoch, 1970-01-01T00:00:00Z (UTC).
+  int64_t expiration_time_;
+};
+
+class FakeSSLIdentity : public rtc::SSLIdentity {
+ public:
+  explicit FakeSSLIdentity(const std::string& data) : cert_(data) {}
+  explicit FakeSSLIdentity(const FakeSSLCertificate& cert) : cert_(cert) {}
+  virtual FakeSSLIdentity* GetReference() const {
+    return new FakeSSLIdentity(*this);
+  }
+  virtual const FakeSSLCertificate& certificate() const { return cert_; }
+  virtual std::string PrivateKeyToPEMString() const {
+    RTC_NOTREACHED();  // Not implemented.
+    return "";
+  }
+  virtual std::string PublicKeyToPEMString() const {
+    RTC_NOTREACHED();  // Not implemented.
+    return "";
+  }
+  virtual bool operator==(const SSLIdentity& other) const {
+    RTC_NOTREACHED();  // Not implemented.
+    return false;
+  }
+ private:
+  FakeSSLCertificate cert_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_FAKESSLIDENTITY_H_
diff --git a/base/file.cc b/base/file.cc
new file mode 100644
index 0000000..983eb8b
--- /dev/null
+++ b/base/file.cc
@@ -0,0 +1,94 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/file.h"
+
+#include <utility>
+
+namespace rtc {
+
+namespace {
+
+std::string NormalizePathname(Pathname&& path) {
+  path.Normalize();
+  return path.pathname();
+}
+
+}  // namespace
+
+File::File(PlatformFile file) : file_(file) {}
+
+File::File() : file_(kInvalidPlatformFileValue) {}
+
+File::~File() {
+  Close();
+}
+
+// static
+File File::Open(const std::string& path) {
+  return File(OpenPlatformFile(path));
+}
+
+// static
+File File::Open(Pathname&& path) {
+  return Open(NormalizePathname(std::move(path)));
+}
+
+// static
+File File::Open(const Pathname& path) {
+  return Open(Pathname(path));
+}
+
+// static
+File File::Create(const std::string& path) {
+  return File(CreatePlatformFile(path));
+}
+
+// static
+File File::Create(Pathname&& path) {
+  return Create(NormalizePathname(std::move(path)));
+}
+
+// static
+File File::Create(const Pathname& path) {
+  return Create(Pathname(path));
+}
+
+// static
+bool File::Remove(const std::string& path) {
+  return RemoveFile(path);
+}
+
+// static
+bool File::Remove(Pathname&& path) {
+  return Remove(NormalizePathname(std::move(path)));
+}
+
+// static
+bool File::Remove(const Pathname& path) {
+  return Remove(Pathname(path));
+}
+
+File::File(File&& other) : file_(other.file_) {
+  other.file_ = kInvalidPlatformFileValue;
+}
+
+File& File::operator=(File&& other) {
+  Close();
+  file_ = other.file_;
+  other.file_ = kInvalidPlatformFileValue;
+  return *this;
+}
+
+bool File::IsOpen() {
+  return file_ != kInvalidPlatformFileValue;
+}
+
+}  // namespace rtc
diff --git a/base/file.h b/base/file.h
index 5a4465f..f4806d1 100644
--- a/base/file.h
+++ b/base/file.h
@@ -11,9 +11,72 @@
 #ifndef WEBRTC_BASE_FILE_H_
 #define WEBRTC_BASE_FILE_H_
 
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/file.h"
+#include <string>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/platform_file.h"
+
+namespace rtc {
+
+// This class wraps the platform specific APIs for simple file interactions.
+//
+// The various read and write methods are best effort, i.e. if an underlying
+// call does not manage to read/write all the data more calls will be performed,
+// until an error is detected or all data is read/written.
+class File {
+ public:
+  // Wraps the given PlatformFile. This class is then responsible for closing
+  // the file, which will be done in the destructor if Close is never called.
+  explicit File(PlatformFile);
+  // The default constructor produces a closed file.
+  File();
+  ~File();
+
+  File(File&& other);
+  File& operator=(File&& other);
+
+  // Open and Create give files with both reading and writing enabled.
+  static File Open(const std::string& path);
+  static File Open(Pathname&& path);
+  static File Open(const Pathname& path);
+  // If the file already exists it will be overwritten.
+  static File Create(const std::string& path);
+  static File Create(Pathname&& path);
+  static File Create(const Pathname& path);
+
+  // Remove a file in the file system.
+  static bool Remove(const std::string& path);
+  static bool Remove(Pathname&& path);
+  static bool Remove(const Pathname& path);
+
+  size_t Write(const uint8_t* data, size_t length);
+  size_t Read(uint8_t* buffer, size_t length);
+
+  // The current position in the file after a call to these methods is platform
+  // dependent (MSVC gives position offset+length, most other
+  // compilers/platforms do not alter the position), i.e. do not depend on it,
+  // do a Seek before any subsequent Read/Write.
+  size_t WriteAt(const uint8_t* data, size_t length, size_t offset);
+  size_t ReadAt(uint8_t* buffer, size_t length, size_t offset);
+
+  // Attempt to position the file at the given offset from the start.
+  // Returns true if successful, false otherwise.
+  bool Seek(size_t offset);
+
+  // Attempt to close the file. Returns true if successful, false otherwise,
+  // most notably when the file is already closed.
+  bool Close();
+
+  bool IsOpen();
+
+ private:
+  PlatformFile file_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(File);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_FILE_H_
diff --git a/base/file_posix.cc b/base/file_posix.cc
new file mode 100644
index 0000000..c386a34
--- /dev/null
+++ b/base/file_posix.cc
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/file.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+size_t File::Write(const uint8_t* data, size_t length) {
+  size_t total_written = 0;
+  do {
+    ssize_t written;
+    do {
+      written = ::write(file_, data + total_written, length - total_written);
+    } while (written == -1 && errno == EINTR);
+    if (written == -1)
+      break;
+    total_written += written;
+  } while (total_written < length);
+  return total_written;
+}
+
+size_t File::Read(uint8_t* buffer, size_t length) {
+  size_t total_read = 0;
+  do {
+    ssize_t read;
+    do {
+      read = ::read(file_, buffer + total_read, length - total_read);
+    } while (read == -1 && errno == EINTR);
+    if (read == -1)
+      break;
+    total_read += read;
+  } while (total_read < length);
+  return total_read;
+}
+
+size_t File::WriteAt(const uint8_t* data, size_t length, size_t offset) {
+  size_t total_written = 0;
+  do {
+    ssize_t written;
+    do {
+      written = ::pwrite(file_, data + total_written, length - total_written,
+                         offset + total_written);
+    } while (written == -1 && errno == EINTR);
+    if (written == -1)
+      break;
+    total_written += written;
+  } while (total_written < length);
+  return total_written;
+}
+
+size_t File::ReadAt(uint8_t* buffer, size_t length, size_t offset) {
+  size_t total_read = 0;
+  do {
+    ssize_t read;
+    do {
+      read = ::pread(file_, buffer + total_read, length - total_read,
+                     offset + total_read);
+    } while (read == -1 && errno == EINTR);
+    if (read == -1)
+      break;
+    total_read += read;
+  } while (total_read < length);
+  return total_read;
+}
+
+bool File::Seek(size_t offset) {
+  RTC_DCHECK_LE(offset, std::numeric_limits<off_t>::max());
+  return lseek(file_, static_cast<off_t>(offset), SEEK_SET) != -1;
+}
+
+bool File::Close() {
+  if (file_ == rtc::kInvalidPlatformFileValue)
+    return false;
+  bool ret = close(file_) == 0;
+  file_ = rtc::kInvalidPlatformFileValue;
+  return ret;
+}
+
+}  // namespace rtc
diff --git a/base/file_unittest.cc b/base/file_unittest.cc
new file mode 100644
index 0000000..99a5d58
--- /dev/null
+++ b/base/file_unittest.cc
@@ -0,0 +1,201 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <limits>
+#include <memory>
+#include <string>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/file.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+#if defined(WEBRTC_WIN)
+
+#include "webrtc/base/win32.h"
+
+#else  // if defined(WEBRTC_WIN)
+
+#include <errno.h>
+
+#endif
+
+namespace rtc {
+
+int LastError() {
+#if defined(WEBRTC_WIN)
+  return ::GetLastError();
+#else
+  return errno;
+#endif
+}
+
+bool VerifyBuffer(uint8_t* buffer, size_t length, uint8_t start_value) {
+  for (size_t i = 0; i < length; ++i) {
+    uint8_t val = start_value++;
+    EXPECT_EQ(val, buffer[i]);
+    if (buffer[i] != val)
+      return false;
+  }
+  // Prevent the same buffer from being verified multiple times simply
+  // because some operation that should have written to it failed
+  memset(buffer, 0, length);
+  return true;
+}
+
+class FileTest : public ::testing::Test {
+ protected:
+  std::string path_;
+  void SetUp() override {
+    path_ = webrtc::test::TempFilename(webrtc::test::OutputPath(), "test_file");
+    ASSERT_FALSE(path_.empty());
+  }
+  void TearDown() override { RemoveFile(path_); }
+};
+
+TEST_F(FileTest, DefaultConstructor) {
+  File file;
+  uint8_t buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+  EXPECT_FALSE(file.IsOpen());
+  EXPECT_EQ(0u, file.Write(buffer, 10));
+  EXPECT_FALSE(file.Seek(0));
+  EXPECT_EQ(0u, file.Read(buffer, 10));
+  EXPECT_EQ(0u, file.WriteAt(buffer, 10, 0));
+  EXPECT_EQ(0u, file.ReadAt(buffer, 10, 0));
+  EXPECT_FALSE(file.Close());
+}
+
+TEST_F(FileTest, DoubleClose) {
+  File file = File::Open(path_);
+  ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+
+  EXPECT_TRUE(file.Close());
+  EXPECT_FALSE(file.Close());
+}
+
+TEST_F(FileTest, SimpleReadWrite) {
+  File file = File::Open(path_);
+  ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+
+  uint8_t data[100] = {0};
+  uint8_t out[100] = {0};
+  for (int i = 0; i < 100; ++i) {
+    data[i] = i;
+  }
+
+  EXPECT_EQ(10u, file.Write(data, 10));
+
+  EXPECT_TRUE(file.Seek(0));
+  EXPECT_EQ(10u, file.Read(out, 10));
+  EXPECT_TRUE(VerifyBuffer(out, 10, 0));
+
+  EXPECT_TRUE(file.Seek(0));
+  EXPECT_EQ(100u, file.Write(data, 100));
+
+  EXPECT_TRUE(file.Seek(0));
+  EXPECT_EQ(100u, file.Read(out, 100));
+  EXPECT_TRUE(VerifyBuffer(out, 100, 0));
+
+  EXPECT_TRUE(file.Seek(1));
+  EXPECT_EQ(50u, file.Write(data, 50));
+  EXPECT_EQ(50u, file.Write(data + 50, 50));
+
+  EXPECT_TRUE(file.Seek(1));
+  EXPECT_EQ(100u, file.Read(out, 100));
+  EXPECT_TRUE(VerifyBuffer(out, 100, 0));
+}
+
+TEST_F(FileTest, ReadWriteClose) {
+  File file = File::Open(path_);
+  ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+
+  uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  uint8_t out[10] = {0};
+  EXPECT_EQ(10u, file.Write(data, 10));
+  EXPECT_TRUE(file.Close());
+
+  File file2 = File::Open(path_);
+  ASSERT_TRUE(file2.IsOpen()) << "Error: " << LastError();
+  EXPECT_EQ(10u, file2.Read(out, 10));
+  EXPECT_TRUE(VerifyBuffer(out, 10, 0));
+}
+
+TEST_F(FileTest, RandomAccessRead) {
+  File file = File::Open(path_);
+  ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+
+  uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  uint8_t out[10] = {0};
+  EXPECT_EQ(10u, file.Write(data, 10));
+
+  EXPECT_EQ(4u, file.ReadAt(out, 4, 0));
+  EXPECT_TRUE(VerifyBuffer(out, 4, 0));
+
+  EXPECT_EQ(4u, file.ReadAt(out, 4, 4));
+  EXPECT_TRUE(VerifyBuffer(out, 4, 4));
+
+  EXPECT_EQ(5u, file.ReadAt(out, 5, 5));
+  EXPECT_TRUE(VerifyBuffer(out, 5, 5));
+}
+
+TEST_F(FileTest, RandomAccessReadWrite) {
+  File file = File::Open(path_);
+  ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+
+  uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  uint8_t out[10] = {0};
+  EXPECT_EQ(10u, file.Write(data, 10));
+  EXPECT_TRUE(file.Seek(4));
+
+  EXPECT_EQ(4u, file.WriteAt(data, 4, 4));
+  EXPECT_EQ(4u, file.ReadAt(out, 4, 4));
+  EXPECT_TRUE(VerifyBuffer(out, 4, 0));
+
+  EXPECT_EQ(2u, file.WriteAt(data, 2, 8));
+  EXPECT_EQ(2u, file.ReadAt(out, 2, 8));
+  EXPECT_TRUE(VerifyBuffer(out, 2, 0));
+}
+
+TEST_F(FileTest, OpenFromPathname) {
+  {
+    File file = File::Open(Pathname(path_));
+    ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+  }
+
+  {
+    Pathname path(path_);
+    File file = File::Open(path);
+    ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+  }
+}
+
+TEST_F(FileTest, CreateFromPathname) {
+  {
+    File file = File::Create(Pathname(path_));
+    ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+  }
+
+  {
+    Pathname path(path_);
+    File file = File::Create(path);
+    ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+  }
+}
+
+TEST_F(FileTest, ShouldBeAbleToRemoveFile) {
+  {
+    File file = File::Open(Pathname(path_));
+    ASSERT_TRUE(file.IsOpen()) << "Error: " << LastError();
+  }
+
+  ASSERT_TRUE(File::Remove(Pathname(path_))) << "Error: " << LastError();
+}
+
+}  // namespace rtc
diff --git a/base/file_win.cc b/base/file_win.cc
new file mode 100644
index 0000000..71e6eff
--- /dev/null
+++ b/base/file_win.cc
@@ -0,0 +1,113 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/file.h"
+
+#include <io.h>
+#include "webrtc/base/win32.h"
+
+#include <limits>  // NOLINT: win32.h should be considered a system header
+
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+size_t File::Write(const uint8_t* data, size_t length) {
+  RTC_DCHECK_LT(length, std::numeric_limits<DWORD>::max());
+  size_t total_written = 0;
+  do {
+    DWORD written;
+    if (!::WriteFile(file_, data + total_written,
+                     static_cast<DWORD>(length - total_written), &written,
+                     nullptr)) {
+      break;
+    }
+    total_written += written;
+  } while (total_written < length);
+  return total_written;
+}
+
+size_t File::Read(uint8_t* buffer, size_t length) {
+  RTC_DCHECK_LT(length, std::numeric_limits<DWORD>::max());
+  size_t total_read = 0;
+  do {
+    DWORD read;
+    if (!::ReadFile(file_, buffer + total_read,
+                    static_cast<DWORD>(length - total_read), &read, nullptr)) {
+      break;
+    }
+    total_read += read;
+  } while (total_read < length);
+  return total_read;
+}
+
+size_t File::WriteAt(const uint8_t* data, size_t length, size_t offset) {
+  RTC_DCHECK_LT(length, std::numeric_limits<DWORD>::max());
+  size_t total_written = 0;
+  do {
+    DWORD written;
+
+    LARGE_INTEGER offset_li;
+    offset_li.QuadPart = offset + total_written;
+
+    OVERLAPPED overlapped = {0};
+    overlapped.Offset = offset_li.LowPart;
+    overlapped.OffsetHigh = offset_li.HighPart;
+
+    if (!::WriteFile(file_, data + total_written,
+                     static_cast<DWORD>(length - total_written), &written,
+                     &overlapped)) {
+      break;
+    }
+
+    total_written += written;
+  } while (total_written < length);
+  return total_written;
+}
+
+size_t File::ReadAt(uint8_t* buffer, size_t length, size_t offset) {
+  RTC_DCHECK_LT(length, std::numeric_limits<DWORD>::max());
+  size_t total_read = 0;
+  do {
+    DWORD read;
+
+    LARGE_INTEGER offset_li;
+    offset_li.QuadPart = offset + total_read;
+
+    OVERLAPPED overlapped = {0};
+    overlapped.Offset = offset_li.LowPart;
+    overlapped.OffsetHigh = offset_li.HighPart;
+
+    if (!::ReadFile(file_, buffer + total_read,
+                    static_cast<DWORD>(length - total_read), &read,
+                    &overlapped)) {
+      break;
+    }
+
+    total_read += read;
+  } while (total_read < length);
+  return total_read;
+}
+
+bool File::Seek(size_t offset) {
+  LARGE_INTEGER distance;
+  distance.QuadPart = offset;
+  return SetFilePointerEx(file_, distance, nullptr, FILE_BEGIN) != 0;
+}
+
+bool File::Close() {
+  if (file_ == kInvalidPlatformFileValue)
+    return false;
+  bool ret = CloseHandle(file_) != 0;
+  file_ = kInvalidPlatformFileValue;
+  return ret;
+}
+
+}  // namespace rtc
diff --git a/base/filerotatingstream.cc b/base/filerotatingstream.cc
new file mode 100644
index 0000000..d1434de
--- /dev/null
+++ b/base/filerotatingstream.cc
@@ -0,0 +1,400 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/filerotatingstream.h"
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/pathutils.h"
+
+// Note: We use std::cerr for logging in the write paths of this stream to avoid
+// infinite loops when logging.
+
+namespace rtc {
+
+FileRotatingStream::FileRotatingStream(const std::string& dir_path,
+                                       const std::string& file_prefix)
+    : FileRotatingStream(dir_path, file_prefix, 0, 0, kRead) {
+}
+
+FileRotatingStream::FileRotatingStream(const std::string& dir_path,
+                                       const std::string& file_prefix,
+                                       size_t max_file_size,
+                                       size_t num_files)
+    : FileRotatingStream(dir_path,
+                         file_prefix,
+                         max_file_size,
+                         num_files,
+                         kWrite) {
+  RTC_DCHECK_GT(max_file_size, 0);
+  RTC_DCHECK_GT(num_files, 1);
+}
+
+FileRotatingStream::FileRotatingStream(const std::string& dir_path,
+                                       const std::string& file_prefix,
+                                       size_t max_file_size,
+                                       size_t num_files,
+                                       Mode mode)
+    : dir_path_(dir_path),
+      file_prefix_(file_prefix),
+      mode_(mode),
+      file_stream_(nullptr),
+      max_file_size_(max_file_size),
+      current_file_index_(0),
+      rotation_index_(0),
+      current_bytes_written_(0),
+      disable_buffering_(false) {
+  RTC_DCHECK(Filesystem::IsFolder(dir_path));
+  switch (mode) {
+    case kWrite: {
+      file_names_.clear();
+      for (size_t i = 0; i < num_files; ++i) {
+        file_names_.push_back(GetFilePath(i, num_files));
+      }
+      rotation_index_ = num_files - 1;
+      break;
+    }
+    case kRead: {
+      file_names_ = GetFilesWithPrefix();
+      std::sort(file_names_.begin(), file_names_.end());
+      if (file_names_.size() > 0) {
+        // |file_names_| is sorted newest first, so read from the end.
+        current_file_index_ = file_names_.size() - 1;
+      }
+      break;
+    }
+  }
+}
+
+FileRotatingStream::~FileRotatingStream() {
+}
+
+StreamState FileRotatingStream::GetState() const {
+  if (mode_ == kRead && current_file_index_ < file_names_.size()) {
+    return SS_OPEN;
+  }
+  if (!file_stream_) {
+    return SS_CLOSED;
+  }
+  return file_stream_->GetState();
+}
+
+StreamResult FileRotatingStream::Read(void* buffer,
+                                      size_t buffer_len,
+                                      size_t* read,
+                                      int* error) {
+  RTC_DCHECK(buffer);
+  if (mode_ != kRead) {
+    return SR_EOS;
+  }
+  if (current_file_index_ >= file_names_.size()) {
+    return SR_EOS;
+  }
+  // We will have no file stream initially, and when we are finished with the
+  // previous file.
+  if (!file_stream_) {
+    if (!OpenCurrentFile()) {
+      return SR_ERROR;
+    }
+  }
+  int local_error = 0;
+  if (!error) {
+    error = &local_error;
+  }
+  StreamResult result = file_stream_->Read(buffer, buffer_len, read, error);
+  if (result == SR_EOS || result == SR_ERROR) {
+    if (result == SR_ERROR) {
+      LOG(LS_ERROR) << "Failed to read from: "
+                    << file_names_[current_file_index_] << "Error: " << error;
+    }
+    // Reached the end of the file, read next file. If there is an error return
+    // the error status but allow for a next read by reading next file.
+    CloseCurrentFile();
+    if (current_file_index_ == 0) {
+      // Just finished reading the last file, signal EOS by setting index.
+      current_file_index_ = file_names_.size();
+    } else {
+      --current_file_index_;
+    }
+    if (read) {
+      *read = 0;
+    }
+    return result == SR_EOS ? SR_SUCCESS : result;
+  } else if (result == SR_SUCCESS) {
+    // Succeeded, continue reading from this file.
+    return SR_SUCCESS;
+  } else {
+    RTC_NOTREACHED();
+  }
+  return result;
+}
+
+StreamResult FileRotatingStream::Write(const void* data,
+                                       size_t data_len,
+                                       size_t* written,
+                                       int* error) {
+  if (mode_ != kWrite) {
+    return SR_EOS;
+  }
+  if (!file_stream_) {
+    std::cerr << "Open() must be called before Write." << std::endl;
+    return SR_ERROR;
+  }
+  // Write as much as will fit in to the current file.
+  RTC_DCHECK_LT(current_bytes_written_, max_file_size_);
+  size_t remaining_bytes = max_file_size_ - current_bytes_written_;
+  size_t write_length = std::min(data_len, remaining_bytes);
+  size_t local_written = 0;
+  if (!written) {
+    written = &local_written;
+  }
+  StreamResult result = file_stream_->Write(data, write_length, written, error);
+  current_bytes_written_ += *written;
+
+  // If we're done with this file, rotate it out.
+  if (current_bytes_written_ >= max_file_size_) {
+    RTC_DCHECK_EQ(current_bytes_written_, max_file_size_);
+    RotateFiles();
+  }
+  return result;
+}
+
+bool FileRotatingStream::Flush() {
+  if (!file_stream_) {
+    return false;
+  }
+  return file_stream_->Flush();
+}
+
+bool FileRotatingStream::GetSize(size_t* size) const {
+  if (mode_ != kRead) {
+    // Not possible to get accurate size on disk when writing because of
+    // potential buffering.
+    return false;
+  }
+  RTC_DCHECK(size);
+  *size = 0;
+  size_t total_size = 0;
+  for (auto file_name : file_names_) {
+    Pathname pathname(file_name);
+    size_t file_size = 0;
+    if (Filesystem::GetFileSize(file_name, &file_size)) {
+      total_size += file_size;
+    }
+  }
+  *size = total_size;
+  return true;
+}
+
+void FileRotatingStream::Close() {
+  CloseCurrentFile();
+}
+
+bool FileRotatingStream::Open() {
+  switch (mode_) {
+    case kRead:
+      // Defer opening to when we first read since we want to return read error
+      // if we fail to open next file.
+      return true;
+    case kWrite: {
+      // Delete existing files when opening for write.
+      std::vector<std::string> matching_files = GetFilesWithPrefix();
+      for (auto matching_file : matching_files) {
+        if (!Filesystem::DeleteFile(matching_file)) {
+          std::cerr << "Failed to delete: " << matching_file << std::endl;
+        }
+      }
+      return OpenCurrentFile();
+    }
+  }
+  return false;
+}
+
+bool FileRotatingStream::DisableBuffering() {
+  disable_buffering_ = true;
+  if (!file_stream_) {
+    std::cerr << "Open() must be called before DisableBuffering()."
+              << std::endl;
+    return false;
+  }
+  return file_stream_->DisableBuffering();
+}
+
+std::string FileRotatingStream::GetFilePath(size_t index) const {
+  RTC_DCHECK_LT(index, file_names_.size());
+  return file_names_[index];
+}
+
+bool FileRotatingStream::OpenCurrentFile() {
+  CloseCurrentFile();
+
+  // Opens the appropriate file in the appropriate mode.
+  RTC_DCHECK_LT(current_file_index_, file_names_.size());
+  std::string file_path = file_names_[current_file_index_];
+  file_stream_.reset(new FileStream());
+  const char* mode = nullptr;
+  switch (mode_) {
+    case kWrite:
+      mode = "w+";
+      // We should always we writing to the zero-th file.
+      RTC_DCHECK_EQ(current_file_index_, 0);
+      break;
+    case kRead:
+      mode = "r";
+      break;
+  }
+  int error = 0;
+  if (!file_stream_->Open(file_path, mode, &error)) {
+    std::cerr << "Failed to open: " << file_path << "Error: " << error
+              << std::endl;
+    file_stream_.reset();
+    return false;
+  }
+  if (disable_buffering_) {
+    file_stream_->DisableBuffering();
+  }
+  return true;
+}
+
+void FileRotatingStream::CloseCurrentFile() {
+  if (!file_stream_) {
+    return;
+  }
+  current_bytes_written_ = 0;
+  file_stream_.reset();
+}
+
+void FileRotatingStream::RotateFiles() {
+  RTC_DCHECK_EQ(mode_, kWrite);
+  CloseCurrentFile();
+  // Rotates the files by deleting the file at |rotation_index_|, which is the
+  // oldest file and then renaming the newer files to have an incremented index.
+  // See header file comments for example.
+  RTC_DCHECK_LT(rotation_index_, file_names_.size());
+  std::string file_to_delete = file_names_[rotation_index_];
+  if (Filesystem::IsFile(file_to_delete)) {
+    if (!Filesystem::DeleteFile(file_to_delete)) {
+      std::cerr << "Failed to delete: " << file_to_delete << std::endl;
+    }
+  }
+  for (auto i = rotation_index_; i > 0; --i) {
+    std::string rotated_name = file_names_[i];
+    std::string unrotated_name = file_names_[i - 1];
+    if (Filesystem::IsFile(unrotated_name)) {
+      if (!Filesystem::MoveFile(unrotated_name, rotated_name)) {
+        std::cerr << "Failed to move: " << unrotated_name << " to "
+                  << rotated_name << std::endl;
+      }
+    }
+  }
+  // Create a new file for 0th index.
+  OpenCurrentFile();
+  OnRotation();
+}
+
+std::vector<std::string> FileRotatingStream::GetFilesWithPrefix() const {
+  std::vector<std::string> files;
+  // Iterate over the files in the directory.
+  DirectoryIterator it;
+  Pathname dir_path;
+  dir_path.SetFolder(dir_path_);
+  if (!it.Iterate(dir_path)) {
+    return files;
+  }
+  do {
+    std::string current_name = it.Name();
+    if (current_name.size() && !it.IsDirectory() &&
+        current_name.compare(0, file_prefix_.size(), file_prefix_) == 0) {
+      Pathname path(dir_path_, current_name);
+      files.push_back(path.pathname());
+    }
+  } while (it.Next());
+  return files;
+}
+
+std::string FileRotatingStream::GetFilePath(size_t index,
+                                            size_t num_files) const {
+  RTC_DCHECK_LT(index, num_files);
+  std::ostringstream file_name;
+  // The format will be "_%<num_digits>zu". We want to zero pad the index so
+  // that it will sort nicely.
+  size_t max_digits = ((num_files - 1) / 10) + 1;
+  size_t num_digits = (index / 10) + 1;
+  RTC_DCHECK_LE(num_digits, max_digits);
+  size_t padding = max_digits - num_digits;
+
+  file_name << file_prefix_ << "_";
+  for (size_t i = 0; i < padding; ++i) {
+    file_name << "0";
+  }
+  file_name << index;
+
+  Pathname file_path(dir_path_, file_name.str());
+  return file_path.pathname();
+}
+
+CallSessionFileRotatingStream::CallSessionFileRotatingStream(
+    const std::string& dir_path)
+    : FileRotatingStream(dir_path, kLogPrefix),
+      max_total_log_size_(0),
+      num_rotations_(0) {
+}
+
+CallSessionFileRotatingStream::CallSessionFileRotatingStream(
+    const std::string& dir_path,
+    size_t max_total_log_size)
+    : FileRotatingStream(dir_path,
+                         kLogPrefix,
+                         max_total_log_size / 2,
+                         GetNumRotatingLogFiles(max_total_log_size) + 1),
+      max_total_log_size_(max_total_log_size),
+      num_rotations_(0) {
+  RTC_DCHECK_GE(max_total_log_size, 4);
+}
+
+const char* CallSessionFileRotatingStream::kLogPrefix = "webrtc_log";
+const size_t CallSessionFileRotatingStream::kRotatingLogFileDefaultSize =
+    1024 * 1024;
+
+void CallSessionFileRotatingStream::OnRotation() {
+  ++num_rotations_;
+  if (num_rotations_ == 1) {
+    // On the first rotation adjust the max file size so subsequent files after
+    // the first are smaller.
+    SetMaxFileSize(GetRotatingLogSize(max_total_log_size_));
+  } else if (num_rotations_ == (GetNumFiles() - 1)) {
+    // On the next rotation the very first file is going to be deleted. Change
+    // the rotation index so this doesn't happen.
+    SetRotationIndex(GetRotationIndex() - 1);
+  }
+}
+
+size_t CallSessionFileRotatingStream::GetRotatingLogSize(
+    size_t max_total_log_size) {
+  size_t num_rotating_log_files = GetNumRotatingLogFiles(max_total_log_size);
+  size_t rotating_log_size = num_rotating_log_files > 2
+                                 ? kRotatingLogFileDefaultSize
+                                 : max_total_log_size / 4;
+  return rotating_log_size;
+}
+
+size_t CallSessionFileRotatingStream::GetNumRotatingLogFiles(
+    size_t max_total_log_size) {
+  // At minimum have two rotating files. Otherwise split the available log size
+  // evenly across 1MB files.
+  return std::max((size_t)2,
+                  (max_total_log_size / 2) / kRotatingLogFileDefaultSize);
+}
+
+}  // namespace rtc
diff --git a/base/filerotatingstream.h b/base/filerotatingstream.h
index 26306db..a3e808c 100644
--- a/base/filerotatingstream.h
+++ b/base/filerotatingstream.h
@@ -11,9 +11,163 @@
 #ifndef WEBRTC_BASE_FILEROTATINGSTREAM_H_
 #define WEBRTC_BASE_FILEROTATINGSTREAM_H_
 
+#include <memory>
+#include <string>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/filerotatingstream.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/stream.h"
+
+namespace rtc {
+
+// FileRotatingStream writes to a file in the directory specified in the
+// constructor. It rotates the files once the current file is full. The
+// individual file size and the number of files used is configurable in the
+// constructor. Open() must be called before using this stream.
+class FileRotatingStream : public StreamInterface {
+ public:
+  // Use this constructor for reading a directory previously written to with
+  // this stream.
+  FileRotatingStream(const std::string& dir_path,
+                     const std::string& file_prefix);
+
+  // Use this constructor for writing to a directory. Files in the directory
+  // matching the prefix will be deleted on open.
+  FileRotatingStream(const std::string& dir_path,
+                     const std::string& file_prefix,
+                     size_t max_file_size,
+                     size_t num_files);
+
+  ~FileRotatingStream() override;
+
+  // StreamInterface methods.
+  StreamState GetState() const override;
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override;
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+  bool Flush() override;
+  // Returns the total file size currently used on disk.
+  bool GetSize(size_t* size) const override;
+  void Close() override;
+
+  // Opens the appropriate file(s). Call this before using the stream.
+  bool Open();
+
+  // Disabling buffering causes writes to block until disk is updated. This is
+  // enabled by default for performance.
+  bool DisableBuffering();
+
+  // Returns the path used for the i-th newest file, where the 0th file is the
+  // newest file. The file may or may not exist, this is just used for
+  // formatting. Index must be less than GetNumFiles().
+  std::string GetFilePath(size_t index) const;
+
+  // Returns the number of files that will used by this stream.
+  size_t GetNumFiles() const { return file_names_.size(); }
+
+ protected:
+  size_t GetMaxFileSize() const { return max_file_size_; }
+
+  void SetMaxFileSize(size_t size) { max_file_size_ = size; }
+
+  size_t GetRotationIndex() const { return rotation_index_; }
+
+  void SetRotationIndex(size_t index) { rotation_index_ = index; }
+
+  virtual void OnRotation() {}
+
+ private:
+  enum Mode { kRead, kWrite };
+
+  FileRotatingStream(const std::string& dir_path,
+                     const std::string& file_prefix,
+                     size_t max_file_size,
+                     size_t num_files,
+                     Mode mode);
+
+  bool OpenCurrentFile();
+  void CloseCurrentFile();
+
+  // Rotates the files by creating a new current file, renaming the
+  // existing files, and deleting the oldest one. e.g.
+  // file_0 -> file_1
+  // file_1 -> file_2
+  // file_2 -> delete
+  // create new file_0
+  void RotateFiles();
+
+  // Returns a list of file names in the directory beginning with the prefix.
+  std::vector<std::string> GetFilesWithPrefix() const;
+  // Private version of GetFilePath.
+  std::string GetFilePath(size_t index, size_t num_files) const;
+
+  const std::string dir_path_;
+  const std::string file_prefix_;
+  const Mode mode_;
+
+  // FileStream is used to write to the current file.
+  std::unique_ptr<FileStream> file_stream_;
+  // Convenience storage for file names so we don't generate them over and over.
+  std::vector<std::string> file_names_;
+  size_t max_file_size_;
+  size_t current_file_index_;
+  // The rotation index indicates the index of the file that will be
+  // deleted first on rotation. Indices lower than this index will be rotated.
+  size_t rotation_index_;
+  // Number of bytes written to current file. We need this because with
+  // buffering the file size read from disk might not be accurate.
+  size_t current_bytes_written_;
+  bool disable_buffering_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(FileRotatingStream);
+};
+
+// CallSessionFileRotatingStream is meant to be used in situations where we will
+// have limited disk space. Its purpose is to read and write logs up to a
+// maximum size. Once the maximum size is exceeded, logs from the middle are
+// deleted whereas logs from the beginning and end are preserved. The reason for
+// this is because we anticipate that in WebRTC the beginning and end of the
+// logs are most useful for call diagnostics.
+//
+// This implementation simply writes to a single file until
+// |max_total_log_size| / 2 bytes are written to it, and subsequently writes to
+// a set of rotating files. We do this by inheriting FileRotatingStream and
+// setting the appropriate internal variables so that we don't delete the last
+// (earliest) file on rotate, and that that file's size is bigger.
+//
+// Open() must be called before using this stream.
+class CallSessionFileRotatingStream : public FileRotatingStream {
+ public:
+  // Use this constructor for reading a directory previously written to with
+  // this stream.
+  explicit CallSessionFileRotatingStream(const std::string& dir_path);
+  // Use this constructor for writing to a directory. Files in the directory
+  // matching what's used by the stream will be deleted. |max_total_log_size|
+  // must be at least 4.
+  CallSessionFileRotatingStream(const std::string& dir_path,
+                                size_t max_total_log_size);
+  ~CallSessionFileRotatingStream() override {}
+
+ protected:
+  void OnRotation() override;
+
+ private:
+  static size_t GetRotatingLogSize(size_t max_total_log_size);
+  static size_t GetNumRotatingLogFiles(size_t max_total_log_size);
+  static const char* kLogPrefix;
+  static const size_t kRotatingLogFileDefaultSize;
+
+  const size_t max_total_log_size_;
+  size_t num_rotations_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(CallSessionFileRotatingStream);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_FILEROTATINGSTREAM_H_
diff --git a/base/filerotatingstream_unittest.cc b/base/filerotatingstream_unittest.cc
new file mode 100644
index 0000000..9ce00da
--- /dev/null
+++ b/base/filerotatingstream_unittest.cc
@@ -0,0 +1,345 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/filerotatingstream.h"
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+namespace rtc {
+
+namespace {
+
+void CleanupLogDirectory(const FileRotatingStream& stream) {
+  for (size_t i = 0; i < stream.GetNumFiles(); ++i) {
+    // Ignore return value, not all files are expected to exist.
+    webrtc::test::RemoveFile(stream.GetFilePath(i));
+  }
+}
+
+}  // namespace
+
+#if defined (WEBRTC_ANDROID)
+// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
+#define MAYBE_FileRotatingStreamTest DISABLED_FileRotatingStreamTest
+#else
+#define MAYBE_FileRotatingStreamTest FileRotatingStreamTest
+#endif
+
+class MAYBE_FileRotatingStreamTest : public ::testing::Test {
+ protected:
+  static const char* kFilePrefix;
+  static const size_t kMaxFileSize;
+
+  void Init(const std::string& dir_name,
+            const std::string& file_prefix,
+            size_t max_file_size,
+            size_t num_log_files) {
+    dir_path_ = webrtc::test::OutputPath();
+
+    // Append per-test output path in order to run within gtest parallel.
+    dir_path_.append(dir_name);
+    dir_path_.push_back(Pathname::DefaultFolderDelimiter());
+    ASSERT_TRUE(webrtc::test::CreateDir(dir_path_));
+    stream_.reset(new FileRotatingStream(dir_path_, file_prefix, max_file_size,
+                                         num_log_files));
+  }
+
+  void TearDown() override {
+    // On windows, open files can't be removed.
+    stream_->Close();
+    CleanupLogDirectory(*stream_);
+    EXPECT_TRUE(webrtc::test::RemoveDir(dir_path_));
+
+    stream_.reset();
+  }
+
+  // Writes the data to the stream and flushes it.
+  void WriteAndFlush(const void* data, const size_t data_len) {
+    EXPECT_EQ(SR_SUCCESS, stream_->WriteAll(data, data_len, nullptr, nullptr));
+    EXPECT_TRUE(stream_->Flush());
+  }
+
+  // Checks that the stream reads in the expected contents and then returns an
+  // end of stream result.
+  void VerifyStreamRead(const char* expected_contents,
+                        const size_t expected_length,
+                        const std::string& dir_path,
+                        const char* file_prefix) {
+    std::unique_ptr<FileRotatingStream> stream;
+    stream.reset(new FileRotatingStream(dir_path, file_prefix));
+    ASSERT_TRUE(stream->Open());
+    size_t read = 0;
+    size_t stream_size = 0;
+    EXPECT_TRUE(stream->GetSize(&stream_size));
+    std::unique_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
+    EXPECT_EQ(SR_SUCCESS,
+              stream->ReadAll(buffer.get(), expected_length, &read, nullptr));
+    EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
+    EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
+    EXPECT_EQ(stream_size, read);
+  }
+
+  void VerifyFileContents(const char* expected_contents,
+                          const size_t expected_length,
+                          const std::string& file_path) {
+    std::unique_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
+    FileStream stream;
+    ASSERT_TRUE(stream.Open(file_path, "r", nullptr));
+    EXPECT_EQ(rtc::SR_SUCCESS,
+              stream.ReadAll(buffer.get(), expected_length, nullptr, nullptr));
+    EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
+    size_t file_size = 0;
+    EXPECT_TRUE(stream.GetSize(&file_size));
+    EXPECT_EQ(file_size, expected_length);
+  }
+
+  std::unique_ptr<FileRotatingStream> stream_;
+  std::string dir_path_;
+};
+
+const char* MAYBE_FileRotatingStreamTest::kFilePrefix =
+    "FileRotatingStreamTest";
+const size_t MAYBE_FileRotatingStreamTest::kMaxFileSize = 2;
+
+// Tests that stream state is correct before and after Open / Close.
+TEST_F(MAYBE_FileRotatingStreamTest, State) {
+  Init("FileRotatingStreamTestState", kFilePrefix, kMaxFileSize, 3);
+
+  EXPECT_EQ(SS_CLOSED, stream_->GetState());
+  ASSERT_TRUE(stream_->Open());
+  EXPECT_EQ(SS_OPEN, stream_->GetState());
+  stream_->Close();
+  EXPECT_EQ(SS_CLOSED, stream_->GetState());
+}
+
+// Tests that nothing is written to file when data of length zero is written.
+TEST_F(MAYBE_FileRotatingStreamTest, EmptyWrite) {
+  Init("FileRotatingStreamTestEmptyWrite", kFilePrefix, kMaxFileSize, 3);
+
+  ASSERT_TRUE(stream_->Open());
+  WriteAndFlush("a", 0);
+
+  std::string logfile_path = stream_->GetFilePath(0);
+  FileStream stream;
+  ASSERT_TRUE(stream.Open(logfile_path, "r", nullptr));
+  size_t file_size = 0;
+  EXPECT_TRUE(stream.GetSize(&file_size));
+  EXPECT_EQ(0u, file_size);
+}
+
+// Tests that a write operation followed by a read returns the expected data
+// and writes to the expected files.
+TEST_F(MAYBE_FileRotatingStreamTest, WriteAndRead) {
+  Init("FileRotatingStreamTestWriteAndRead", kFilePrefix, kMaxFileSize, 3);
+
+  ASSERT_TRUE(stream_->Open());
+  // The test is set up to create three log files of length 2. Write and check
+  // contents.
+  std::string messages[3] = {"aa", "bb", "cc"};
+  for (size_t i = 0; i < arraysize(messages); ++i) {
+    const std::string& message = messages[i];
+    WriteAndFlush(message.c_str(), message.size());
+    // Since the max log size is 2, we will be causing rotation. Read from the
+    // next file.
+    VerifyFileContents(message.c_str(), message.size(),
+                       stream_->GetFilePath(1));
+  }
+  // Check that exactly three files exist.
+  for (size_t i = 0; i < arraysize(messages); ++i) {
+    EXPECT_TRUE(Filesystem::IsFile(stream_->GetFilePath(i)));
+  }
+  std::string message("d");
+  WriteAndFlush(message.c_str(), message.size());
+  for (size_t i = 0; i < arraysize(messages); ++i) {
+    EXPECT_TRUE(Filesystem::IsFile(stream_->GetFilePath(i)));
+  }
+  // TODO(tkchin): Maybe check all the files in the dir.
+
+  // Reopen for read.
+  std::string expected_contents("bbccd");
+  VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
+                   dir_path_, kFilePrefix);
+}
+
+// Tests that writing data greater than the total capacity of the files
+// overwrites the files correctly and is read correctly after.
+TEST_F(MAYBE_FileRotatingStreamTest, WriteOverflowAndRead) {
+  Init("FileRotatingStreamTestWriteOverflowAndRead", kFilePrefix, kMaxFileSize,
+       3);
+  ASSERT_TRUE(stream_->Open());
+  // This should cause overflow across all three files, such that the first file
+  // we wrote to also gets overwritten.
+  std::string message("foobarbaz");
+  WriteAndFlush(message.c_str(), message.size());
+  std::string expected_file_contents("z");
+  VerifyFileContents(expected_file_contents.c_str(),
+                     expected_file_contents.size(), stream_->GetFilePath(0));
+  std::string expected_stream_contents("arbaz");
+  VerifyStreamRead(expected_stream_contents.c_str(),
+                   expected_stream_contents.size(), dir_path_, kFilePrefix);
+}
+
+// Tests that the returned file paths have the right folder and prefix.
+TEST_F(MAYBE_FileRotatingStreamTest, GetFilePath) {
+  Init("FileRotatingStreamTestGetFilePath", kFilePrefix, kMaxFileSize, 20);
+  for (auto i = 0; i < 20; ++i) {
+    Pathname path(stream_->GetFilePath(i));
+    EXPECT_EQ(0, path.folder().compare(dir_path_));
+    EXPECT_EQ(0, path.filename().compare(0, strlen(kFilePrefix), kFilePrefix));
+  }
+}
+
+#if defined (WEBRTC_ANDROID)
+// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
+#define MAYBE_CallSessionFileRotatingStreamTest \
+    DISABLED_CallSessionFileRotatingStreamTest
+#else
+#define MAYBE_CallSessionFileRotatingStreamTest \
+    CallSessionFileRotatingStreamTest
+#endif
+
+class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test {
+ protected:
+  void Init(const std::string& dir_name, size_t max_total_log_size) {
+    dir_path_ = webrtc::test::OutputPath();
+
+    // Append per-test output path in order to run within gtest parallel.
+    dir_path_.append(dir_name);
+    dir_path_.push_back(Pathname::DefaultFolderDelimiter());
+    ASSERT_TRUE(webrtc::test::CreateDir(dir_path_));
+    stream_.reset(
+        new CallSessionFileRotatingStream(dir_path_, max_total_log_size));
+  }
+
+  virtual void TearDown() {
+    // On windows, open files can't be removed.
+    stream_->Close();
+    CleanupLogDirectory(*stream_);
+    EXPECT_TRUE(webrtc::test::RemoveDir(dir_path_));
+
+    stream_.reset();
+  }
+
+  // Writes the data to the stream and flushes it.
+  void WriteAndFlush(const void* data, const size_t data_len) {
+    EXPECT_EQ(SR_SUCCESS, stream_->WriteAll(data, data_len, nullptr, nullptr));
+    EXPECT_TRUE(stream_->Flush());
+  }
+
+  // Checks that the stream reads in the expected contents and then returns an
+  // end of stream result.
+  void VerifyStreamRead(const char* expected_contents,
+                        const size_t expected_length,
+                        const std::string& dir_path) {
+    std::unique_ptr<CallSessionFileRotatingStream> stream(
+        new CallSessionFileRotatingStream(dir_path));
+    ASSERT_TRUE(stream->Open());
+    size_t read = 0;
+    size_t stream_size = 0;
+    EXPECT_TRUE(stream->GetSize(&stream_size));
+    std::unique_ptr<uint8_t[]> buffer(new uint8_t[expected_length]);
+    EXPECT_EQ(SR_SUCCESS,
+              stream->ReadAll(buffer.get(), expected_length, &read, nullptr));
+    EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length));
+    EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr));
+    EXPECT_EQ(stream_size, read);
+  }
+
+  std::unique_ptr<CallSessionFileRotatingStream> stream_;
+  std::string dir_path_;
+};
+
+// Tests that writing and reading to a stream with the smallest possible
+// capacity works.
+TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadSmallest) {
+  Init("CallSessionFileRotatingStreamTestWriteAndReadSmallest", 4);
+
+  ASSERT_TRUE(stream_->Open());
+  std::string message("abcde");
+  WriteAndFlush(message.c_str(), message.size());
+  std::string expected_contents("abe");
+  VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
+                   dir_path_);
+}
+
+// Tests that writing and reading to a stream with capacity lesser than 4MB
+// behaves correctly.
+TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadSmall) {
+  Init("CallSessionFileRotatingStreamTestWriteAndReadSmall", 8);
+
+  ASSERT_TRUE(stream_->Open());
+  std::string message("123456789");
+  WriteAndFlush(message.c_str(), message.size());
+  std::string expected_contents("1234789");
+  VerifyStreamRead(expected_contents.c_str(), expected_contents.size(),
+                   dir_path_);
+}
+
+// Tests that writing and reading to a stream with capacity greater than 4MB
+// behaves correctly.
+TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadLarge) {
+  Init("CallSessionFileRotatingStreamTestWriteAndReadLarge", 6 * 1024 * 1024);
+
+  ASSERT_TRUE(stream_->Open());
+  const size_t buffer_size = 1024 * 1024;
+  std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
+  for (int i = 0; i < 8; i++) {
+    memset(buffer.get(), i, buffer_size);
+    EXPECT_EQ(SR_SUCCESS,
+              stream_->WriteAll(buffer.get(), buffer_size, nullptr, nullptr));
+  }
+
+  stream_.reset(new CallSessionFileRotatingStream(dir_path_));
+  ASSERT_TRUE(stream_->Open());
+  std::unique_ptr<uint8_t[]> expected_buffer(new uint8_t[buffer_size]);
+  int expected_vals[] = {0, 1, 2, 6, 7};
+  for (size_t i = 0; i < arraysize(expected_vals); ++i) {
+    memset(expected_buffer.get(), expected_vals[i], buffer_size);
+    EXPECT_EQ(SR_SUCCESS,
+              stream_->ReadAll(buffer.get(), buffer_size, nullptr, nullptr));
+    EXPECT_EQ(0, memcmp(buffer.get(), expected_buffer.get(), buffer_size));
+  }
+  EXPECT_EQ(SR_EOS, stream_->ReadAll(buffer.get(), 1, nullptr, nullptr));
+}
+
+// Tests that writing and reading to a stream where only the first file is
+// written to behaves correctly.
+TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadFirstHalf) {
+  Init("CallSessionFileRotatingStreamTestWriteAndReadFirstHalf",
+       6 * 1024 * 1024);
+  ASSERT_TRUE(stream_->Open());
+  const size_t buffer_size = 1024 * 1024;
+  std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
+  for (int i = 0; i < 2; i++) {
+    memset(buffer.get(), i, buffer_size);
+    EXPECT_EQ(SR_SUCCESS,
+              stream_->WriteAll(buffer.get(), buffer_size, nullptr, nullptr));
+  }
+
+  stream_.reset(new CallSessionFileRotatingStream(dir_path_));
+  ASSERT_TRUE(stream_->Open());
+  std::unique_ptr<uint8_t[]> expected_buffer(new uint8_t[buffer_size]);
+  int expected_vals[] = {0, 1};
+  for (size_t i = 0; i < arraysize(expected_vals); ++i) {
+    memset(expected_buffer.get(), expected_vals[i], buffer_size);
+    EXPECT_EQ(SR_SUCCESS,
+              stream_->ReadAll(buffer.get(), buffer_size, nullptr, nullptr));
+    EXPECT_EQ(0, memcmp(buffer.get(), expected_buffer.get(), buffer_size));
+  }
+  EXPECT_EQ(SR_EOS, stream_->ReadAll(buffer.get(), 1, nullptr, nullptr));
+}
+
+}  // namespace rtc
diff --git a/base/fileutils.cc b/base/fileutils.cc
new file mode 100644
index 0000000..187da14
--- /dev/null
+++ b/base/fileutils.cc
@@ -0,0 +1,133 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/fileutils.h"
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/stringutils.h"
+
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32filesystem.h"
+#else
+#include "webrtc/base/unixfilesystem.h"
+#endif
+
+#if !defined(WEBRTC_WIN)
+#define MAX_PATH 260
+#endif
+
+namespace rtc {
+
+//////////////////////////
+// Directory Iterator   //
+//////////////////////////
+
+// A DirectoryIterator is created with a given directory. It originally points
+// to the first file in the directory, and can be advanecd with Next(). This
+// allows you to get information about each file.
+
+  // Constructor
+DirectoryIterator::DirectoryIterator()
+#ifdef WEBRTC_WIN
+    : handle_(INVALID_HANDLE_VALUE) {
+#else
+    : dir_(nullptr),
+      dirent_(nullptr){
+#endif
+}
+
+  // Destructor
+DirectoryIterator::~DirectoryIterator() {
+#if defined(WEBRTC_WIN)
+  if (handle_ != INVALID_HANDLE_VALUE)
+    ::FindClose(handle_);
+#else
+  if (dir_)
+    closedir(dir_);
+#endif
+}
+
+  // Starts traversing a directory.
+  // dir is the directory to traverse
+  // returns true if the directory exists and is valid
+bool DirectoryIterator::Iterate(const Pathname &dir) {
+  directory_ = dir.pathname();
+#if defined(WEBRTC_WIN)
+  if (handle_ != INVALID_HANDLE_VALUE)
+    ::FindClose(handle_);
+  std::string d = dir.pathname() + '*';
+  handle_ = ::FindFirstFile(ToUtf16(d).c_str(), &data_);
+  if (handle_ == INVALID_HANDLE_VALUE)
+    return false;
+#else
+  if (dir_ != nullptr)
+    closedir(dir_);
+  dir_ = ::opendir(directory_.c_str());
+  if (dir_ == nullptr)
+    return false;
+  dirent_ = readdir(dir_);
+  if (dirent_ == nullptr)
+    return false;
+
+  if (::stat(std::string(directory_ + Name()).c_str(), &stat_) != 0)
+    return false;
+#endif
+  return true;
+}
+
+  // Advances to the next file
+  // returns true if there were more files in the directory.
+bool DirectoryIterator::Next() {
+#if defined(WEBRTC_WIN)
+  return ::FindNextFile(handle_, &data_) == TRUE;
+#else
+  dirent_ = ::readdir(dir_);
+  if (dirent_ == nullptr)
+    return false;
+
+  return ::stat(std::string(directory_ + Name()).c_str(), &stat_) == 0;
+#endif
+}
+
+  // returns true if the file currently pointed to is a directory
+bool DirectoryIterator::IsDirectory() const {
+#if defined(WEBRTC_WIN)
+  return (data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FALSE;
+#else
+  return S_ISDIR(stat_.st_mode);
+#endif
+}
+
+  // returns the name of the file currently pointed to
+std::string DirectoryIterator::Name() const {
+#if defined(WEBRTC_WIN)
+  return ToUtf8(data_.cFileName);
+#else
+  RTC_DCHECK(dirent_);
+  return dirent_->d_name;
+#endif
+}
+
+FilesystemInterface* Filesystem::default_filesystem_ = nullptr;
+
+FilesystemInterface *Filesystem::EnsureDefaultFilesystem() {
+  if (!default_filesystem_) {
+#if defined(WEBRTC_WIN)
+    default_filesystem_ = new Win32Filesystem();
+#else
+    default_filesystem_ = new UnixFilesystem();
+#endif
+  }
+  return default_filesystem_;
+}
+
+}  // namespace rtc
diff --git a/base/fileutils.h b/base/fileutils.h
index 18de30c..00b4d8d 100644
--- a/base/fileutils.h
+++ b/base/fileutils.h
@@ -1,4 +1,3 @@
-
 /*
  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
  *
@@ -12,9 +11,172 @@
 #ifndef WEBRTC_BASE_FILEUTILS_H_
 #define WEBRTC_BASE_FILEUTILS_H_
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/fileutils.h"
+#if !defined(WEBRTC_WIN)
+#include <dirent.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/platform_file.h"
+
+namespace rtc {
+
+class FileStream;
+class Pathname;
+
+//////////////////////////
+// Directory Iterator   //
+//////////////////////////
+
+// A DirectoryIterator is created with a given directory. It originally points
+// to the first file in the directory, and can be advanecd with Next(). This
+// allows you to get information about each file.
+
+class DirectoryIterator {
+  friend class Filesystem;
+ public:
+  // Constructor
+  DirectoryIterator();
+  // Destructor
+  virtual ~DirectoryIterator();
+
+  // Starts traversing a directory
+  // dir is the directory to traverse
+  // returns true if the directory exists and is valid
+  // The iterator will point to the first entry in the directory
+  virtual bool Iterate(const Pathname &path);
+
+  // Advances to the next file
+  // returns true if there were more files in the directory.
+  virtual bool Next();
+
+  // returns true if the file currently pointed to is a directory
+  virtual bool IsDirectory() const;
+
+  // returns the name of the file currently pointed to
+  virtual std::string Name() const;
+
+ private:
+  std::string directory_;
+#if defined(WEBRTC_WIN)
+  WIN32_FIND_DATA data_;
+  HANDLE handle_;
+#else
+  DIR *dir_;
+  struct dirent *dirent_;
+  struct stat stat_;
+#endif
+};
+
+class FilesystemInterface {
+ public:
+  virtual ~FilesystemInterface() {}
+
+  // This will attempt to delete the path located at filename.
+  // It DCHECKs and returns false if the path points to a folder or a
+  // non-existent file.
+  virtual bool DeleteFile(const Pathname &filename) = 0;
+
+  // Creates a directory. This will call itself recursively to create /foo/bar
+  // even if /foo does not exist. Returns true if the function succeeds.
+  virtual bool CreateFolder(const Pathname &pathname) = 0;
+
+  // This moves a file from old_path to new_path, where "old_path" is a
+  // plain file. This DCHECKs and returns false if old_path points to a
+  // directory, and returns true if the function succeeds.
+  virtual bool MoveFile(const Pathname &old_path, const Pathname &new_path) = 0;
+
+  // Returns true if pathname refers to a directory
+  virtual bool IsFolder(const Pathname& pathname) = 0;
+
+  // Returns true if pathname refers to a file
+  virtual bool IsFile(const Pathname& pathname) = 0;
+
+  // Returns true if pathname refers to no filesystem object, every parent
+  // directory either exists, or is also absent.
+  virtual bool IsAbsent(const Pathname& pathname) = 0;
+
+  // A folder appropriate for storing temporary files (Contents are
+  // automatically deleted when the program exits)
+  virtual bool GetTemporaryFolder(Pathname &path, bool create,
+                                  const std::string *append) = 0;
+
+  virtual std::string TempFilename(const Pathname &dir,
+                                   const std::string &prefix) = 0;
+
+  // Determines the size of the file indicated by path.
+  virtual bool GetFileSize(const Pathname& path, size_t* size) = 0;
+};
+
+class Filesystem {
+ public:
+  static FilesystemInterface *default_filesystem() {
+    RTC_DCHECK(default_filesystem_);
+    return default_filesystem_;
+  }
+
+  static void set_default_filesystem(FilesystemInterface *filesystem) {
+    default_filesystem_ = filesystem;
+  }
+
+  static FilesystemInterface *swap_default_filesystem(
+      FilesystemInterface *filesystem) {
+    FilesystemInterface *cur = default_filesystem_;
+    default_filesystem_ = filesystem;
+    return cur;
+  }
+
+  static bool CreateFolder(const Pathname &pathname) {
+    return EnsureDefaultFilesystem()->CreateFolder(pathname);
+  }
+
+  static bool DeleteFile(const Pathname &filename) {
+    return EnsureDefaultFilesystem()->DeleteFile(filename);
+  }
+
+  static bool MoveFile(const Pathname &old_path, const Pathname &new_path) {
+    return EnsureDefaultFilesystem()->MoveFile(old_path, new_path);
+  }
+
+  static bool IsFolder(const Pathname& pathname) {
+    return EnsureDefaultFilesystem()->IsFolder(pathname);
+  }
+
+  static bool IsFile(const Pathname &pathname) {
+    return EnsureDefaultFilesystem()->IsFile(pathname);
+  }
+
+  static bool IsAbsent(const Pathname &pathname) {
+    return EnsureDefaultFilesystem()->IsAbsent(pathname);
+  }
+
+  static bool GetTemporaryFolder(Pathname &path, bool create,
+                                 const std::string *append) {
+    return EnsureDefaultFilesystem()->GetTemporaryFolder(path, create, append);
+  }
+
+  static std::string TempFilename(const Pathname &dir,
+                                  const std::string &prefix) {
+    return EnsureDefaultFilesystem()->TempFilename(dir, prefix);
+  }
+
+  static bool GetFileSize(const Pathname& path, size_t* size) {
+    return EnsureDefaultFilesystem()->GetFileSize(path, size);
+  }
+
+ private:
+  static FilesystemInterface* default_filesystem_;
+
+  static FilesystemInterface *EnsureDefaultFilesystem();
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Filesystem);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_FILEUTILS_H_
diff --git a/base/fileutils_unittest.cc b/base/fileutils_unittest.cc
new file mode 100644
index 0000000..c9650e2
--- /dev/null
+++ b/base/fileutils_unittest.cc
@@ -0,0 +1,33 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/stream.h"
+
+namespace rtc {
+
+#if defined (WEBRTC_ANDROID)
+// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
+#define MAYBE_FilesystemTest DISABLED_FilesystemTest
+#else
+#define MAYBE_FilesystemTest FilesystemTest
+#endif
+
+// Make sure we can get a temp folder for the later tests.
+TEST(MAYBE_FilesystemTest, GetTemporaryFolder) {
+  Pathname path;
+  EXPECT_TRUE(Filesystem::GetTemporaryFolder(path, true, nullptr));
+}
+
+}  // namespace rtc
diff --git a/base/firewallsocketserver.cc b/base/firewallsocketserver.cc
new file mode 100644
index 0000000..4a2a404
--- /dev/null
+++ b/base/firewallsocketserver.cc
@@ -0,0 +1,271 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/firewallsocketserver.h"
+
+#include <algorithm>
+
+#include "webrtc/base/asyncsocket.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+
+namespace rtc {
+
+class FirewallSocket : public AsyncSocketAdapter {
+ public:
+  FirewallSocket(FirewallSocketServer* server, AsyncSocket* socket, int type)
+    : AsyncSocketAdapter(socket), server_(server), type_(type) {
+  }
+
+  int Bind(const SocketAddress& addr) override {
+    if (!server_->IsBindableIp(addr.ipaddr())) {
+      SetError(EINVAL);
+      return SOCKET_ERROR;
+    }
+    return AsyncSocketAdapter::Bind(addr);
+  }
+
+  int Connect(const SocketAddress& addr) override {
+    if (type_ == SOCK_STREAM) {
+      if (!server_->Check(FP_TCP, GetLocalAddress(), addr)) {
+        LOG(LS_VERBOSE) << "FirewallSocket outbound TCP connection from "
+                        << GetLocalAddress().ToSensitiveString() << " to "
+                        << addr.ToSensitiveString() << " denied";
+        // TODO: Handle this asynchronously.
+        SetError(EHOSTUNREACH);
+        return SOCKET_ERROR;
+      }
+    }
+    return AsyncSocketAdapter::Connect(addr);
+  }
+  int Send(const void* pv, size_t cb) override {
+    return SendTo(pv, cb, GetRemoteAddress());
+  }
+  int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override {
+    RTC_DCHECK(type_ == SOCK_DGRAM || type_ == SOCK_STREAM);
+    FirewallProtocol protocol = (type_ == SOCK_DGRAM) ? FP_UDP : FP_TCP;
+    if (!server_->Check(protocol, GetLocalAddress(), addr)) {
+      LOG(LS_VERBOSE) << "FirewallSocket outbound packet with type " << type_
+                      << " from " << GetLocalAddress().ToSensitiveString()
+                      << " to " << addr.ToSensitiveString() << " dropped";
+      return static_cast<int>(cb);
+    }
+    return AsyncSocketAdapter::SendTo(pv, cb, addr);
+  }
+  int Recv(void* pv, size_t cb, int64_t* timestamp) override {
+    SocketAddress addr;
+    return RecvFrom(pv, cb, &addr, timestamp);
+  }
+  int RecvFrom(void* pv,
+               size_t cb,
+               SocketAddress* paddr,
+               int64_t* timestamp) override {
+    if (type_ == SOCK_DGRAM) {
+      while (true) {
+        int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr, timestamp);
+        if (res <= 0)
+          return res;
+        if (server_->Check(FP_UDP, *paddr, GetLocalAddress()))
+          return res;
+        LOG(LS_VERBOSE) << "FirewallSocket inbound UDP packet from "
+                        << paddr->ToSensitiveString() << " to "
+                        << GetLocalAddress().ToSensitiveString() << " dropped";
+      }
+    }
+    return AsyncSocketAdapter::RecvFrom(pv, cb, paddr, timestamp);
+  }
+
+  int Listen(int backlog) override {
+    if (!server_->tcp_listen_enabled()) {
+      LOG(LS_VERBOSE) << "FirewallSocket listen attempt denied";
+      return -1;
+    }
+
+    return AsyncSocketAdapter::Listen(backlog);
+  }
+  AsyncSocket* Accept(SocketAddress* paddr) override {
+    SocketAddress addr;
+    while (AsyncSocket* sock = AsyncSocketAdapter::Accept(&addr)) {
+      if (server_->Check(FP_TCP, addr, GetLocalAddress())) {
+        if (paddr)
+          *paddr = addr;
+        return sock;
+      }
+      sock->Close();
+      delete sock;
+      LOG(LS_VERBOSE) << "FirewallSocket inbound TCP connection from "
+                      << addr.ToSensitiveString() << " to "
+                      << GetLocalAddress().ToSensitiveString() << " denied";
+    }
+    return 0;
+  }
+
+ private:
+  FirewallSocketServer* server_;
+  int type_;
+};
+
+FirewallSocketServer::FirewallSocketServer(SocketServer* server,
+                                           FirewallManager* manager,
+                                           bool should_delete_server)
+    : server_(server), manager_(manager),
+      should_delete_server_(should_delete_server),
+      udp_sockets_enabled_(true), tcp_sockets_enabled_(true),
+      tcp_listen_enabled_(true) {
+  if (manager_)
+    manager_->AddServer(this);
+}
+
+FirewallSocketServer::~FirewallSocketServer() {
+  if (manager_)
+    manager_->RemoveServer(this);
+
+  if (server_ && should_delete_server_) {
+    delete server_;
+    server_ = nullptr;
+  }
+}
+
+void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p,
+                                   FirewallDirection d,
+                                   const SocketAddress& addr) {
+  SocketAddress any;
+  if (d == FD_IN || d == FD_ANY) {
+    AddRule(allow, p, any, addr);
+  }
+  if (d == FD_OUT || d == FD_ANY) {
+    AddRule(allow, p, addr, any);
+  }
+}
+
+
+void FirewallSocketServer::AddRule(bool allow, FirewallProtocol p,
+                                   const SocketAddress& src,
+                                   const SocketAddress& dst) {
+  Rule r;
+  r.allow = allow;
+  r.p = p;
+  r.src = src;
+  r.dst = dst;
+  CritScope scope(&crit_);
+  rules_.push_back(r);
+}
+
+void FirewallSocketServer::ClearRules() {
+  CritScope scope(&crit_);
+  rules_.clear();
+}
+
+bool FirewallSocketServer::Check(FirewallProtocol p,
+                                 const SocketAddress& src,
+                                 const SocketAddress& dst) {
+  CritScope scope(&crit_);
+  for (size_t i = 0; i < rules_.size(); ++i) {
+    const Rule& r = rules_[i];
+    if ((r.p != p) && (r.p != FP_ANY))
+      continue;
+    if ((r.src.ipaddr() != src.ipaddr()) && !r.src.IsNil())
+      continue;
+    if ((r.src.port() != src.port()) && (r.src.port() != 0))
+      continue;
+    if ((r.dst.ipaddr() != dst.ipaddr()) && !r.dst.IsNil())
+      continue;
+    if ((r.dst.port() != dst.port()) && (r.dst.port() != 0))
+      continue;
+    return r.allow;
+  }
+  return true;
+}
+
+void FirewallSocketServer::SetUnbindableIps(
+    const std::vector<rtc::IPAddress>& unbindable_ips) {
+  unbindable_ips_ = unbindable_ips;
+}
+
+bool FirewallSocketServer::IsBindableIp(const rtc::IPAddress& ip) {
+  return std::find(unbindable_ips_.begin(), unbindable_ips_.end(), ip) ==
+         unbindable_ips_.end();
+}
+
+Socket* FirewallSocketServer::CreateSocket(int type) {
+  return CreateSocket(AF_INET, type);
+}
+
+Socket* FirewallSocketServer::CreateSocket(int family, int type) {
+  return WrapSocket(server_->CreateAsyncSocket(family, type), type);
+}
+
+AsyncSocket* FirewallSocketServer::CreateAsyncSocket(int type) {
+  return CreateAsyncSocket(AF_INET, type);
+}
+
+AsyncSocket* FirewallSocketServer::CreateAsyncSocket(int family, int type) {
+  return WrapSocket(server_->CreateAsyncSocket(family, type), type);
+}
+
+void FirewallSocketServer::SetMessageQueue(MessageQueue* queue) {
+  server_->SetMessageQueue(queue);
+}
+
+bool FirewallSocketServer::Wait(int cms, bool process_io) {
+  return server_->Wait(cms, process_io);
+}
+
+void FirewallSocketServer::WakeUp() {
+  return server_->WakeUp();
+}
+
+AsyncSocket* FirewallSocketServer::WrapSocket(AsyncSocket* sock, int type) {
+  if (!sock ||
+      (type == SOCK_STREAM && !tcp_sockets_enabled_) ||
+      (type == SOCK_DGRAM && !udp_sockets_enabled_)) {
+    LOG(LS_VERBOSE) << "FirewallSocketServer socket creation denied";
+    delete sock;
+    return nullptr;
+  }
+  return new FirewallSocket(this, sock, type);
+}
+
+FirewallManager::FirewallManager() {
+}
+
+FirewallManager::~FirewallManager() {
+  RTC_DCHECK(servers_.empty());
+}
+
+void FirewallManager::AddServer(FirewallSocketServer* server) {
+  CritScope scope(&crit_);
+  servers_.push_back(server);
+}
+
+void FirewallManager::RemoveServer(FirewallSocketServer* server) {
+  CritScope scope(&crit_);
+  servers_.erase(std::remove(servers_.begin(), servers_.end(), server),
+                 servers_.end());
+}
+
+void FirewallManager::AddRule(bool allow, FirewallProtocol p,
+                              FirewallDirection d, const SocketAddress& addr) {
+  CritScope scope(&crit_);
+  for (std::vector<FirewallSocketServer*>::const_iterator it =
+      servers_.begin(); it != servers_.end(); ++it) {
+    (*it)->AddRule(allow, p, d, addr);
+  }
+}
+
+void FirewallManager::ClearRules() {
+  CritScope scope(&crit_);
+  for (std::vector<FirewallSocketServer*>::const_iterator it =
+      servers_.begin(); it != servers_.end(); ++it) {
+    (*it)->ClearRules();
+  }
+}
+
+}  // namespace rtc
diff --git a/base/firewallsocketserver.h b/base/firewallsocketserver.h
index 18ad9bc..21a476b 100644
--- a/base/firewallsocketserver.h
+++ b/base/firewallsocketserver.h
@@ -11,9 +11,115 @@
 #ifndef WEBRTC_BASE_FIREWALLSOCKETSERVER_H_
 #define WEBRTC_BASE_FIREWALLSOCKETSERVER_H_
 
+#include <vector>
+#include "webrtc/base/socketserver.h"
+#include "webrtc/base/criticalsection.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/firewallsocketserver.h"
+namespace rtc {
+
+class FirewallManager;
+
+// This SocketServer shim simulates a rule-based firewall server.
+
+enum FirewallProtocol { FP_UDP, FP_TCP, FP_ANY };
+enum FirewallDirection { FD_IN, FD_OUT, FD_ANY };
+
+class FirewallSocketServer : public SocketServer {
+ public:
+  FirewallSocketServer(SocketServer* server,
+                       FirewallManager* manager = nullptr,
+                       bool should_delete_server = false);
+  ~FirewallSocketServer() override;
+
+  SocketServer* socketserver() const { return server_; }
+  void set_socketserver(SocketServer* server) {
+    if (server_ && should_delete_server_) {
+      delete server_;
+      server_ = nullptr;
+      should_delete_server_ = false;
+    }
+    server_ = server;
+  }
+
+  // Settings to control whether CreateSocket or Socket::Listen succeed.
+  void set_udp_sockets_enabled(bool enabled) { udp_sockets_enabled_ = enabled; }
+  void set_tcp_sockets_enabled(bool enabled) { tcp_sockets_enabled_ = enabled; }
+  bool tcp_listen_enabled() const { return tcp_listen_enabled_; }
+  void set_tcp_listen_enabled(bool enabled) { tcp_listen_enabled_ = enabled; }
+
+  // Rules govern the behavior of Connect/Accept/Send/Recv attempts.
+  void AddRule(bool allow, FirewallProtocol p = FP_ANY,
+               FirewallDirection d = FD_ANY,
+               const SocketAddress& addr = SocketAddress());
+  void AddRule(bool allow, FirewallProtocol p,
+               const SocketAddress& src, const SocketAddress& dst);
+  void ClearRules();
+
+  bool Check(FirewallProtocol p,
+             const SocketAddress& src, const SocketAddress& dst);
+
+  // Set the IP addresses for which Bind will fail. By default this list is
+  // empty. This can be used to simulate a real OS that refuses to bind to
+  // addresses under various circumstances.
+  //
+  // No matter how many addresses are added (including INADDR_ANY), the server
+  // will still allow creating outgoing TCP connections, since they don't
+  // require explicitly binding a socket.
+  void SetUnbindableIps(const std::vector<rtc::IPAddress>& unbindable_ips);
+  bool IsBindableIp(const rtc::IPAddress& ip);
+
+  Socket* CreateSocket(int type) override;
+  Socket* CreateSocket(int family, int type) override;
+
+  AsyncSocket* CreateAsyncSocket(int type) override;
+  AsyncSocket* CreateAsyncSocket(int family, int type) override;
+
+  void SetMessageQueue(MessageQueue* queue) override;
+  bool Wait(int cms, bool process_io) override;
+  void WakeUp() override;
+
+  Socket * WrapSocket(Socket * sock, int type);
+  AsyncSocket * WrapSocket(AsyncSocket * sock, int type);
+
+ private:
+  SocketServer * server_;
+  FirewallManager * manager_;
+  CriticalSection crit_;
+  struct Rule {
+    bool allow;
+    FirewallProtocol p;
+    FirewallDirection d;
+    SocketAddress src;
+    SocketAddress dst;
+  };
+  std::vector<Rule> rules_;
+  std::vector<rtc::IPAddress> unbindable_ips_;
+  bool should_delete_server_;
+  bool udp_sockets_enabled_;
+  bool tcp_sockets_enabled_;
+  bool tcp_listen_enabled_;
+};
+
+// FirewallManager allows you to manage firewalls in multiple threads together
+
+class FirewallManager {
+ public:
+  FirewallManager();
+  ~FirewallManager();
+
+  void AddServer(FirewallSocketServer * server);
+  void RemoveServer(FirewallSocketServer * server);
+
+  void AddRule(bool allow, FirewallProtocol p = FP_ANY,
+               FirewallDirection d = FD_ANY,
+               const SocketAddress& addr = SocketAddress());
+  void ClearRules();
+
+ private:
+  CriticalSection crit_;
+  std::vector<FirewallSocketServer *> servers_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_FIREWALLSOCKETSERVER_H_
diff --git a/base/flags.cc b/base/flags.cc
new file mode 100644
index 0000000..9b7177c
--- /dev/null
+++ b/base/flags.cc
@@ -0,0 +1,299 @@
+/*
+ *  Copyright 2006 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/flags.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webrtc/base/checks.h"
+
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#include <shellapi.h>
+#endif
+
+namespace rtc {
+// -----------------------------------------------------------------------------
+// Implementation of Flag
+
+Flag::Flag(const char* file, const char* name, const char* comment,
+           Type type, void* variable, FlagValue default__)
+    : file_(file),
+      name_(name),
+      comment_(comment),
+      type_(type),
+      variable_(reinterpret_cast<FlagValue*>(variable)),
+      default_(default__) {
+  FlagList::Register(this);
+}
+
+
+void Flag::SetToDefault() {
+  // Note that we cannot simply do '*variable_ = default_;' since
+  // flag variables are not really of type FlagValue and thus may
+  // be smaller! The FlagValue union is simply 'overlayed' on top
+  // of a flag variable for convenient access. Since union members
+  // are guarantee to be aligned at the beginning, this works.
+  switch (type_) {
+    case Flag::BOOL:
+      variable_->b = default_.b;
+      return;
+    case Flag::INT:
+      variable_->i = default_.i;
+      return;
+    case Flag::FLOAT:
+      variable_->f = default_.f;
+      return;
+    case Flag::STRING:
+      variable_->s = default_.s;
+      return;
+  }
+  FATAL() << "unreachable code";
+}
+
+
+static const char* Type2String(Flag::Type type) {
+  switch (type) {
+    case Flag::BOOL: return "bool";
+    case Flag::INT: return "int";
+    case Flag::FLOAT: return "float";
+    case Flag::STRING: return "string";
+  }
+  FATAL() << "unreachable code";
+}
+
+
+static void PrintFlagValue(Flag::Type type, FlagValue* p) {
+  switch (type) {
+    case Flag::BOOL:
+      printf("%s", (p->b ? "true" : "false"));
+      return;
+    case Flag::INT:
+      printf("%d", p->i);
+      return;
+    case Flag::FLOAT:
+      printf("%f", p->f);
+      return;
+    case Flag::STRING:
+      printf("%s", p->s);
+      return;
+  }
+  FATAL() << "unreachable code";
+}
+
+
+void Flag::Print(bool print_current_value) {
+  printf("  --%s (%s)  type: %s  default: ", name_, comment_,
+          Type2String(type_));
+  PrintFlagValue(type_, &default_);
+  if (print_current_value) {
+    printf("  current value: ");
+    PrintFlagValue(type_, variable_);
+  }
+  printf("\n");
+}
+
+
+// -----------------------------------------------------------------------------
+// Implementation of FlagList
+
+Flag* FlagList::list_ = nullptr;
+
+FlagList::FlagList() {
+  list_ = nullptr;
+}
+
+void FlagList::Print(const char* file, bool print_current_value) {
+  // Since flag registration is likely by file (= C++ file),
+  // we don't need to sort by file and still get grouped output.
+  const char* current = nullptr;
+  for (Flag* f = list_; f != nullptr; f = f->next()) {
+    if (file == nullptr || file == f->file()) {
+      if (current != f->file()) {
+        printf("Flags from %s:\n", f->file());
+        current = f->file();
+      }
+      f->Print(print_current_value);
+    }
+  }
+}
+
+
+Flag* FlagList::Lookup(const char* name) {
+  Flag* f = list_;
+  while (f != nullptr && strcmp(name, f->name()) != 0)
+    f = f->next();
+  return f;
+}
+
+
+void FlagList::SplitArgument(const char* arg,
+                             char* buffer, int buffer_size,
+                             const char** name, const char** value,
+                             bool* is_bool) {
+  *name = nullptr;
+  *value = nullptr;
+  *is_bool = false;
+
+  if (*arg == '-') {
+    // find the begin of the flag name
+    arg++;  // remove 1st '-'
+    if (*arg == '-')
+      arg++;  // remove 2nd '-'
+    if (arg[0] == 'n' && arg[1] == 'o') {
+      arg += 2;  // remove "no"
+      *is_bool = true;
+    }
+    *name = arg;
+
+    // find the end of the flag name
+    while (*arg != '\0' && *arg != '=')
+      arg++;
+
+    // get the value if any
+    if (*arg == '=') {
+      // make a copy so we can NUL-terminate flag name
+      int n = static_cast<int>(arg - *name);
+      RTC_CHECK_LT(n, buffer_size);
+      memcpy(buffer, *name, n * sizeof(char));
+      buffer[n] = '\0';
+      *name = buffer;
+      // get the value
+      *value = arg + 1;
+    }
+  }
+}
+
+
+int FlagList::SetFlagsFromCommandLine(int* argc, const char** argv,
+                                      bool remove_flags) {
+  // parse arguments
+  for (int i = 1; i < *argc; /* see below */) {
+    int j = i;  // j > 0
+    const char* arg = argv[i++];
+
+    // split arg into flag components
+    char buffer[1024];
+    const char* name;
+    const char* value;
+    bool is_bool;
+    SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);
+
+    if (name != nullptr) {
+      // lookup the flag
+      Flag* flag = Lookup(name);
+      if (flag == nullptr) {
+        fprintf(stderr, "Error: unrecognized flag %s\n", arg);
+        return j;
+      }
+
+      // if we still need a flag value, use the next argument if available
+      if (flag->type() != Flag::BOOL && value == nullptr) {
+        if (i < *argc) {
+          value = argv[i++];
+        } else {
+          fprintf(stderr, "Error: missing value for flag %s of type %s\n",
+            arg, Type2String(flag->type()));
+          return j;
+        }
+      }
+
+      // set the flag
+      char empty[] = { '\0' };
+      char* endp = empty;
+      switch (flag->type()) {
+        case Flag::BOOL:
+          *flag->bool_variable() = !is_bool;
+          break;
+        case Flag::INT:
+          *flag->int_variable() = strtol(value, &endp, 10);
+          break;
+        case Flag::FLOAT:
+          *flag->float_variable() = strtod(value, &endp);
+          break;
+        case Flag::STRING:
+          *flag->string_variable() = value;
+          break;
+      }
+
+      // handle errors
+      if ((flag->type() == Flag::BOOL && value != nullptr) ||
+          (flag->type() != Flag::BOOL && is_bool) || *endp != '\0') {
+        fprintf(stderr, "Error: illegal value for flag %s of type %s\n",
+          arg, Type2String(flag->type()));
+        return j;
+      }
+
+      // remove the flag & value from the command
+      if (remove_flags)
+        while (j < i)
+          argv[j++] = nullptr;
+    }
+  }
+
+  // shrink the argument list
+  if (remove_flags) {
+    int j = 1;
+    for (int i = 1; i < *argc; i++) {
+      if (argv[i] != nullptr)
+        argv[j++] = argv[i];
+    }
+    *argc = j;
+  }
+
+  // parsed all flags successfully
+  return 0;
+}
+
+void FlagList::Register(Flag* flag) {
+  RTC_DCHECK(flag);
+  RTC_DCHECK_GT(strlen(flag->name()), 0);
+  // NOTE: Don't call Lookup() within Register because it accesses the name_
+  // of other flags in list_, and if the flags are coming from two different
+  // compilation units, the initialization order between them is undefined, and
+  // this will trigger an asan initialization-order-fiasco error.
+  flag->next_ = list_;
+  list_ = flag;
+}
+
+#if defined(WEBRTC_WIN)
+WindowsCommandLineArguments::WindowsCommandLineArguments() {
+  // start by getting the command line.
+  LPTSTR command_line = ::GetCommandLine();
+   // now, convert it to a list of wide char strings.
+  LPWSTR *wide_argv = ::CommandLineToArgvW(command_line, &argc_);
+  // now allocate an array big enough to hold that many string pointers.
+  argv_ = new char*[argc_];
+
+  // iterate over the returned wide strings;
+  for(int i = 0; i < argc_; ++i) {
+    std::string s = rtc::ToUtf8(wide_argv[i], wcslen(wide_argv[i]));
+    char *buffer = new char[s.length() + 1];
+    rtc::strcpyn(buffer, s.length() + 1, s.c_str());
+
+    // make sure the argv array has the right string at this point.
+    argv_[i] = buffer;
+  }
+  LocalFree(wide_argv);
+}
+
+WindowsCommandLineArguments::~WindowsCommandLineArguments() {
+  // need to free each string in the array, and then the array.
+  for(int i = 0; i < argc_; i++) {
+    delete[] argv_[i];
+  }
+
+  delete[] argv_;
+}
+#endif  // WEBRTC_WIN
+
+}  // namespace rtc
diff --git a/base/flags.h b/base/flags.h
index 9094466..d6a871e 100644
--- a/base/flags.h
+++ b/base/flags.h
@@ -20,12 +20,249 @@
 // The implementation only relies and basic C++ functionality
 // and needs no special library or STL support.
 
-#ifndef WEBRTC_BASE_FLAGS_H_
-#define WEBRTC_BASE_FLAGS_H_
+#ifndef WEBRTC_BASE_FLAGS_H__
+#define WEBRTC_BASE_FLAGS_H__
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+
+namespace rtc {
+
+// Internal use only.
+union FlagValue {
+  // Note: Because in C++ non-bool values are silently converted into
+  // bool values ('bool b = "false";' results in b == true!), we pass
+  // and int argument to New_BOOL as this appears to be safer - sigh.
+  // In particular, it prevents the (not uncommon!) bug where a bool
+  // flag is defined via: DEFINE_bool(flag, "false", "some comment");.
+  static FlagValue New_BOOL(int b) {
+    FlagValue v;
+    v.b = (b != 0);
+    return v;
+  }
+
+  static FlagValue New_INT(int i) {
+    FlagValue v;
+    v.i = i;
+    return v;
+  }
+
+  static FlagValue New_FLOAT(float f) {
+    FlagValue v;
+    v.f = f;
+    return v;
+  }
+
+  static FlagValue New_STRING(const char* s) {
+    FlagValue v;
+    v.s = s;
+    return v;
+  }
+
+  bool b;
+  int i;
+  double f;
+  const char* s;
+};
 
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/flags.h"
+// Each flag can be accessed programmatically via a Flag object.
+class Flag {
+ public:
+  enum Type { BOOL, INT, FLOAT, STRING };
 
-#endif  // SHARED_COMMANDLINEFLAGS_FLAGS_H_
+  // Internal use only.
+  Flag(const char* file, const char* name, const char* comment,
+       Type type, void* variable, FlagValue default_);
+
+  // General flag information
+  const char* file() const  { return file_; }
+  const char* name() const  { return name_; }
+  const char* comment() const  { return comment_; }
+
+  // Flag type
+  Type type() const  { return type_; }
+
+  // Flag variables
+  bool* bool_variable() const {
+    RTC_DCHECK_EQ(BOOL, type_);
+    return &variable_->b;
+  }
+
+  int* int_variable() const {
+    RTC_DCHECK_EQ(INT, type_);
+    return &variable_->i;
+  }
+
+  double* float_variable() const {
+    RTC_DCHECK_EQ(FLOAT, type_);
+    return &variable_->f;
+  }
+
+  const char** string_variable() const {
+    RTC_DCHECK_EQ(STRING, type_);
+    return &variable_->s;
+  }
+
+  // Default values
+  bool bool_default() const {
+    RTC_DCHECK_EQ(BOOL, type_);
+    return default_.b;
+  }
+
+  int int_default() const {
+    RTC_DCHECK_EQ(INT, type_);
+    return default_.i;
+  }
+
+  double float_default() const {
+    RTC_DCHECK_EQ(FLOAT, type_);
+    return default_.f;
+  }
+
+  const char* string_default() const {
+    RTC_DCHECK_EQ(STRING, type_);
+    return default_.s;
+  }
+
+  // Resets a flag to its default value
+  void SetToDefault();
+
+  // Iteration support
+  Flag* next() const  { return next_; }
+
+  // Prints flag information. The current flag value is only printed
+  // if print_current_value is set.
+  void Print(bool print_current_value);
+
+ private:
+  const char* file_;
+  const char* name_;
+  const char* comment_;
+
+  Type type_;
+  FlagValue* variable_;
+  FlagValue default_;
+
+  Flag* next_;
+
+  friend class FlagList;  // accesses next_
+};
+
+
+// Internal use only.
+#define DEFINE_FLAG(type, c_type, name, default, comment) \
+  /* define and initialize the flag */                    \
+  c_type FLAG_##name = (default);                         \
+  /* register the flag */                                 \
+  static rtc::Flag Flag_##name(__FILE__, #name, (comment),      \
+                               rtc::Flag::type, &FLAG_##name,   \
+                               rtc::FlagValue::New_##type(default))
+
+
+// Internal use only.
+#define DECLARE_FLAG(c_type, name)              \
+  /* declare the external flag */               \
+  extern c_type FLAG_##name
+
+
+// Use the following macros to define a new flag:
+#define DEFINE_bool(name, default, comment) \
+  DEFINE_FLAG(BOOL, bool, name, default, comment)
+#define DEFINE_int(name, default, comment) \
+  DEFINE_FLAG(INT, int, name, default, comment)
+#define DEFINE_float(name, default, comment) \
+  DEFINE_FLAG(FLOAT, double, name, default, comment)
+#define DEFINE_string(name, default, comment) \
+  DEFINE_FLAG(STRING, const char*, name, default, comment)
+
+
+// Use the following macros to declare a flag defined elsewhere:
+#define DECLARE_bool(name)  DECLARE_FLAG(bool, name)
+#define DECLARE_int(name)  DECLARE_FLAG(int, name)
+#define DECLARE_float(name)  DECLARE_FLAG(double, name)
+#define DECLARE_string(name)  DECLARE_FLAG(const char*, name)
+
+
+// The global list of all flags.
+class FlagList {
+ public:
+  FlagList();
+
+  // The null-terminated list of all flags. Traverse with Flag::next().
+  static Flag* list()  { return list_; }
+
+  // If file != nullptr, prints information for all flags defined in file;
+  // otherwise prints information for all flags in all files. The current flag
+  // value is only printed if print_current_value is set.
+  static void Print(const char* file, bool print_current_value);
+
+  // Lookup a flag by name. Returns the matching flag or null.
+  static Flag* Lookup(const char* name);
+
+  // Helper function to parse flags: Takes an argument arg and splits it into
+  // a flag name and flag value (or null if they are missing). is_bool is set
+  // if the arg started with "-no" or "--no". The buffer may be used to NUL-
+  // terminate the name, it must be large enough to hold any possible name.
+  static void SplitArgument(const char* arg,
+                            char* buffer, int buffer_size,
+                            const char** name, const char** value,
+                            bool* is_bool);
+
+  // Set the flag values by parsing the command line. If remove_flags
+  // is set, the flags and associated values are removed from (argc,
+  // argv). Returns 0 if no error occurred. Otherwise, returns the
+  // argv index > 0 for the argument where an error occurred. In that
+  // case, (argc, argv) will remain unchanged indepdendent of the
+  // remove_flags value, and no assumptions about flag settings should
+  // be made.
+  //
+  // The following syntax for flags is accepted (both '-' and '--' are ok):
+  //
+  //   --flag        (bool flags only)
+  //   --noflag      (bool flags only)
+  //   --flag=value  (non-bool flags only, no spaces around '=')
+  //   --flag value  (non-bool flags only)
+  static int SetFlagsFromCommandLine(int* argc,
+                                     const char** argv,
+                                     bool remove_flags);
+  static inline int SetFlagsFromCommandLine(int* argc,
+                                            char** argv,
+                                            bool remove_flags) {
+    return SetFlagsFromCommandLine(argc, const_cast<const char**>(argv),
+                                   remove_flags);
+  }
+
+  // Registers a new flag. Called during program initialization. Not
+  // thread-safe.
+  static void Register(Flag* flag);
+
+ private:
+  static Flag* list_;
+};
+
+#if defined(WEBRTC_WIN)
+// A helper class to translate Windows command line arguments into UTF8,
+// which then allows us to just pass them to the flags system.
+// This encapsulates all the work of getting the command line and translating
+// it to an array of 8-bit strings; all you have to do is create one of these,
+// and then call argc() and argv().
+class WindowsCommandLineArguments {
+ public:
+  WindowsCommandLineArguments();
+  ~WindowsCommandLineArguments();
+
+  int argc() { return argc_; }
+  char **argv() { return argv_; }
+ private:
+  int argc_;
+  char **argv_;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(WindowsCommandLineArguments);
+};
+#endif  // WEBRTC_WIN
+
+}  // namespace rtc
+
+#endif  // SHARED_COMMANDLINEFLAGS_FLAGS_H__
diff --git a/base/format_macros.h b/base/format_macros.h
index 844e71e..90f86a6 100644
--- a/base/format_macros.h
+++ b/base/format_macros.h
@@ -11,9 +11,86 @@
 #ifndef WEBRTC_BASE_FORMAT_MACROS_H_
 #define WEBRTC_BASE_FORMAT_MACROS_H_
 
+// This file defines the format macros for some integer types and is derived
+// from Chromium's base/format_macros.h.
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/format_macros.h"
+// To print a 64-bit value in a portable way:
+//   int64_t value;
+//   printf("xyz:%" PRId64, value);
+// The "d" in the macro corresponds to %d; you can also use PRIu64 etc.
+//
+// To print a size_t value in a portable way:
+//   size_t size;
+//   printf("xyz: %" PRIuS, size);
+// The "u" in the macro corresponds to %u, and S is for "size".
+
+#include "webrtc/typedefs.h"
+
+#if defined(WEBRTC_POSIX)
+
+#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
+#error "inttypes.h has already been included before this header file, but "
+#error "without __STDC_FORMAT_MACROS defined."
+#endif
+
+#if !defined(__STDC_FORMAT_MACROS)
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include <inttypes.h>
+
+#if !defined(PRIuS)
+#define PRIuS "zu"
+#endif
+
+// The size of NSInteger and NSUInteger varies between 32-bit and 64-bit
+// architectures and Apple does not provides standard format macros and
+// recommends casting. This has many drawbacks, so instead define macros
+// for formatting those types.
+#if defined(WEBRTC_MAC)
+#if defined(WEBRTC_ARCH_64_BITS)
+#if !defined(PRIdNS)
+#define PRIdNS "ld"
+#endif
+#if !defined(PRIuNS)
+#define PRIuNS "lu"
+#endif
+#if !defined(PRIxNS)
+#define PRIxNS "lx"
+#endif
+#else  // defined(WEBRTC_ARCH_64_BITS)
+#if !defined(PRIdNS)
+#define PRIdNS "d"
+#endif
+#if !defined(PRIuNS)
+#define PRIuNS "u"
+#endif
+#if !defined(PRIxNS)
+#define PRIxNS "x"
+#endif
+#endif
+#endif  // defined(WEBRTC_MAC)
+
+#else  // WEBRTC_WIN
+
+#include <inttypes.h>
+
+#if !defined(PRId64)
+#define PRId64 "I64d"
+#endif
+
+#if !defined(PRIu64)
+#define PRIu64 "I64u"
+#endif
+
+#if !defined(PRIx64)
+#define PRIx64 "I64x"
+#endif
+
+#if !defined(PRIuS)
+#define PRIuS "Iu"
+#endif
+
+#endif
 
 #endif  // WEBRTC_BASE_FORMAT_MACROS_H_
diff --git a/base/function_view.h b/base/function_view.h
index 1230026..861bccf 100644
--- a/base/function_view.h
+++ b/base/function_view.h
@@ -11,9 +11,120 @@
 #ifndef WEBRTC_BASE_FUNCTION_VIEW_H_
 #define WEBRTC_BASE_FUNCTION_VIEW_H_
 
+#include <type_traits>
+#include <utility>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/function_view.h"
+#include "webrtc/base/checks.h"
+
+// Just like std::function, FunctionView will wrap any callable and hide its
+// actual type, exposing only its signature. But unlike std::function,
+// FunctionView doesn't own its callable---it just points to it. Thus, it's a
+// good choice mainly as a function argument when the callable argument will
+// not be called again once the function has returned.
+//
+// Its constructors are implicit, so that callers won't have to convert lambdas
+// and other callables to FunctionView<Blah(Blah, Blah)> explicitly. This is
+// safe because FunctionView is only a reference to the real callable.
+//
+// Example use:
+//
+//   void SomeFunction(rtc::FunctionView<int(int)> index_transform);
+//   ...
+//   SomeFunction([](int i) { return 2 * i + 1; });
+//
+// Note: FunctionView is tiny (essentially just two pointers) and trivially
+// copyable, so it's probably cheaper to pass it by value than by const
+// reference.
+
+namespace rtc {
+
+template <typename T>
+class FunctionView;  // Undefined.
+
+template <typename RetT, typename... ArgT>
+class FunctionView<RetT(ArgT...)> final {
+ public:
+  // Constructor for lambdas and other callables; it accepts every type of
+  // argument except those noted in its enable_if call.
+  template <
+      typename F,
+      typename std::enable_if<
+          // Not for function pointers; we have another constructor for that
+          // below.
+          !std::is_function<typename std::remove_pointer<
+              typename std::remove_reference<F>::type>::type>::value &&
+
+          // Not for nullptr; we have another constructor for that below.
+          !std::is_same<std::nullptr_t,
+                        typename std::remove_cv<F>::type>::value &&
+
+          // Not for FunctionView objects; we have another constructor for that
+          // (the implicitly declared copy constructor).
+          !std::is_same<FunctionView,
+                        typename std::remove_cv<typename std::remove_reference<
+                            F>::type>::type>::value>::type* = nullptr>
+  FunctionView(F&& f)
+      : call_(CallVoidPtr<typename std::remove_reference<F>::type>) {
+    f_.void_ptr = &f;
+  }
+
+  // Constructor that accepts function pointers. If the argument is null, the
+  // result is an empty FunctionView.
+  template <
+      typename F,
+      typename std::enable_if<std::is_function<typename std::remove_pointer<
+          typename std::remove_reference<F>::type>::type>::value>::type* =
+          nullptr>
+  FunctionView(F&& f)
+      : call_(f ? CallFunPtr<typename std::remove_pointer<F>::type> : nullptr) {
+    f_.fun_ptr = reinterpret_cast<void (*)()>(f);
+  }
+
+  // Constructor that accepts nullptr. It creates an empty FunctionView.
+  template <typename F,
+            typename std::enable_if<std::is_same<
+                std::nullptr_t,
+                typename std::remove_cv<F>::type>::value>::type* = nullptr>
+  FunctionView(F&& f) : call_(nullptr) {}
+
+  // Default constructor. Creates an empty FunctionView.
+  FunctionView() : call_(nullptr) {}
+
+  RetT operator()(ArgT... args) const {
+    RTC_DCHECK(call_);
+    return call_(f_, std::forward<ArgT>(args)...);
+  }
+
+  // Returns true if we have a function, false if we don't (i.e., we're null).
+  explicit operator bool() const { return !!call_; }
+
+ private:
+  union VoidUnion {
+    void* void_ptr;
+    void (*fun_ptr)();
+  };
+
+  template <typename F>
+  static RetT CallVoidPtr(VoidUnion vu, ArgT... args) {
+    return (*static_cast<F*>(vu.void_ptr))(std::forward<ArgT>(args)...);
+  }
+  template <typename F>
+  static RetT CallFunPtr(VoidUnion vu, ArgT... args) {
+    return (reinterpret_cast<typename std::add_pointer<F>::type>(vu.fun_ptr))(
+        std::forward<ArgT>(args)...);
+  }
+
+  // A pointer to the callable thing, with type information erased. It's a
+  // union because we have to use separate types depending on if the callable
+  // thing is a function pointer or something else.
+  VoidUnion f_;
+
+  // Pointer to a dispatch function that knows the type of the callable thing
+  // that's stored in f_, and how to call it. A FunctionView object is empty
+  // (null) iff call_ is null.
+  RetT (*call_)(VoidUnion, ArgT...);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_FUNCTION_VIEW_H_
diff --git a/base/function_view_unittest.cc b/base/function_view_unittest.cc
new file mode 100644
index 0000000..c769fe6
--- /dev/null
+++ b/base/function_view_unittest.cc
@@ -0,0 +1,175 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <utility>
+
+#include "webrtc/base/function_view.h"
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+namespace {
+
+int CallWith33(rtc::FunctionView<int(int)> fv) {
+  return fv ? fv(33) : -1;
+}
+
+int Add33(int x) {
+  return x + 33;
+}
+
+}  // namespace
+
+// Test the main use case of FunctionView: implicitly converting a callable
+// argument.
+TEST(FunctionViewTest, ImplicitConversion) {
+  EXPECT_EQ(38, CallWith33([](int x) { return x + 5; }));
+  EXPECT_EQ(66, CallWith33(Add33));
+  EXPECT_EQ(-1, CallWith33(nullptr));
+}
+
+TEST(FunctionViewTest, IntIntLambdaWithoutState) {
+  auto f = [](int x) { return x + 1; };
+  EXPECT_EQ(18, f(17));
+  rtc::FunctionView<int(int)> fv(f);
+  EXPECT_TRUE(fv);
+  EXPECT_EQ(18, fv(17));
+}
+
+TEST(FunctionViewTest, IntVoidLambdaWithState) {
+  int x = 13;
+  auto f = [x]() mutable { return ++x; };
+  rtc::FunctionView<int()> fv(f);
+  EXPECT_TRUE(fv);
+  EXPECT_EQ(14, f());
+  EXPECT_EQ(15, fv());
+  EXPECT_EQ(16, f());
+  EXPECT_EQ(17, fv());
+}
+
+TEST(FunctionViewTest, IntIntFunction) {
+  rtc::FunctionView<int(int)> fv(Add33);
+  EXPECT_TRUE(fv);
+  EXPECT_EQ(50, fv(17));
+}
+
+TEST(FunctionViewTest, IntIntFunctionPointer) {
+  rtc::FunctionView<int(int)> fv(&Add33);
+  EXPECT_TRUE(fv);
+  EXPECT_EQ(50, fv(17));
+}
+
+TEST(FunctionViewTest, Null) {
+  // These two call constructors that statically construct null FunctionViews.
+  EXPECT_FALSE(rtc::FunctionView<int()>());
+  EXPECT_FALSE(rtc::FunctionView<int()>(nullptr));
+
+  // This calls the constructor for function pointers.
+  EXPECT_FALSE(rtc::FunctionView<int()>(reinterpret_cast<int(*)()>(0)));
+}
+
+// Ensure that FunctionView handles move-only arguments and return values.
+TEST(FunctionViewTest, UniquePtrPassthrough) {
+  auto f = [](std::unique_ptr<int> x) { return x; };
+  rtc::FunctionView<std::unique_ptr<int>(std::unique_ptr<int>)> fv(f);
+  std::unique_ptr<int> x(new int);
+  int* x_addr = x.get();
+  auto y = fv(std::move(x));
+  EXPECT_EQ(x_addr, y.get());
+}
+
+TEST(FunctionViewTest, CopyConstructor) {
+  auto f17 = [] { return 17; };
+  rtc::FunctionView<int()> fv1(f17);
+  rtc::FunctionView<int()> fv2(fv1);
+  EXPECT_EQ(17, fv1());
+  EXPECT_EQ(17, fv2());
+}
+
+TEST(FunctionViewTest, MoveConstructorIsCopy) {
+  auto f17 = [] { return 17; };
+  rtc::FunctionView<int()> fv1(f17);
+  rtc::FunctionView<int()> fv2(std::move(fv1));
+  EXPECT_EQ(17, fv1());
+  EXPECT_EQ(17, fv2());
+}
+
+TEST(FunctionViewTest, CopyAssignment) {
+  auto f17 = [] { return 17; };
+  rtc::FunctionView<int()> fv1(f17);
+  auto f23 = [] { return 23; };
+  rtc::FunctionView<int()> fv2(f23);
+  EXPECT_EQ(17, fv1());
+  EXPECT_EQ(23, fv2());
+  fv2 = fv1;
+  EXPECT_EQ(17, fv1());
+  EXPECT_EQ(17, fv2());
+}
+
+TEST(FunctionViewTest, MoveAssignmentIsCopy) {
+  auto f17 = [] { return 17; };
+  rtc::FunctionView<int()> fv1(f17);
+  auto f23 = [] { return 23; };
+  rtc::FunctionView<int()> fv2(f23);
+  EXPECT_EQ(17, fv1());
+  EXPECT_EQ(23, fv2());
+  fv2 = std::move(fv1);
+  EXPECT_EQ(17, fv1());
+  EXPECT_EQ(17, fv2());
+}
+
+TEST(FunctionViewTest, Swap) {
+  auto f17 = [] { return 17; };
+  rtc::FunctionView<int()> fv1(f17);
+  auto f23 = [] { return 23; };
+  rtc::FunctionView<int()> fv2(f23);
+  EXPECT_EQ(17, fv1());
+  EXPECT_EQ(23, fv2());
+  using std::swap;
+  swap(fv1, fv2);
+  EXPECT_EQ(23, fv1());
+  EXPECT_EQ(17, fv2());
+}
+
+// Ensure that when you copy-construct a FunctionView, the new object points to
+// the same function as the old one (as opposed to the new object pointing to
+// the old one).
+TEST(FunctionViewTest, CopyConstructorChaining) {
+  auto f17 = [] { return 17; };
+  rtc::FunctionView<int()> fv1(f17);
+  rtc::FunctionView<int()> fv2(fv1);
+  EXPECT_EQ(17, fv1());
+  EXPECT_EQ(17, fv2());
+  auto f23 = [] { return 23; };
+  fv1 = f23;
+  EXPECT_EQ(23, fv1());
+  EXPECT_EQ(17, fv2());
+}
+
+// Ensure that when you assign one FunctionView to another, we actually make a
+// copy (as opposed to making the second FunctionView point to the first one).
+TEST(FunctionViewTest, CopyAssignmentChaining) {
+  auto f17 = [] { return 17; };
+  rtc::FunctionView<int()> fv1(f17);
+  rtc::FunctionView<int()> fv2;
+  EXPECT_TRUE(fv1);
+  EXPECT_EQ(17, fv1());
+  EXPECT_FALSE(fv2);
+  fv2 = fv1;
+  EXPECT_EQ(17, fv1());
+  EXPECT_EQ(17, fv2());
+  auto f23 = [] { return 23; };
+  fv1 = f23;
+  EXPECT_EQ(23, fv1());
+  EXPECT_EQ(17, fv2());
+}
+
+}  // namespace rtc
diff --git a/base/gtest_prod_util.h b/base/gtest_prod_util.h
index 0c25943..f0cb114 100644
--- a/base/gtest_prod_util.h
+++ b/base/gtest_prod_util.h
@@ -11,9 +11,28 @@
 #ifndef WEBRTC_BASE_GTEST_PROD_UTIL_H_
 #define WEBRTC_BASE_GTEST_PROD_UTIL_H_
 
+// Define our own version of FRIEND_TEST here rather than including
+// gtest_prod.h to avoid depending on any part of GTest in production code.
+#define FRIEND_TEST_WEBRTC(test_case_name, test_name)\
+friend class test_case_name##_##test_name##_Test
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/gtest_prod_util.h"
+// This file is a plain copy of Chromium's base/gtest_prod_util.h.
+//
+// This is a wrapper for gtest's FRIEND_TEST macro that friends
+// test with all possible prefixes. This is very helpful when changing the test
+// prefix, because the friend declarations don't need to be updated.
+//
+// Example usage:
+//
+// class MyClass {
+//  private:
+//   void MyMethod();
+//   FRIEND_TEST_ALL_PREFIXES(MyClassTest, MyMethod);
+// };
+#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \
+  FRIEND_TEST_WEBRTC(test_case_name, test_name); \
+  FRIEND_TEST_WEBRTC(test_case_name, DISABLED_##test_name); \
+  FRIEND_TEST_WEBRTC(test_case_name, FLAKY_##test_name); \
+  FRIEND_TEST_WEBRTC(test_case_name, FAILS_##test_name)
 
 #endif  // WEBRTC_BASE_GTEST_PROD_UTIL_H_
diff --git a/base/gunit.h b/base/gunit.h
index d6c092e..10258c7 100644
--- a/base/gunit.h
+++ b/base/gunit.h
@@ -11,9 +11,140 @@
 #ifndef WEBRTC_BASE_GUNIT_H_
 #define WEBRTC_BASE_GUNIT_H_
 
+#include "webrtc/base/fakeclock.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/thread.h"
+#if defined(GTEST_RELATIVE_PATH)
+#include "webrtc/test/gtest.h"
+#else
+#include "testing/base/public/gunit.h"
+#endif
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/gunit.h"
+// Wait until "ex" is true, or "timeout" expires.
+#define WAIT(ex, timeout)                                       \
+  for (int64_t start = rtc::SystemTimeMillis();                 \
+       !(ex) && rtc::SystemTimeMillis() < start + (timeout);) { \
+    rtc::Thread::Current()->ProcessMessages(0);                 \
+    rtc::Thread::Current()->SleepMs(1);                         \
+  }
+
+// This returns the result of the test in res, so that we don't re-evaluate
+// the expression in the XXXX_WAIT macros below, since that causes problems
+// when the expression is only true the first time you check it.
+#define WAIT_(ex, timeout, res)                                   \
+  do {                                                            \
+    int64_t start = rtc::SystemTimeMillis();                      \
+    res = (ex);                                                   \
+    while (!res && rtc::SystemTimeMillis() < start + (timeout)) { \
+      rtc::Thread::Current()->ProcessMessages(0);                 \
+      rtc::Thread::Current()->SleepMs(1);                         \
+      res = (ex);                                                 \
+    }                                                             \
+  } while (0)
+
+// The typical EXPECT_XXXX and ASSERT_XXXXs, but done until true or a timeout.
+#define EXPECT_TRUE_WAIT(ex, timeout) \
+  do { \
+    bool res; \
+    WAIT_(ex, timeout, res); \
+    if (!res) EXPECT_TRUE(ex); \
+  } while (0)
+
+#define EXPECT_EQ_WAIT(v1, v2, timeout) \
+  do { \
+    bool res; \
+    WAIT_(v1 == v2, timeout, res); \
+    if (!res) EXPECT_EQ(v1, v2); \
+  } while (0)
+
+#define ASSERT_TRUE_WAIT(ex, timeout) \
+  do { \
+    bool res; \
+    WAIT_(ex, timeout, res); \
+    if (!res) ASSERT_TRUE(ex); \
+  } while (0)
+
+#define ASSERT_EQ_WAIT(v1, v2, timeout) \
+  do { \
+    bool res; \
+    WAIT_(v1 == v2, timeout, res); \
+    if (!res) ASSERT_EQ(v1, v2); \
+  } while (0)
+
+// Version with a "soft" timeout and a margin. This logs if the timeout is
+// exceeded, but it only fails if the expression still isn't true after the
+// margin time passes.
+#define EXPECT_TRUE_WAIT_MARGIN(ex, timeout, margin)                       \
+  do {                                                                     \
+    bool res;                                                              \
+    WAIT_(ex, timeout, res);                                               \
+    if (res) {                                                             \
+      break;                                                               \
+    }                                                                      \
+    LOG(LS_WARNING) << "Expression " << #ex << " still not true after "    \
+                    << (timeout) << "ms; waiting an additional " << margin \
+                    << "ms";                                               \
+    WAIT_(ex, margin, res);                                                \
+    if (!res) {                                                            \
+      EXPECT_TRUE(ex);                                                     \
+    }                                                                      \
+  } while (0)
+
+// Wait until "ex" is true, or "timeout" expires, using fake clock where
+// messages are processed every millisecond.
+// TODO(pthatcher): Allow tests to control how many milliseconds to advance.
+#define SIMULATED_WAIT(ex, timeout, clock)                    \
+  for (int64_t start = rtc::TimeMillis();                     \
+       !(ex) && rtc::TimeMillis() < start + (timeout);) {     \
+    (clock).AdvanceTime(rtc::TimeDelta::FromMilliseconds(1)); \
+  }
+
+// This returns the result of the test in res, so that we don't re-evaluate
+// the expression in the XXXX_WAIT macros below, since that causes problems
+// when the expression is only true the first time you check it.
+#define SIMULATED_WAIT_(ex, timeout, res, clock)                \
+  do {                                                          \
+    int64_t start = rtc::TimeMillis();                          \
+    res = (ex);                                                 \
+    while (!res && rtc::TimeMillis() < start + (timeout)) {     \
+      (clock).AdvanceTime(rtc::TimeDelta::FromMilliseconds(1)); \
+      res = (ex);                                               \
+    }                                                           \
+  } while (0)
+
+// The typical EXPECT_XXXX, but done until true or a timeout with a fake clock.
+#define EXPECT_TRUE_SIMULATED_WAIT(ex, timeout, clock) \
+  do {                                                 \
+    bool res;                                          \
+    SIMULATED_WAIT_(ex, timeout, res, clock);          \
+    if (!res) {                                        \
+      EXPECT_TRUE(ex);                                 \
+    }                                                  \
+  } while (0)
+
+#define EXPECT_EQ_SIMULATED_WAIT(v1, v2, timeout, clock) \
+  do {                                                   \
+    bool res;                                            \
+    SIMULATED_WAIT_(v1 == v2, timeout, res, clock);      \
+    if (!res) {                                          \
+      EXPECT_EQ(v1, v2);                                 \
+    }                                                    \
+  } while (0)
+
+#define ASSERT_TRUE_SIMULATED_WAIT(ex, timeout, clock) \
+  do {                                                 \
+    bool res;                                          \
+    SIMULATED_WAIT_(ex, timeout, res, clock);          \
+    if (!res)                                          \
+      ASSERT_TRUE(ex);                                 \
+  } while (0)
+
+#define ASSERT_EQ_SIMULATED_WAIT(v1, v2, timeout, clock) \
+  do {                                                   \
+    bool res;                                            \
+    SIMULATED_WAIT_(v1 == v2, timeout, res, clock);      \
+    if (!res)                                            \
+      ASSERT_EQ(v1, v2);                                 \
+  } while (0)
 
 #endif  // WEBRTC_BASE_GUNIT_H_
diff --git a/base/gunit_prod.h b/base/gunit_prod.h
index 436abee..dc39bbd 100644
--- a/base/gunit_prod.h
+++ b/base/gunit_prod.h
@@ -11,8 +11,14 @@
 #ifndef WEBRTC_BASE_GUNIT_PROD_H_
 #define WEBRTC_BASE_GUNIT_PROD_H_
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/gunit_prod.h"
+#if defined(WEBRTC_ANDROID)
+// Android doesn't use gtest at all, so anything that relies on gtest should
+// check this define first.
+#define NO_GTEST
+#elif defined (GTEST_RELATIVE_PATH)
+#include "gtest/gtest_prod.h"
+#else
+#include "testing/base/gunit_prod.h"
+#endif
 
 #endif  // WEBRTC_BASE_GUNIT_PROD_H_
diff --git a/base/helpers.cc b/base/helpers.cc
new file mode 100644
index 0000000..aa6a6ae
--- /dev/null
+++ b/base/helpers.cc
@@ -0,0 +1,218 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/helpers.h"
+
+#include <limits>
+#include <memory>
+
+#include <openssl/rand.h>
+
+#include "webrtc/base/base64.h"
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/timeutils.h"
+
+// Protect against max macro inclusion.
+#undef max
+
+namespace rtc {
+
+// Base class for RNG implementations.
+class RandomGenerator {
+ public:
+  virtual ~RandomGenerator() {}
+  virtual bool Init(const void* seed, size_t len) = 0;
+  virtual bool Generate(void* buf, size_t len) = 0;
+};
+
+// The OpenSSL RNG.
+class SecureRandomGenerator : public RandomGenerator {
+ public:
+  SecureRandomGenerator() {}
+  ~SecureRandomGenerator() override {}
+  bool Init(const void* seed, size_t len) override { return true; }
+  bool Generate(void* buf, size_t len) override {
+    return (RAND_bytes(reinterpret_cast<unsigned char*>(buf), len) > 0);
+  }
+};
+
+// A test random generator, for predictable output.
+class TestRandomGenerator : public RandomGenerator {
+ public:
+  TestRandomGenerator() : seed_(7) {
+  }
+  ~TestRandomGenerator() override {
+  }
+  bool Init(const void* seed, size_t len) override { return true; }
+  bool Generate(void* buf, size_t len) override {
+    for (size_t i = 0; i < len; ++i) {
+      static_cast<uint8_t*>(buf)[i] = static_cast<uint8_t>(GetRandom());
+    }
+    return true;
+  }
+
+ private:
+  int GetRandom() {
+    return ((seed_ = seed_ * 214013L + 2531011L) >> 16) & 0x7fff;
+  }
+  int seed_;
+};
+
+namespace {
+
+// TODO: Use Base64::Base64Table instead.
+static const char kBase64[64] = {
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
+
+static const char kHex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                              '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+static const char kUuidDigit17[4] = {'8', '9', 'a', 'b'};
+
+// This round about way of creating a global RNG is to safe-guard against
+// indeterminant static initialization order.
+std::unique_ptr<RandomGenerator>& GetGlobalRng() {
+  RTC_DEFINE_STATIC_LOCAL(std::unique_ptr<RandomGenerator>, global_rng,
+                          (new SecureRandomGenerator()));
+  return global_rng;
+}
+
+RandomGenerator& Rng() {
+  return *GetGlobalRng();
+}
+
+}  // namespace
+
+void SetRandomTestMode(bool test) {
+  if (!test) {
+    GetGlobalRng().reset(new SecureRandomGenerator());
+  } else {
+    GetGlobalRng().reset(new TestRandomGenerator());
+  }
+}
+
+bool InitRandom(int seed) {
+  return InitRandom(reinterpret_cast<const char*>(&seed), sizeof(seed));
+}
+
+bool InitRandom(const char* seed, size_t len) {
+  if (!Rng().Init(seed, len)) {
+    LOG(LS_ERROR) << "Failed to init random generator!";
+    return false;
+  }
+  return true;
+}
+
+std::string CreateRandomString(size_t len) {
+  std::string str;
+  RTC_CHECK(CreateRandomString(len, &str));
+  return str;
+}
+
+static bool CreateRandomString(size_t len,
+                        const char* table, int table_size,
+                        std::string* str) {
+  str->clear();
+  // Avoid biased modulo division below.
+  if (256 % table_size) {
+    LOG(LS_ERROR) << "Table size must divide 256 evenly!";
+    return false;
+  }
+  std::unique_ptr<uint8_t[]> bytes(new uint8_t[len]);
+  if (!Rng().Generate(bytes.get(), len)) {
+    LOG(LS_ERROR) << "Failed to generate random string!";
+    return false;
+  }
+  str->reserve(len);
+  for (size_t i = 0; i < len; ++i) {
+    str->push_back(table[bytes[i] % table_size]);
+  }
+  return true;
+}
+
+bool CreateRandomString(size_t len, std::string* str) {
+  return CreateRandomString(len, kBase64, 64, str);
+}
+
+bool CreateRandomString(size_t len, const std::string& table,
+                        std::string* str) {
+  return CreateRandomString(len, table.c_str(),
+                            static_cast<int>(table.size()), str);
+}
+
+bool CreateRandomData(size_t length, std::string* data) {
+  data->resize(length);
+  // std::string is guaranteed to use contiguous memory in c++11 so we can
+  // safely write directly to it.
+  return Rng().Generate(&data->at(0), length);
+}
+
+// Version 4 UUID is of the form:
+// xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
+// Where 'x' is a hex digit, and 'y' is 8, 9, a or b.
+std::string CreateRandomUuid() {
+  std::string str;
+  std::unique_ptr<uint8_t[]> bytes(new uint8_t[31]);
+  RTC_CHECK(Rng().Generate(bytes.get(), 31));
+  str.reserve(36);
+  for (size_t i = 0; i < 8; ++i) {
+    str.push_back(kHex[bytes[i] % 16]);
+  }
+  str.push_back('-');
+  for (size_t i = 8; i < 12; ++i) {
+    str.push_back(kHex[bytes[i] % 16]);
+  }
+  str.push_back('-');
+  str.push_back('4');
+  for (size_t i = 12; i < 15; ++i) {
+    str.push_back(kHex[bytes[i] % 16]);
+  }
+  str.push_back('-');
+  str.push_back(kUuidDigit17[bytes[15] % 4]);
+  for (size_t i = 16; i < 19; ++i) {
+    str.push_back(kHex[bytes[i] % 16]);
+  }
+  str.push_back('-');
+  for (size_t i = 19; i < 31; ++i) {
+    str.push_back(kHex[bytes[i] % 16]);
+  }
+  return str;
+}
+
+uint32_t CreateRandomId() {
+  uint32_t id;
+  RTC_CHECK(Rng().Generate(&id, sizeof(id)));
+  return id;
+}
+
+uint64_t CreateRandomId64() {
+  return static_cast<uint64_t>(CreateRandomId()) << 32 | CreateRandomId();
+}
+
+uint32_t CreateRandomNonZeroId() {
+  uint32_t id;
+  do {
+    id = CreateRandomId();
+  } while (id == 0);
+  return id;
+}
+
+double CreateRandomDouble() {
+  return CreateRandomId() / (std::numeric_limits<uint32_t>::max() +
+                             std::numeric_limits<double>::epsilon());
+}
+
+}  // namespace rtc
diff --git a/base/helpers.h b/base/helpers.h
index 86a388e..fcf77af 100644
--- a/base/helpers.h
+++ b/base/helpers.h
@@ -11,9 +11,54 @@
 #ifndef WEBRTC_BASE_HELPERS_H_
 #define WEBRTC_BASE_HELPERS_H_
 
+#include <string>
+#include "webrtc/base/basictypes.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/helpers.h"
+namespace rtc {
+
+// For testing, we can return predictable data.
+void SetRandomTestMode(bool test);
+
+// Initializes the RNG, and seeds it with the specified entropy.
+bool InitRandom(int seed);
+bool InitRandom(const char* seed, size_t len);
+
+// Generates a (cryptographically) random string of the given length.
+// We generate base64 values so that they will be printable.
+std::string CreateRandomString(size_t length);
+
+// Generates a (cryptographically) random string of the given length.
+// We generate base64 values so that they will be printable.
+// Return false if the random number generator failed.
+bool CreateRandomString(size_t length, std::string* str);
+
+// Generates a (cryptographically) random string of the given length,
+// with characters from the given table. Return false if the random
+// number generator failed.
+// For ease of implementation, the function requires that the table
+// size evenly divide 256; otherwise, it returns false.
+bool CreateRandomString(size_t length, const std::string& table,
+                        std::string* str);
+
+// Generates (cryptographically) random data of the given length.
+// Return false if the random number generator failed.
+bool CreateRandomData(size_t length, std::string* data);
+
+// Generates a (cryptographically) random UUID version 4 string.
+std::string CreateRandomUuid();
+
+// Generates a random id.
+uint32_t CreateRandomId();
+
+// Generates a 64 bit random id.
+uint64_t CreateRandomId64();
+
+// Generates a random id > 0.
+uint32_t CreateRandomNonZeroId();
+
+// Generates a random double between 0.0 (inclusive) and 1.0 (exclusive).
+double CreateRandomDouble();
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_HELPERS_H_
diff --git a/base/helpers_unittest.cc b/base/helpers_unittest.cc
new file mode 100644
index 0000000..394e2b0
--- /dev/null
+++ b/base/helpers_unittest.cc
@@ -0,0 +1,111 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string>
+
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/ssladapter.h"
+
+namespace rtc {
+
+class RandomTest : public testing::Test {};
+
+TEST_F(RandomTest, TestCreateRandomId) {
+  CreateRandomId();
+}
+
+TEST_F(RandomTest, TestCreateRandomDouble) {
+  for (int i = 0; i < 100; ++i) {
+    double r = CreateRandomDouble();
+    EXPECT_GE(r, 0.0);
+    EXPECT_LT(r, 1.0);
+  }
+}
+
+TEST_F(RandomTest, TestCreateNonZeroRandomId) {
+  EXPECT_NE(0U, CreateRandomNonZeroId());
+}
+
+TEST_F(RandomTest, TestCreateRandomString) {
+  std::string random = CreateRandomString(256);
+  EXPECT_EQ(256U, random.size());
+  std::string random2;
+  EXPECT_TRUE(CreateRandomString(256, &random2));
+  EXPECT_NE(random, random2);
+  EXPECT_EQ(256U, random2.size());
+}
+
+TEST_F(RandomTest, TestCreateRandomData) {
+  static size_t kRandomDataLength = 32;
+  std::string random1;
+  std::string random2;
+  EXPECT_TRUE(CreateRandomData(kRandomDataLength, &random1));
+  EXPECT_EQ(kRandomDataLength, random1.size());
+  EXPECT_TRUE(CreateRandomData(kRandomDataLength, &random2));
+  EXPECT_EQ(kRandomDataLength, random2.size());
+  EXPECT_NE(0, memcmp(random1.data(), random2.data(), kRandomDataLength));
+}
+
+TEST_F(RandomTest, TestCreateRandomStringEvenlyDivideTable) {
+  static std::string kUnbiasedTable("01234567");
+  std::string random;
+  EXPECT_TRUE(CreateRandomString(256, kUnbiasedTable, &random));
+  EXPECT_EQ(256U, random.size());
+
+  static std::string kBiasedTable("0123456789");
+  EXPECT_FALSE(CreateRandomString(256, kBiasedTable, &random));
+  EXPECT_EQ(0U, random.size());
+}
+
+TEST_F(RandomTest, TestCreateRandomUuid) {
+  std::string random = CreateRandomUuid();
+  EXPECT_EQ(36U, random.size());
+}
+
+TEST_F(RandomTest, TestCreateRandomForTest) {
+  // Make sure we get the output we expect.
+  SetRandomTestMode(true);
+  EXPECT_EQ(2154761789U, CreateRandomId());
+  EXPECT_EQ("h0ISP4S5SJKH/9EY", CreateRandomString(16));
+  EXPECT_EQ("41706e92-cdd3-46d9-a22d-8ff1737ffb11", CreateRandomUuid());
+  static size_t kRandomDataLength = 32;
+  std::string random;
+  EXPECT_TRUE(CreateRandomData(kRandomDataLength, &random));
+  EXPECT_EQ(kRandomDataLength, random.size());
+  Buffer expected("\xbd\x52\x2a\x4b\x97\x93\x2f\x1c"
+      "\xc4\x72\xab\xa2\x88\x68\x3e\xcc"
+      "\xa3\x8d\xaf\x13\x3b\xbc\x83\xbb"
+      "\x16\xf1\xcf\x56\x0c\xf5\x4a\x8b", kRandomDataLength);
+  EXPECT_EQ(0, memcmp(expected.data(), random.data(), kRandomDataLength));
+
+  // Reset and make sure we get the same output.
+  SetRandomTestMode(true);
+  EXPECT_EQ(2154761789U, CreateRandomId());
+  EXPECT_EQ("h0ISP4S5SJKH/9EY", CreateRandomString(16));
+  EXPECT_EQ("41706e92-cdd3-46d9-a22d-8ff1737ffb11", CreateRandomUuid());
+  EXPECT_TRUE(CreateRandomData(kRandomDataLength, &random));
+  EXPECT_EQ(kRandomDataLength, random.size());
+  EXPECT_EQ(0, memcmp(expected.data(), random.data(), kRandomDataLength));
+
+  // Test different character sets.
+  SetRandomTestMode(true);
+  std::string str;
+  EXPECT_TRUE(CreateRandomString(16, "a", &str));
+  EXPECT_EQ("aaaaaaaaaaaaaaaa", str);
+  EXPECT_TRUE(CreateRandomString(16, "abcd", &str));
+  EXPECT_EQ("dbaaabdaccbcabbd", str);
+
+  // Turn off test mode for other tests.
+  SetRandomTestMode(false);
+}
+
+}  // namespace rtc
diff --git a/base/httpbase.cc b/base/httpbase.cc
new file mode 100644
index 0000000..bc8ac64
--- /dev/null
+++ b/base/httpbase.cc
@@ -0,0 +1,886 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#else  // !WEBRTC_WIN
+#define SEC_E_CERT_EXPIRED (-2146893016)
+#endif  // !WEBRTC_WIN
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/httpbase.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/socket.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+//////////////////////////////////////////////////////////////////////
+// Helpers
+//////////////////////////////////////////////////////////////////////
+
+bool MatchHeader(const char* str, size_t len, HttpHeader header) {
+  const char* const header_str = ToString(header);
+  const size_t header_len = strlen(header_str);
+  return (len == header_len) && (_strnicmp(str, header_str, header_len) == 0);
+}
+
+enum {
+  MSG_READ
+};
+
+//////////////////////////////////////////////////////////////////////
+// HttpParser
+//////////////////////////////////////////////////////////////////////
+
+HttpParser::HttpParser() {
+  reset();
+}
+
+HttpParser::~HttpParser() {
+}
+
+void
+HttpParser::reset() {
+  state_ = ST_LEADER;
+  chunked_ = false;
+  data_size_ = SIZE_UNKNOWN;
+}
+
+HttpParser::ProcessResult
+HttpParser::Process(const char* buffer, size_t len, size_t* processed,
+                    HttpError* error) {
+  *processed = 0;
+  *error = HE_NONE;
+
+  if (state_ >= ST_COMPLETE) {
+    RTC_NOTREACHED();
+    return PR_COMPLETE;
+  }
+
+  while (true) {
+    if (state_ < ST_DATA) {
+      size_t pos = *processed;
+      while ((pos < len) && (buffer[pos] != '\n')) {
+        pos += 1;
+      }
+      if (pos >= len) {
+        break;  // don't have a full header
+      }
+      const char* line = buffer + *processed;
+      size_t len = (pos - *processed);
+      *processed = pos + 1;
+      while ((len > 0) && isspace(static_cast<unsigned char>(line[len-1]))) {
+        len -= 1;
+      }
+      ProcessResult result = ProcessLine(line, len, error);
+      LOG(LS_VERBOSE) << "Processed line, result=" << result;
+
+      if (PR_CONTINUE != result) {
+        return result;
+      }
+    } else if (data_size_ == 0) {
+      if (chunked_) {
+        state_ = ST_CHUNKTERM;
+      } else {
+        return PR_COMPLETE;
+      }
+    } else {
+      size_t available = len - *processed;
+      if (available <= 0) {
+        break; // no more data
+      }
+      if ((data_size_ != SIZE_UNKNOWN) && (available > data_size_)) {
+        available = data_size_;
+      }
+      size_t read = 0;
+      ProcessResult result = ProcessData(buffer + *processed, available, read,
+                                         error);
+      LOG(LS_VERBOSE) << "Processed data, result: " << result << " read: "
+                      << read << " err: " << error;
+
+      if (PR_CONTINUE != result) {
+        return result;
+      }
+      *processed += read;
+      if (data_size_ != SIZE_UNKNOWN) {
+        data_size_ -= read;
+      }
+    }
+  }
+
+  return PR_CONTINUE;
+}
+
+HttpParser::ProcessResult
+HttpParser::ProcessLine(const char* line, size_t len, HttpError* error) {
+  LOG_F(LS_VERBOSE) << " state: " << state_ << " line: "
+                    << std::string(line, len) << " len: " << len << " err: "
+                    << error;
+
+  switch (state_) {
+  case ST_LEADER:
+    state_ = ST_HEADERS;
+    return ProcessLeader(line, len, error);
+
+  case ST_HEADERS:
+    if (len > 0) {
+      const char* value = strchrn(line, len, ':');
+      if (!value) {
+        *error = HE_PROTOCOL;
+        return PR_COMPLETE;
+      }
+      size_t nlen = (value - line);
+      const char* eol = line + len;
+      do {
+        value += 1;
+      } while ((value < eol) && isspace(static_cast<unsigned char>(*value)));
+      size_t vlen = eol - value;
+      if (MatchHeader(line, nlen, HH_CONTENT_LENGTH)) {
+        // sscanf isn't safe with strings that aren't null-terminated, and there
+        // is no guarantee that |value| is.
+        // Create a local copy that is null-terminated.
+        std::string value_str(value, vlen);
+        unsigned int temp_size;
+        if (sscanf(value_str.c_str(), "%u", &temp_size) != 1) {
+          *error = HE_PROTOCOL;
+          return PR_COMPLETE;
+        }
+        data_size_ = static_cast<size_t>(temp_size);
+      } else if (MatchHeader(line, nlen, HH_TRANSFER_ENCODING)) {
+        if ((vlen == 7) && (_strnicmp(value, "chunked", 7) == 0)) {
+          chunked_ = true;
+        } else if ((vlen == 8) && (_strnicmp(value, "identity", 8) == 0)) {
+          chunked_ = false;
+        } else {
+          *error = HE_PROTOCOL;
+          return PR_COMPLETE;
+        }
+      }
+      return ProcessHeader(line, nlen, value, vlen, error);
+    } else {
+      state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA;
+      return ProcessHeaderComplete(chunked_, data_size_, error);
+    }
+    break;
+
+  case ST_CHUNKSIZE:
+    if (len > 0) {
+      char* ptr = nullptr;
+      data_size_ = strtoul(line, &ptr, 16);
+      if (ptr != line + len) {
+        *error = HE_PROTOCOL;
+        return PR_COMPLETE;
+      }
+      state_ = (data_size_ == 0) ? ST_TRAILERS : ST_DATA;
+    } else {
+      *error = HE_PROTOCOL;
+      return PR_COMPLETE;
+    }
+    break;
+
+  case ST_CHUNKTERM:
+    if (len > 0) {
+      *error = HE_PROTOCOL;
+      return PR_COMPLETE;
+    } else {
+      state_ = chunked_ ? ST_CHUNKSIZE : ST_DATA;
+    }
+    break;
+
+  case ST_TRAILERS:
+    if (len == 0) {
+      return PR_COMPLETE;
+    }
+    // *error = onHttpRecvTrailer();
+    break;
+
+  default:
+    RTC_NOTREACHED();
+    break;
+  }
+
+  return PR_CONTINUE;
+}
+
+bool
+HttpParser::is_valid_end_of_input() const {
+  return (state_ == ST_DATA) && (data_size_ == SIZE_UNKNOWN);
+}
+
+void
+HttpParser::complete(HttpError error) {
+  if (state_ < ST_COMPLETE) {
+    state_ = ST_COMPLETE;
+    OnComplete(error);
+  }
+}
+
+//////////////////////////////////////////////////////////////////////
+// HttpBase::DocumentStream
+//////////////////////////////////////////////////////////////////////
+
+class BlockingMemoryStream : public ExternalMemoryStream {
+public:
+  BlockingMemoryStream(char* buffer, size_t size)
+  : ExternalMemoryStream(buffer, size) { }
+
+  StreamResult DoReserve(size_t size, int* error) override {
+    return (buffer_length_ >= size) ? SR_SUCCESS : SR_BLOCK;
+  }
+};
+
+class HttpBase::DocumentStream : public StreamInterface {
+public:
+  DocumentStream(HttpBase* base) : base_(base), error_(HE_DEFAULT) { }
+
+  StreamState GetState() const override {
+    if (nullptr == base_)
+      return SS_CLOSED;
+    if (HM_RECV == base_->mode_)
+      return SS_OPEN;
+    return SS_OPENING;
+  }
+
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override {
+    if (!base_) {
+      if (error) *error = error_;
+      return (HE_NONE == error_) ? SR_EOS : SR_ERROR;
+    }
+
+    if (HM_RECV != base_->mode_) {
+      return SR_BLOCK;
+    }
+
+    // DoReceiveLoop writes http document data to the StreamInterface* document
+    // member of HttpData.  In this case, we want this data to be written
+    // directly to our buffer.  To accomplish this, we wrap our buffer with a
+    // StreamInterface, and replace the existing document with our wrapper.
+    // When the method returns, we restore the old document.  Ideally, we would
+    // pass our StreamInterface* to DoReceiveLoop, but due to the callbacks
+    // of HttpParser, we would still need to store the pointer temporarily.
+    std::unique_ptr<StreamInterface> stream(
+        new BlockingMemoryStream(reinterpret_cast<char*>(buffer), buffer_len));
+
+    // Replace the existing document with our wrapped buffer.
+    base_->data_->document.swap(stream);
+
+    // Pump the I/O loop.  DoReceiveLoop is guaranteed not to attempt to
+    // complete the I/O process, which means that our wrapper is not in danger
+    // of being deleted.  To ensure this, DoReceiveLoop returns true when it
+    // wants complete to be called.  We make sure to uninstall our wrapper
+    // before calling complete().
+    HttpError http_error;
+    bool complete = base_->DoReceiveLoop(&http_error);
+
+    // Reinstall the original output document.
+    base_->data_->document.swap(stream);
+
+    // If we reach the end of the receive stream, we disconnect our stream
+    // adapter from the HttpBase, and further calls to read will either return
+    // EOS or ERROR, appropriately.  Finally, we call complete().
+    StreamResult result = SR_BLOCK;
+    if (complete) {
+      HttpBase* base = Disconnect(http_error);
+      if (error) *error = error_;
+      result = (HE_NONE == error_) ? SR_EOS : SR_ERROR;
+      base->complete(http_error);
+    }
+
+    // Even if we are complete, if some data was read we must return SUCCESS.
+    // Future Reads will return EOS or ERROR based on the error_ variable.
+    size_t position;
+    stream->GetPosition(&position);
+    if (position > 0) {
+      if (read) *read = position;
+      result = SR_SUCCESS;
+    }
+    return result;
+  }
+
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override {
+    if (error) *error = -1;
+    return SR_ERROR;
+  }
+
+  void Close() override {
+    if (base_) {
+      HttpBase* base = Disconnect(HE_NONE);
+      if (HM_RECV == base->mode_ && base->http_stream_) {
+        // Read I/O could have been stalled on the user of this DocumentStream,
+        // so restart the I/O process now that we've removed ourselves.
+        base->http_stream_->PostEvent(SE_READ, 0);
+      }
+    }
+  }
+
+  bool GetAvailable(size_t* size) const override {
+    if (!base_ || HM_RECV != base_->mode_)
+      return false;
+    size_t data_size = base_->GetDataRemaining();
+    if (SIZE_UNKNOWN == data_size)
+      return false;
+    if (size)
+      *size = data_size;
+    return true;
+  }
+
+  HttpBase* Disconnect(HttpError error) {
+    RTC_DCHECK(nullptr != base_);
+    RTC_DCHECK(nullptr != base_->doc_stream_);
+    HttpBase* base = base_;
+    base_->doc_stream_ = nullptr;
+    base_ = nullptr;
+    error_ = error;
+    return base;
+  }
+
+private:
+  HttpBase* base_;
+  HttpError error_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// HttpBase
+//////////////////////////////////////////////////////////////////////
+
+HttpBase::HttpBase()
+    : mode_(HM_NONE),
+      data_(nullptr),
+      notify_(nullptr),
+      http_stream_(nullptr),
+      doc_stream_(nullptr) {}
+
+HttpBase::~HttpBase() {
+  RTC_DCHECK(HM_NONE == mode_);
+}
+
+bool
+HttpBase::isConnected() const {
+  return (http_stream_ != nullptr) && (http_stream_->GetState() == SS_OPEN);
+}
+
+bool
+HttpBase::attach(StreamInterface* stream) {
+  if ((mode_ != HM_NONE) || (http_stream_ != nullptr) || (stream == nullptr)) {
+    RTC_NOTREACHED();
+    return false;
+  }
+  http_stream_ = stream;
+  http_stream_->SignalEvent.connect(this, &HttpBase::OnHttpStreamEvent);
+  mode_ = (http_stream_->GetState() == SS_OPENING) ? HM_CONNECT : HM_NONE;
+  return true;
+}
+
+StreamInterface*
+HttpBase::detach() {
+  RTC_DCHECK(HM_NONE == mode_);
+  if (mode_ != HM_NONE) {
+    return nullptr;
+  }
+  StreamInterface* stream = http_stream_;
+  http_stream_ = nullptr;
+  if (stream) {
+    stream->SignalEvent.disconnect(this);
+  }
+  return stream;
+}
+
+void
+HttpBase::send(HttpData* data) {
+  RTC_DCHECK(HM_NONE == mode_);
+  if (mode_ != HM_NONE) {
+    return;
+  } else if (!isConnected()) {
+    OnHttpStreamEvent(http_stream_, SE_CLOSE, HE_DISCONNECTED);
+    return;
+  }
+
+  mode_ = HM_SEND;
+  data_ = data;
+  len_ = 0;
+  ignore_data_ = chunk_data_ = false;
+
+  if (data_->document) {
+    data_->document->SignalEvent.connect(this, &HttpBase::OnDocumentEvent);
+  }
+
+  std::string encoding;
+  if (data_->hasHeader(HH_TRANSFER_ENCODING, &encoding)
+      && (encoding == "chunked")) {
+    chunk_data_ = true;
+  }
+
+  len_ = data_->formatLeader(buffer_, sizeof(buffer_));
+  len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n");
+
+  header_ = data_->begin();
+  if (header_ == data_->end()) {
+    // We must call this at least once, in the case where there are no headers.
+    queue_headers();
+  }
+
+  flush_data();
+}
+
+void
+HttpBase::recv(HttpData* data) {
+  RTC_DCHECK(HM_NONE == mode_);
+  if (mode_ != HM_NONE) {
+    return;
+  } else if (!isConnected()) {
+    OnHttpStreamEvent(http_stream_, SE_CLOSE, HE_DISCONNECTED);
+    return;
+  }
+
+  mode_ = HM_RECV;
+  data_ = data;
+  len_ = 0;
+  ignore_data_ = chunk_data_ = false;
+
+  reset();
+  if (doc_stream_) {
+    doc_stream_->SignalEvent(doc_stream_, SE_OPEN | SE_READ, 0);
+  } else {
+    read_and_process_data();
+  }
+}
+
+void
+HttpBase::abort(HttpError err) {
+  if (mode_ != HM_NONE) {
+    if (http_stream_ != nullptr) {
+      http_stream_->Close();
+    }
+    do_complete(err);
+  }
+}
+
+StreamInterface* HttpBase::GetDocumentStream() {
+  if (doc_stream_)
+    return nullptr;
+  doc_stream_ = new DocumentStream(this);
+  return doc_stream_;
+}
+
+HttpError HttpBase::HandleStreamClose(int error) {
+  if (http_stream_ != nullptr) {
+    http_stream_->Close();
+  }
+  if (error == 0) {
+    if ((mode_ == HM_RECV) && is_valid_end_of_input()) {
+      return HE_NONE;
+    } else {
+      return HE_DISCONNECTED;
+    }
+  } else if (error == SOCKET_EACCES) {
+    return HE_AUTH;
+  } else if (error == SEC_E_CERT_EXPIRED) {
+    return HE_CERTIFICATE_EXPIRED;
+  }
+  LOG_F(LS_ERROR) << "(" << error << ")";
+  return (HM_CONNECT == mode_) ? HE_CONNECT_FAILED : HE_SOCKET_ERROR;
+}
+
+bool HttpBase::DoReceiveLoop(HttpError* error) {
+  RTC_DCHECK(HM_RECV == mode_);
+  RTC_DCHECK(nullptr != error);
+
+  // Do to the latency between receiving read notifications from
+  // pseudotcpchannel, we rely on repeated calls to read in order to acheive
+  // ideal throughput.  The number of reads is limited to prevent starving
+  // the caller.
+
+  size_t loop_count = 0;
+  const size_t kMaxReadCount = 20;
+  bool process_requires_more_data = false;
+  do {
+    // The most frequent use of this function is response to new data available
+    // on http_stream_.  Therefore, we optimize by attempting to read from the
+    // network first (as opposed to processing existing data first).
+
+    if (len_ < sizeof(buffer_)) {
+      // Attempt to buffer more data.
+      size_t read;
+      int read_error;
+      StreamResult read_result = http_stream_->Read(buffer_ + len_,
+                                                    sizeof(buffer_) - len_,
+                                                    &read, &read_error);
+      switch (read_result) {
+      case SR_SUCCESS:
+        RTC_DCHECK(len_ + read <= sizeof(buffer_));
+        len_ += read;
+        break;
+      case SR_BLOCK:
+        if (process_requires_more_data) {
+          // We're can't make progress until more data is available.
+          return false;
+        }
+        // Attempt to process the data already in our buffer.
+        break;
+      case SR_EOS:
+        // Clean close, with no error.
+        read_error = 0;
+        FALLTHROUGH();  // Fall through to HandleStreamClose.
+      case SR_ERROR:
+        *error = HandleStreamClose(read_error);
+        return true;
+      }
+    } else if (process_requires_more_data) {
+      // We have too much unprocessed data in our buffer.  This should only
+      // occur when a single HTTP header is longer than the buffer size (32K).
+      // Anything longer than that is almost certainly an error.
+      *error = HE_OVERFLOW;
+      return true;
+    }
+
+    // Process data in our buffer.  Process is not guaranteed to process all
+    // the buffered data.  In particular, it will wait until a complete
+    // protocol element (such as http header, or chunk size) is available,
+    // before processing it in its entirety.  Also, it is valid and sometimes
+    // necessary to call Process with an empty buffer, since the state machine
+    // may have interrupted state transitions to complete.
+    size_t processed;
+    ProcessResult process_result = Process(buffer_, len_, &processed,
+                                            error);
+    RTC_DCHECK(processed <= len_);
+    len_ -= processed;
+    memmove(buffer_, buffer_ + processed, len_);
+    switch (process_result) {
+    case PR_CONTINUE:
+      // We need more data to make progress.
+      process_requires_more_data = true;
+      break;
+    case PR_BLOCK:
+      // We're stalled on writing the processed data.
+      return false;
+    case PR_COMPLETE:
+      // *error already contains the correct code.
+      return true;
+    }
+  } while (++loop_count <= kMaxReadCount);
+
+  LOG_F(LS_WARNING) << "danger of starvation";
+  return false;
+}
+
+void
+HttpBase::read_and_process_data() {
+  HttpError error;
+  if (DoReceiveLoop(&error)) {
+    complete(error);
+  }
+}
+
+void
+HttpBase::flush_data() {
+  RTC_DCHECK(HM_SEND == mode_);
+
+  // When send_required is true, no more buffering can occur without a network
+  // write.
+  bool send_required = (len_ >= sizeof(buffer_));
+
+  while (true) {
+    RTC_DCHECK(len_ <= sizeof(buffer_));
+
+    // HTTP is inherently sensitive to round trip latency, since a frequent use
+    // case is for small requests and responses to be sent back and forth, and
+    // the lack of pipelining forces a single request to take a minimum of the
+    // round trip time.  As a result, it is to our benefit to pack as much data
+    // into each packet as possible.  Thus, we defer network writes until we've
+    // buffered as much data as possible.
+
+    if (!send_required && (header_ != data_->end())) {
+      // First, attempt to queue more header data.
+      send_required = queue_headers();
+    }
+
+    if (!send_required && data_->document) {
+      // Next, attempt to queue document data.
+
+      const size_t kChunkDigits = 8;
+      size_t offset, reserve;
+      if (chunk_data_) {
+        // Reserve characters at the start for X-byte hex value and \r\n
+        offset = len_ + kChunkDigits + 2;
+        // ... and 2 characters at the end for \r\n
+        reserve = offset + 2;
+      } else {
+        offset = len_;
+        reserve = offset;
+      }
+
+      if (reserve >= sizeof(buffer_)) {
+        send_required = true;
+      } else {
+        size_t read;
+        int error;
+        StreamResult result = data_->document->Read(buffer_ + offset,
+                                                    sizeof(buffer_) - reserve,
+                                                    &read, &error);
+        if (result == SR_SUCCESS) {
+          RTC_DCHECK(reserve + read <= sizeof(buffer_));
+          if (chunk_data_) {
+            // Prepend the chunk length in hex.
+            // Note: sprintfn appends a null terminator, which is why we can't
+            // combine it with the line terminator.
+            sprintfn(buffer_ + len_, kChunkDigits + 1, "%.*x",
+                     kChunkDigits, read);
+            // Add line terminator to the chunk length.
+            memcpy(buffer_ + len_ + kChunkDigits, "\r\n", 2);
+            // Add line terminator to the end of the chunk.
+            memcpy(buffer_ + offset + read, "\r\n", 2);
+          }
+          len_ = reserve + read;
+        } else if (result == SR_BLOCK) {
+          // Nothing to do but flush data to the network.
+          send_required = true;
+        } else if (result == SR_EOS) {
+          if (chunk_data_) {
+            // Append the empty chunk and empty trailers, then turn off
+            // chunking.
+            RTC_DCHECK(len_ + 5 <= sizeof(buffer_));
+            memcpy(buffer_ + len_, "0\r\n\r\n", 5);
+            len_ += 5;
+            chunk_data_ = false;
+          } else if (0 == len_) {
+            // No more data to read, and no more data to write.
+            do_complete();
+            return;
+          }
+          // Although we are done reading data, there is still data which needs
+          // to be flushed to the network.
+          send_required = true;
+        } else {
+          LOG_F(LS_ERROR) << "Read error: " << error;
+          do_complete(HE_STREAM);
+          return;
+        }
+      }
+    }
+
+    if (0 == len_) {
+      // No data currently available to send.
+      if (!data_->document) {
+        // If there is no source document, that means we're done.
+        do_complete();
+      }
+      return;
+    }
+
+    size_t written;
+    int error;
+    StreamResult result = http_stream_->Write(buffer_, len_, &written, &error);
+    if (result == SR_SUCCESS) {
+      RTC_DCHECK(written <= len_);
+      len_ -= written;
+      memmove(buffer_, buffer_ + written, len_);
+      send_required = false;
+    } else if (result == SR_BLOCK) {
+      if (send_required) {
+        // Nothing more we can do until network is writeable.
+        return;
+      }
+    } else {
+      RTC_DCHECK(result == SR_ERROR);
+      LOG_F(LS_ERROR) << "error";
+      OnHttpStreamEvent(http_stream_, SE_CLOSE, error);
+      return;
+    }
+  }
+
+  RTC_NOTREACHED();
+}
+
+bool
+HttpBase::queue_headers() {
+  RTC_DCHECK(HM_SEND == mode_);
+  while (header_ != data_->end()) {
+    size_t len = sprintfn(buffer_ + len_, sizeof(buffer_) - len_,
+                          "%.*s: %.*s\r\n",
+                          header_->first.size(), header_->first.data(),
+                          header_->second.size(), header_->second.data());
+    if (len_ + len < sizeof(buffer_) - 3) {
+      len_ += len;
+      ++header_;
+    } else if (len_ == 0) {
+      LOG(WARNING) << "discarding header that is too long: " << header_->first;
+      ++header_;
+    } else {
+      // Not enough room for the next header, write to network first.
+      return true;
+    }
+  }
+  // End of headers
+  len_ += strcpyn(buffer_ + len_, sizeof(buffer_) - len_, "\r\n");
+  return false;
+}
+
+void
+HttpBase::do_complete(HttpError err) {
+  RTC_DCHECK(mode_ != HM_NONE);
+  HttpMode mode = mode_;
+  mode_ = HM_NONE;
+  if (data_ && data_->document) {
+    data_->document->SignalEvent.disconnect(this);
+  }
+  data_ = nullptr;
+  if ((HM_RECV == mode) && doc_stream_) {
+    RTC_DCHECK(HE_NONE !=
+               err);  // We should have Disconnected doc_stream_ already.
+    DocumentStream* ds = doc_stream_;
+    ds->Disconnect(err);
+    ds->SignalEvent(ds, SE_CLOSE, err);
+  }
+  if (notify_) {
+    notify_->onHttpComplete(mode, err);
+  }
+}
+
+//
+// Stream Signals
+//
+
+void
+HttpBase::OnHttpStreamEvent(StreamInterface* stream, int events, int error) {
+  RTC_DCHECK(stream == http_stream_);
+  if ((events & SE_OPEN) && (mode_ == HM_CONNECT)) {
+    do_complete();
+    return;
+  }
+
+  if ((events & SE_WRITE) && (mode_ == HM_SEND)) {
+    flush_data();
+    return;
+  }
+
+  if ((events & SE_READ) && (mode_ == HM_RECV)) {
+    if (doc_stream_) {
+      doc_stream_->SignalEvent(doc_stream_, SE_READ, 0);
+    } else {
+      read_and_process_data();
+    }
+    return;
+  }
+
+  if ((events & SE_CLOSE) == 0)
+    return;
+
+  HttpError http_error = HandleStreamClose(error);
+  if (mode_ == HM_RECV) {
+    complete(http_error);
+  } else if (mode_ != HM_NONE) {
+    do_complete(http_error);
+  } else if (notify_) {
+    notify_->onHttpClosed(http_error);
+  }
+}
+
+void
+HttpBase::OnDocumentEvent(StreamInterface* stream, int events, int error) {
+  RTC_DCHECK(stream == data_->document.get());
+  if ((events & SE_WRITE) && (mode_ == HM_RECV)) {
+    read_and_process_data();
+    return;
+  }
+
+  if ((events & SE_READ) && (mode_ == HM_SEND)) {
+    flush_data();
+    return;
+  }
+
+  if (events & SE_CLOSE) {
+    LOG_F(LS_ERROR) << "Read error: " << error;
+    do_complete(HE_STREAM);
+    return;
+  }
+}
+
+//
+// HttpParser Implementation
+//
+
+HttpParser::ProcessResult
+HttpBase::ProcessLeader(const char* line, size_t len, HttpError* error) {
+  *error = data_->parseLeader(line, len);
+  return (HE_NONE == *error) ? PR_CONTINUE : PR_COMPLETE;
+}
+
+HttpParser::ProcessResult
+HttpBase::ProcessHeader(const char* name, size_t nlen, const char* value,
+                        size_t vlen, HttpError* error) {
+  std::string sname(name, nlen), svalue(value, vlen);
+  data_->addHeader(sname, svalue);
+  return PR_CONTINUE;
+}
+
+HttpParser::ProcessResult
+HttpBase::ProcessHeaderComplete(bool chunked, size_t& data_size,
+                                HttpError* error) {
+  StreamInterface* old_docstream = doc_stream_;
+  if (notify_) {
+    *error = notify_->onHttpHeaderComplete(chunked, data_size);
+    // The request must not be aborted as a result of this callback.
+    RTC_DCHECK(nullptr != data_);
+  }
+  if ((HE_NONE == *error) && data_->document) {
+    data_->document->SignalEvent.connect(this, &HttpBase::OnDocumentEvent);
+  }
+  if (HE_NONE != *error) {
+    return PR_COMPLETE;
+  }
+  if (old_docstream != doc_stream_) {
+    // Break out of Process loop, since our I/O model just changed.
+    return PR_BLOCK;
+  }
+  return PR_CONTINUE;
+}
+
+HttpParser::ProcessResult
+HttpBase::ProcessData(const char* data, size_t len, size_t& read,
+                      HttpError* error) {
+  if (ignore_data_ || !data_->document) {
+    read = len;
+    return PR_CONTINUE;
+  }
+  int write_error = 0;
+  switch (data_->document->Write(data, len, &read, &write_error)) {
+  case SR_SUCCESS:
+    return PR_CONTINUE;
+  case SR_BLOCK:
+    return PR_BLOCK;
+  case SR_EOS:
+    LOG_F(LS_ERROR) << "Unexpected EOS";
+    *error = HE_STREAM;
+    return PR_COMPLETE;
+  case SR_ERROR:
+  default:
+    LOG_F(LS_ERROR) << "Write error: " << write_error;
+    *error = HE_STREAM;
+    return PR_COMPLETE;
+  }
+}
+
+void
+HttpBase::OnComplete(HttpError err) {
+  LOG_F(LS_VERBOSE);
+  do_complete(err);
+}
+
+} // namespace rtc
diff --git a/base/httpbase.h b/base/httpbase.h
index a66ce15..4b834a4 100644
--- a/base/httpbase.h
+++ b/base/httpbase.h
@@ -9,12 +9,179 @@
  */
 
 
-#ifndef WEBRTC_BASE_HTTPBASE_H_
-#define WEBRTC_BASE_HTTPBASE_H_
+#ifndef WEBRTC_BASE_HTTPBASE_H__
+#define WEBRTC_BASE_HTTPBASE_H__
 
+#include "webrtc/base/httpcommon.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/httpbase.h"
+namespace rtc {
 
-#endif // WEBRTC_BASE_HTTPBASE_H_
+class StreamInterface;
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpParser - Parses an HTTP stream provided via Process and end_of_input, and
+// generates events for:
+//  Structural Elements: Leader, Headers, Document Data
+//  Events: End of Headers, End of Document, Errors
+///////////////////////////////////////////////////////////////////////////////
+
+class HttpParser {
+public:
+  enum ProcessResult { PR_CONTINUE, PR_BLOCK, PR_COMPLETE };
+  HttpParser();
+  virtual ~HttpParser();
+  
+  void reset();
+  ProcessResult Process(const char* buffer, size_t len, size_t* processed,
+                        HttpError* error);
+  bool is_valid_end_of_input() const;
+  void complete(HttpError err);
+  
+  size_t GetDataRemaining() const { return data_size_; }
+
+protected:
+  ProcessResult ProcessLine(const char* line, size_t len, HttpError* error);
+
+  // HttpParser Interface
+  virtual ProcessResult ProcessLeader(const char* line, size_t len,
+                                      HttpError* error) = 0;
+  virtual ProcessResult ProcessHeader(const char* name, size_t nlen,
+                                      const char* value, size_t vlen,
+                                      HttpError* error) = 0;
+  virtual ProcessResult ProcessHeaderComplete(bool chunked, size_t& data_size,
+                                              HttpError* error) = 0;
+  virtual ProcessResult ProcessData(const char* data, size_t len, size_t& read,
+                                    HttpError* error) = 0;
+  virtual void OnComplete(HttpError err) = 0;
+  
+private:
+  enum State {
+    ST_LEADER, ST_HEADERS,
+    ST_CHUNKSIZE, ST_CHUNKTERM, ST_TRAILERS,
+    ST_DATA, ST_COMPLETE
+  } state_;
+  bool chunked_;
+  size_t data_size_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// IHttpNotify
+///////////////////////////////////////////////////////////////////////////////
+
+enum HttpMode { HM_NONE, HM_CONNECT, HM_RECV, HM_SEND };
+
+class IHttpNotify {
+public:
+  virtual ~IHttpNotify() {}
+  virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) = 0;
+  virtual void onHttpComplete(HttpMode mode, HttpError err) = 0;
+  virtual void onHttpClosed(HttpError err) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpBase - Provides a state machine for implementing HTTP-based components.
+// Attach HttpBase to a StreamInterface which represents a bidirectional HTTP
+// stream, and then call send() or recv() to initiate sending or receiving one
+// side of an HTTP transaction.  By default, HttpBase operates as an I/O pump,
+// moving data from the HTTP stream to the HttpData object and vice versa.
+// However, it can also operate in stream mode, in which case the user of the
+// stream interface drives I/O via calls to Read().
+///////////////////////////////////////////////////////////////////////////////
+
+class HttpBase
+: private HttpParser,
+  public sigslot::has_slots<>
+{
+public:
+  HttpBase();
+  ~HttpBase() override;
+
+  void notify(IHttpNotify* notify) { notify_ = notify; }
+  bool attach(StreamInterface* stream);
+  StreamInterface* stream() { return http_stream_; }
+  StreamInterface* detach();
+  bool isConnected() const;
+
+  void send(HttpData* data);
+  void recv(HttpData* data);
+  void abort(HttpError err);
+
+  HttpMode mode() const { return mode_; }
+
+  void set_ignore_data(bool ignore) { ignore_data_ = ignore; }
+  bool ignore_data() const { return ignore_data_; }
+
+  // Obtaining this stream puts HttpBase into stream mode until the stream
+  // is closed.  HttpBase can only expose one open stream interface at a time.
+  // Further calls will return null.
+  StreamInterface* GetDocumentStream();
+
+protected:
+  // Do cleanup when the http stream closes (error may be 0 for a clean
+  // shutdown), and return the error code to signal.
+  HttpError HandleStreamClose(int error);
+
+  // DoReceiveLoop acts as a data pump, pulling data from the http stream,
+  // pushing it through the HttpParser, and then populating the HttpData object
+  // based on the callbacks from the parser.  One of the most interesting
+  // callbacks is ProcessData, which provides the actual http document body.
+  // This data is then written to the HttpData::document.  As a result, data
+  // flows from the network to the document, with some incidental protocol
+  // parsing in between.
+  // Ideally, we would pass in the document* to DoReceiveLoop, to more easily
+  // support GetDocumentStream().  However, since the HttpParser is callback
+  // driven, we are forced to store the pointer somewhere until the callback
+  // is triggered.
+  // Returns true if the received document has finished, and
+  // HttpParser::complete should be called.
+  bool DoReceiveLoop(HttpError* err);
+
+  void read_and_process_data();
+  void flush_data();
+  bool queue_headers();
+  void do_complete(HttpError err = HE_NONE);
+
+  void OnHttpStreamEvent(StreamInterface* stream, int events, int error);
+  void OnDocumentEvent(StreamInterface* stream, int events, int error);
+
+  // HttpParser Interface
+  ProcessResult ProcessLeader(const char* line,
+                              size_t len,
+                              HttpError* error) override;
+  ProcessResult ProcessHeader(const char* name,
+                              size_t nlen,
+                              const char* value,
+                              size_t vlen,
+                              HttpError* error) override;
+  ProcessResult ProcessHeaderComplete(bool chunked,
+                                      size_t& data_size,
+                                      HttpError* error) override;
+  ProcessResult ProcessData(const char* data,
+                            size_t len,
+                            size_t& read,
+                            HttpError* error) override;
+  void OnComplete(HttpError err) override;
+
+private:
+  class DocumentStream;
+  friend class DocumentStream;
+
+  enum { kBufferSize = 32 * 1024 };
+
+  HttpMode mode_;
+  HttpData* data_;
+  IHttpNotify* notify_;
+  StreamInterface* http_stream_;
+  DocumentStream* doc_stream_;
+  char buffer_[kBufferSize];
+  size_t len_;
+
+  bool ignore_data_, chunk_data_;
+  HttpData::const_iterator header_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace rtc
+
+#endif // WEBRTC_BASE_HTTPBASE_H__
diff --git a/base/httpbase_unittest.cc b/base/httpbase_unittest.cc
new file mode 100644
index 0000000..11fa821
--- /dev/null
+++ b/base/httpbase_unittest.cc
@@ -0,0 +1,526 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/httpbase.h"
+#include "webrtc/base/testutils.h"
+
+namespace rtc {
+
+const char* const kHttpResponse =
+  "HTTP/1.1 200\r\n"
+  "Connection: Keep-Alive\r\n"
+  "Content-Type: text/plain\r\n"
+  "Proxy-Authorization: 42\r\n"
+  "Transfer-Encoding: chunked\r\n"
+  "\r\n"
+  "00000008\r\n"
+  "Goodbye!\r\n"
+  "0\r\n\r\n";
+
+const char* const kHttpEmptyResponse =
+  "HTTP/1.1 200\r\n"
+  "Connection: Keep-Alive\r\n"
+  "Content-Length: 0\r\n"
+  "Proxy-Authorization: 42\r\n"
+  "\r\n";
+
+const char* const kHttpResponsePrefix =
+  "HTTP/1.1 200\r\n"
+  "Connection: Keep-Alive\r\n"
+  "Content-Type: text/plain\r\n"
+  "Proxy-Authorization: 42\r\n"
+  "Transfer-Encoding: chunked\r\n"
+  "\r\n"
+  "8\r\n"
+  "Goodbye!\r\n";
+
+class HttpBaseTest : public testing::Test, public IHttpNotify {
+public:
+  enum EventType { E_HEADER_COMPLETE, E_COMPLETE, E_CLOSED };
+  struct Event {
+    EventType event;
+    bool chunked;
+    size_t data_size;
+    HttpMode mode;
+    HttpError err;
+  };
+  HttpBaseTest() : mem(nullptr), obtain_stream(false), http_stream(nullptr) {}
+
+  virtual void SetUp() { }
+  virtual void TearDown() {
+    delete http_stream;
+    // Avoid an ASSERT, in case a test doesn't clean up properly
+    base.abort(HE_NONE);
+  }
+
+  virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) {
+    LOG_F(LS_VERBOSE) << "chunked: " << chunked << " size: " << data_size;
+    Event e = { E_HEADER_COMPLETE, chunked, data_size, HM_NONE, HE_NONE};
+    events.push_back(e);
+    if (obtain_stream) {
+      ObtainDocumentStream();
+    }
+    return HE_NONE;
+  }
+  virtual void onHttpComplete(HttpMode mode, HttpError err) {
+    LOG_F(LS_VERBOSE) << "mode: " << mode << " err: " << err;
+    Event e = { E_COMPLETE, false, 0, mode, err };
+    events.push_back(e);
+  }
+  virtual void onHttpClosed(HttpError err) {
+    LOG_F(LS_VERBOSE) << "err: " << err;
+    Event e = { E_CLOSED, false, 0, HM_NONE, err };
+    events.push_back(e);
+  }
+
+  void SetupSource(const char* response);
+
+  void VerifyHeaderComplete(size_t event_count, bool empty_doc);
+  void VerifyDocumentContents(const char* expected_data,
+                              size_t expected_length = SIZE_UNKNOWN);
+
+  void ObtainDocumentStream();
+  void VerifyDocumentStreamIsOpening();
+  void VerifyDocumentStreamOpenEvent();
+  void ReadDocumentStreamData(const char* expected_data);
+  void VerifyDocumentStreamIsEOS();
+
+  void SetupDocument(const char* response);
+  void VerifySourceContents(const char* expected_data,
+                            size_t expected_length = SIZE_UNKNOWN);
+
+  void VerifyTransferComplete(HttpMode mode, HttpError error);
+
+  HttpBase base;
+  MemoryStream* mem;
+  HttpResponseData data;
+
+  // The source of http data, and source events
+  webrtc::testing::StreamSource src;
+  std::vector<Event> events;
+
+  // Document stream, and stream events
+  bool obtain_stream;
+  StreamInterface* http_stream;
+  webrtc::testing::StreamSink sink;
+};
+
+void HttpBaseTest::SetupSource(const char* http_data) {
+  LOG_F(LS_VERBOSE) << "Enter";
+
+  src.SetState(SS_OPENING);
+  src.QueueString(http_data);
+
+  base.notify(this);
+  base.attach(&src);
+  EXPECT_TRUE(events.empty());
+
+  src.SetState(SS_OPEN);
+  ASSERT_EQ(1U, events.size());
+  EXPECT_EQ(E_COMPLETE, events[0].event);
+  EXPECT_EQ(HM_CONNECT, events[0].mode);
+  EXPECT_EQ(HE_NONE, events[0].err);
+  events.clear();
+
+  mem = new MemoryStream;
+  data.document.reset(mem);
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) {
+  LOG_F(LS_VERBOSE) << "Enter";
+
+  ASSERT_EQ(event_count, events.size());
+  EXPECT_EQ(E_HEADER_COMPLETE, events[0].event);
+
+  std::string header;
+  EXPECT_EQ(HVER_1_1, data.version);
+  EXPECT_EQ(static_cast<uint32_t>(HC_OK), data.scode);
+  EXPECT_TRUE(data.hasHeader(HH_PROXY_AUTHORIZATION, &header));
+  EXPECT_EQ("42", header);
+  EXPECT_TRUE(data.hasHeader(HH_CONNECTION, &header));
+  EXPECT_EQ("Keep-Alive", header);
+
+  if (empty_doc) {
+    EXPECT_FALSE(events[0].chunked);
+    EXPECT_EQ(0U, events[0].data_size);
+
+    EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header));
+    EXPECT_EQ("0", header);
+  } else {
+    EXPECT_TRUE(events[0].chunked);
+    EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size);
+
+    EXPECT_TRUE(data.hasHeader(HH_CONTENT_TYPE, &header));
+    EXPECT_EQ("text/plain", header);
+    EXPECT_TRUE(data.hasHeader(HH_TRANSFER_ENCODING, &header));
+    EXPECT_EQ("chunked", header);
+  }
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyDocumentContents(const char* expected_data,
+                                          size_t expected_length) {
+  LOG_F(LS_VERBOSE) << "Enter";
+
+  if (SIZE_UNKNOWN == expected_length) {
+    expected_length = strlen(expected_data);
+  }
+  EXPECT_EQ(mem, data.document.get());
+
+  size_t length;
+  mem->GetSize(&length);
+  EXPECT_EQ(expected_length, length);
+  EXPECT_TRUE(0 == memcmp(expected_data, mem->GetBuffer(), length));
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::ObtainDocumentStream() {
+  LOG_F(LS_VERBOSE) << "Enter";
+  EXPECT_FALSE(http_stream);
+  http_stream = base.GetDocumentStream();
+  ASSERT_TRUE(nullptr != http_stream);
+  sink.Monitor(http_stream);
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyDocumentStreamIsOpening() {
+  LOG_F(LS_VERBOSE) << "Enter";
+  ASSERT_TRUE(nullptr != http_stream);
+  EXPECT_EQ(0, sink.Events(http_stream));
+  EXPECT_EQ(SS_OPENING, http_stream->GetState());
+
+  size_t read = 0;
+  char buffer[5] = { 0 };
+  EXPECT_EQ(SR_BLOCK,
+            http_stream->Read(buffer, sizeof(buffer), &read, nullptr));
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyDocumentStreamOpenEvent() {
+  LOG_F(LS_VERBOSE) << "Enter";
+
+  ASSERT_TRUE(nullptr != http_stream);
+  EXPECT_EQ(SE_OPEN | SE_READ, sink.Events(http_stream));
+  EXPECT_EQ(SS_OPEN, http_stream->GetState());
+
+  // HTTP headers haven't arrived yet
+  EXPECT_EQ(0U, events.size());
+  EXPECT_EQ(static_cast<uint32_t>(HC_INTERNAL_SERVER_ERROR), data.scode);
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) {
+  LOG_F(LS_VERBOSE) << "Enter";
+
+  ASSERT_TRUE(nullptr != http_stream);
+  EXPECT_EQ(SS_OPEN, http_stream->GetState());
+
+  // Pump the HTTP I/O using Read, and verify the results.
+  size_t verified_length = 0;
+  const size_t expected_length = strlen(expected_data);
+  while (verified_length < expected_length) {
+    size_t read = 0;
+    char buffer[5] = { 0 };
+    size_t amt_to_read =
+        std::min(expected_length - verified_length, sizeof(buffer));
+    EXPECT_EQ(SR_SUCCESS,
+              http_stream->Read(buffer, amt_to_read, &read, nullptr));
+    EXPECT_EQ(amt_to_read, read);
+    EXPECT_TRUE(0 == memcmp(expected_data + verified_length, buffer, read));
+    verified_length += read;
+  }
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyDocumentStreamIsEOS() {
+  LOG_F(LS_VERBOSE) << "Enter";
+
+  ASSERT_TRUE(nullptr != http_stream);
+  size_t read = 0;
+  char buffer[5] = { 0 };
+  EXPECT_EQ(SR_EOS, http_stream->Read(buffer, sizeof(buffer), &read, nullptr));
+  EXPECT_EQ(SS_CLOSED, http_stream->GetState());
+
+  // When EOS is caused by Read, we don't expect SE_CLOSE
+  EXPECT_EQ(0, sink.Events(http_stream));
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::SetupDocument(const char* document_data) {
+  LOG_F(LS_VERBOSE) << "Enter";
+  src.SetState(SS_OPEN);
+
+  base.notify(this);
+  base.attach(&src);
+  EXPECT_TRUE(events.empty());
+
+  if (document_data) {
+    // Note: we could just call data.set_success("text/plain", mem), but that
+    // won't allow us to use the chunked transfer encoding.
+    mem = new MemoryStream(document_data);
+    data.document.reset(mem);
+    data.setHeader(HH_CONTENT_TYPE, "text/plain");
+    data.setHeader(HH_TRANSFER_ENCODING, "chunked");
+  } else {
+    data.setHeader(HH_CONTENT_LENGTH, "0");
+  }
+  data.scode = HC_OK;
+  data.setHeader(HH_PROXY_AUTHORIZATION, "42");
+  data.setHeader(HH_CONNECTION, "Keep-Alive");
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifySourceContents(const char* expected_data,
+                                        size_t expected_length) {
+  LOG_F(LS_VERBOSE) << "Enter";
+  if (SIZE_UNKNOWN == expected_length) {
+    expected_length = strlen(expected_data);
+  }
+  std::string contents = src.ReadData();
+  EXPECT_EQ(expected_length, contents.length());
+  EXPECT_TRUE(0 == memcmp(expected_data, contents.data(), expected_length));
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyTransferComplete(HttpMode mode, HttpError error) {
+  LOG_F(LS_VERBOSE) << "Enter";
+  // Verify that http operation has completed
+  ASSERT_TRUE(events.size() > 0);
+  size_t last_event = events.size() - 1;
+  EXPECT_EQ(E_COMPLETE, events[last_event].event);
+  EXPECT_EQ(mode, events[last_event].mode);
+  EXPECT_EQ(error, events[last_event].err);
+  LOG_F(LS_VERBOSE) << "Exit";
+}
+
+//
+// Tests
+//
+
+TEST_F(HttpBaseTest, SupportsSend) {
+  // Queue response document
+  SetupDocument("Goodbye!");
+
+  // Begin send
+  base.send(&data);
+
+  // Send completed successfully
+  VerifyTransferComplete(HM_SEND, HE_NONE);
+  VerifySourceContents(kHttpResponse);
+}
+
+TEST_F(HttpBaseTest, SupportsSendNoDocument) {
+  // Queue response document
+  SetupDocument(nullptr);
+
+  // Begin send
+  base.send(&data);
+
+  // Send completed successfully
+  VerifyTransferComplete(HM_SEND, HE_NONE);
+  VerifySourceContents(kHttpEmptyResponse);
+}
+
+TEST_F(HttpBaseTest, SignalsCompleteOnInterruptedSend) {
+  // This test is attempting to expose a bug that occurs when a particular
+  // base objects is used for receiving, and then used for sending.  In
+  // particular, the HttpParser state is different after receiving.  Simulate
+  // that here.
+  SetupSource(kHttpResponse);
+  base.recv(&data);
+  VerifyTransferComplete(HM_RECV, HE_NONE);
+
+  src.Clear();
+  data.clear(true);
+  events.clear();
+  base.detach();
+
+  // Queue response document
+  SetupDocument("Goodbye!");
+
+  // Prevent entire response from being sent
+  const size_t kInterruptedLength = strlen(kHttpResponse) - 1;
+  src.SetWriteBlock(kInterruptedLength);
+
+  // Begin send
+  base.send(&data);
+
+  // Document is mostly complete, but no completion signal yet.
+  EXPECT_TRUE(events.empty());
+  VerifySourceContents(kHttpResponse, kInterruptedLength);
+
+  src.SetState(SS_CLOSED);
+
+  // Send completed with disconnect error, and no additional data.
+  VerifyTransferComplete(HM_SEND, HE_DISCONNECTED);
+  EXPECT_TRUE(src.ReadData().empty());
+}
+
+TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) {
+  // Queue response document
+  SetupSource(kHttpResponse);
+
+  // Begin receive
+  base.recv(&data);
+
+  // Document completed successfully
+  VerifyHeaderComplete(2, false);
+  VerifyTransferComplete(HM_RECV, HE_NONE);
+  VerifyDocumentContents("Goodbye!");
+}
+
+TEST_F(HttpBaseTest, SupportsReceiveViaStreamPull) {
+  // Switch to pull mode
+  ObtainDocumentStream();
+  VerifyDocumentStreamIsOpening();
+
+  // Queue response document
+  SetupSource(kHttpResponse);
+  VerifyDocumentStreamIsOpening();
+
+  // Begin receive
+  base.recv(&data);
+
+  // Pull document data
+  VerifyDocumentStreamOpenEvent();
+  ReadDocumentStreamData("Goodbye!");
+  VerifyDocumentStreamIsEOS();
+
+  // Document completed successfully
+  VerifyHeaderComplete(2, false);
+  VerifyTransferComplete(HM_RECV, HE_NONE);
+  VerifyDocumentContents("");
+}
+
+TEST_F(HttpBaseTest, DISABLED_AllowsCloseStreamBeforeDocumentIsComplete) {
+
+  // TODO: Remove extra logging once test failure is understood
+  LoggingSeverity old_sev = rtc::LogMessage::GetLogToDebug();
+  rtc::LogMessage::LogToDebug(LS_VERBOSE);
+
+
+  // Switch to pull mode
+  ObtainDocumentStream();
+  VerifyDocumentStreamIsOpening();
+
+  // Queue response document
+  SetupSource(kHttpResponse);
+  VerifyDocumentStreamIsOpening();
+
+  // Begin receive
+  base.recv(&data);
+
+  // Pull some of the data
+  VerifyDocumentStreamOpenEvent();
+  ReadDocumentStreamData("Goodb");
+
+  // We've seen the header by now
+  VerifyHeaderComplete(1, false);
+
+  // Close the pull stream, this will transition back to push I/O.
+  http_stream->Close();
+  Thread::Current()->ProcessMessages(0);
+
+  // Remainder of document completed successfully
+  VerifyTransferComplete(HM_RECV, HE_NONE);
+  VerifyDocumentContents("ye!");
+
+  rtc::LogMessage::LogToDebug(old_sev);
+}
+
+TEST_F(HttpBaseTest, AllowsGetDocumentStreamInResponseToHttpHeader) {
+  // Queue response document
+  SetupSource(kHttpResponse);
+
+  // Switch to pull mode in response to header arrival
+  obtain_stream = true;
+
+  // Begin receive
+  base.recv(&data);
+
+  // We've already seen the header, but not data has arrived
+  VerifyHeaderComplete(1, false);
+  VerifyDocumentContents("");
+
+  // Pull the document data
+  ReadDocumentStreamData("Goodbye!");
+  VerifyDocumentStreamIsEOS();
+
+  // Document completed successfully
+  VerifyTransferComplete(HM_RECV, HE_NONE);
+  VerifyDocumentContents("");
+}
+
+TEST_F(HttpBaseTest, AllowsGetDocumentStreamWithEmptyDocumentBody) {
+  // Queue empty response document
+  SetupSource(kHttpEmptyResponse);
+
+  // Switch to pull mode in response to header arrival
+  obtain_stream = true;
+
+  // Begin receive
+  base.recv(&data);
+
+  // We've already seen the header, but not data has arrived
+  VerifyHeaderComplete(1, true);
+  VerifyDocumentContents("");
+
+  // The document is still open, until we attempt to read
+  ASSERT_TRUE(nullptr != http_stream);
+  EXPECT_EQ(SS_OPEN, http_stream->GetState());
+
+  // Attempt to read data, and discover EOS
+  VerifyDocumentStreamIsEOS();
+
+  // Document completed successfully
+  VerifyTransferComplete(HM_RECV, HE_NONE);
+  VerifyDocumentContents("");
+}
+
+TEST_F(HttpBaseTest, SignalsDocumentStreamCloseOnUnexpectedClose) {
+  // Switch to pull mode
+  ObtainDocumentStream();
+  VerifyDocumentStreamIsOpening();
+
+  // Queue response document
+  SetupSource(kHttpResponsePrefix);
+  VerifyDocumentStreamIsOpening();
+
+  // Begin receive
+  base.recv(&data);
+
+  // Pull document data
+  VerifyDocumentStreamOpenEvent();
+  ReadDocumentStreamData("Goodbye!");
+
+  // Simulate unexpected close
+  src.SetState(SS_CLOSED);
+
+  // Observe error event on document stream
+  EXPECT_EQ(webrtc::testing::SSE_ERROR, sink.Events(http_stream));
+
+  // Future reads give an error
+  int error = 0;
+  char buffer[5] = { 0 };
+  EXPECT_EQ(SR_ERROR,
+            http_stream->Read(buffer, sizeof(buffer), nullptr, &error));
+  EXPECT_EQ(HE_DISCONNECTED, error);
+
+  // Document completed with error
+  VerifyHeaderComplete(2, false);
+  VerifyTransferComplete(HM_RECV, HE_DISCONNECTED);
+  VerifyDocumentContents("");
+}
+
+} // namespace rtc
diff --git a/base/httpcommon-inl.h b/base/httpcommon-inl.h
index 7dfe182..f29f075 100644
--- a/base/httpcommon-inl.h
+++ b/base/httpcommon-inl.h
@@ -8,12 +8,125 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_HTTPCOMMON_INL_H_
-#define WEBRTC_BASE_HTTPCOMMON_INL_H_
+#ifndef WEBRTC_BASE_HTTPCOMMON_INL_H__
+#define WEBRTC_BASE_HTTPCOMMON_INL_H__
 
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/httpcommon.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/httpcommon-inl.h"
+namespace rtc {
 
-#endif  // WEBRTC_BASE_HTTPCOMMON_INL_H_
+///////////////////////////////////////////////////////////////////////////////
+// Url
+///////////////////////////////////////////////////////////////////////////////
+
+template<class CTYPE>
+void Url<CTYPE>::do_set_url(const CTYPE* val, size_t len) {
+  if (ascnicmp(val, "http://", 7) == 0) {
+    val += 7; len -= 7;
+    secure_ = false;
+  } else if (ascnicmp(val, "https://", 8) == 0) {
+    val += 8; len -= 8;
+    secure_ = true;
+  } else {
+    clear();
+    return;
+  }
+  const CTYPE* path = strchrn(val, len, static_cast<CTYPE>('/'));
+  if (!path) {
+    path = val + len;
+  }
+  size_t address_length = (path - val);
+  do_set_address(val, address_length);
+  do_set_full_path(path, len - address_length);
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_set_address(const CTYPE* val, size_t len) {
+  if (const CTYPE* at = strchrn(val, len, static_cast<CTYPE>('@'))) {
+    // Everything before the @ is a user:password combo, so skip it.
+    len -= at - val + 1;
+    val = at + 1;
+  }
+  if (const CTYPE* colon = strchrn(val, len, static_cast<CTYPE>(':'))) {
+    host_.assign(val, colon - val);
+    // Note: In every case, we're guaranteed that colon is followed by a null,
+    // or non-numeric character.
+    port_ = static_cast<uint16_t>(::strtoul(colon + 1, nullptr, 10));
+    // TODO: Consider checking for invalid data following port number.
+  } else {
+    host_.assign(val, len);
+    port_ = HttpDefaultPort(secure_);
+  }
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_set_full_path(const CTYPE* val, size_t len) {
+  const CTYPE* query = strchrn(val, len, static_cast<CTYPE>('?'));
+  if (!query) {
+    query = val + len;
+  }
+  size_t path_length = (query - val);
+  if (0 == path_length) {
+    // TODO: consider failing in this case.
+    path_.assign(1, static_cast<CTYPE>('/'));
+  } else {
+    RTC_DCHECK(val[0] == static_cast<CTYPE>('/'));
+    path_.assign(val, path_length);
+  }
+  query_.assign(query, len - path_length);
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_get_url(string* val) const {
+  CTYPE protocol[9];
+  asccpyn(protocol, arraysize(protocol), secure_ ? "https://" : "http://");
+  val->append(protocol);
+  do_get_address(val);
+  do_get_full_path(val);
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_get_address(string* val) const {
+  val->append(host_);
+  if (port_ != HttpDefaultPort(secure_)) {
+    CTYPE format[5], port[32];
+    asccpyn(format, arraysize(format), ":%hu");
+    sprintfn(port, arraysize(port), format, port_);
+    val->append(port);
+  }
+}
+
+template<class CTYPE>
+void Url<CTYPE>::do_get_full_path(string* val) const {
+  val->append(path_);
+  val->append(query_);
+}
+
+template<class CTYPE>
+bool Url<CTYPE>::get_attribute(const string& name, string* value) const {
+  if (query_.empty())
+    return false;
+
+  std::string::size_type pos = query_.find(name, 1);
+  if (std::string::npos == pos)
+    return false;
+
+  pos += name.length() + 1;
+  if ((pos > query_.length()) || (static_cast<CTYPE>('=') != query_[pos-1]))
+    return false;
+
+  std::string::size_type end = query_.find(static_cast<CTYPE>('&'), pos);
+  if (std::string::npos == end) {
+    end = query_.length();
+  }
+  value->assign(query_.substr(pos, end - pos));
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_HTTPCOMMON_INL_H__
diff --git a/base/httpcommon.cc b/base/httpcommon.cc
new file mode 100644
index 0000000..031505b
--- /dev/null
+++ b/base/httpcommon.cc
@@ -0,0 +1,1009 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <time.h>
+
+#if defined(WEBRTC_WIN)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define SECURITY_WIN32
+#include <security.h>
+#endif
+
+#include <algorithm>
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/base64.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/cryptstring.h"
+#include "webrtc/base/httpcommon-inl.h"
+#include "webrtc/base/httpcommon.h"
+#include "webrtc/base/messagedigest.h"
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+
+#if defined(WEBRTC_WIN)
+extern const ConstantLabel SECURITY_ERRORS[];
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Enum - TODO: expose globally later?
+//////////////////////////////////////////////////////////////////////
+
+bool find_string(size_t& index, const std::string& needle,
+                 const char* const haystack[], size_t max_index) {
+  for (index=0; index<max_index; ++index) {
+    if (_stricmp(needle.c_str(), haystack[index]) == 0) {
+      return true;
+    }
+  }
+  return false;
+}
+
+template<class E>
+struct Enum {
+  static const char** Names;
+  static size_t Size;
+
+  static inline const char* Name(E val) { return Names[val]; }
+  static inline bool Parse(E& val, const std::string& name) {
+    size_t index;
+    if (!find_string(index, name, Names, Size))
+      return false;
+    val = static_cast<E>(index);
+    return true;
+  }
+
+  E val;
+
+  inline operator E&() { return val; }
+  inline Enum& operator=(E rhs) { val = rhs; return *this; }
+
+  inline const char* name() const { return Name(val); }
+  inline bool assign(const std::string& name) { return Parse(val, name); }
+  inline Enum& operator=(const std::string& rhs) { assign(rhs); return *this; }
+};
+
+#define ENUM(e,n) \
+  template<> const char** Enum<e>::Names = n; \
+  template<> size_t Enum<e>::Size = sizeof(n)/sizeof(n[0])
+
+//////////////////////////////////////////////////////////////////////
+// HttpCommon
+//////////////////////////////////////////////////////////////////////
+
+static const char* kHttpVersions[HVER_LAST+1] = {
+  "1.0", "1.1", "Unknown"
+};
+ENUM(HttpVersion, kHttpVersions);
+
+static const char* kHttpVerbs[HV_LAST+1] = {
+  "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD"
+};
+ENUM(HttpVerb, kHttpVerbs);
+
+static const char* kHttpHeaders[HH_LAST+1] = {
+  "Age",
+  "Cache-Control",
+  "Connection",
+  "Content-Disposition",
+  "Content-Length",
+  "Content-Range",
+  "Content-Type",
+  "Cookie",
+  "Date",
+  "ETag",
+  "Expires",
+  "Host",
+  "If-Modified-Since",
+  "If-None-Match",
+  "Keep-Alive",
+  "Last-Modified",
+  "Location",
+  "Proxy-Authenticate",
+  "Proxy-Authorization",
+  "Proxy-Connection",
+  "Range",
+  "Set-Cookie",
+  "TE",
+  "Trailers",
+  "Transfer-Encoding",
+  "Upgrade",
+  "User-Agent",
+  "WWW-Authenticate",
+};
+ENUM(HttpHeader, kHttpHeaders);
+
+const char* ToString(HttpVersion version) {
+  return Enum<HttpVersion>::Name(version);
+}
+
+bool FromString(HttpVersion& version, const std::string& str) {
+  return Enum<HttpVersion>::Parse(version, str);
+}
+
+const char* ToString(HttpVerb verb) {
+  return Enum<HttpVerb>::Name(verb);
+}
+
+bool FromString(HttpVerb& verb, const std::string& str) {
+  return Enum<HttpVerb>::Parse(verb, str);
+}
+
+const char* ToString(HttpHeader header) {
+  return Enum<HttpHeader>::Name(header);
+}
+
+bool FromString(HttpHeader& header, const std::string& str) {
+  return Enum<HttpHeader>::Parse(header, str);
+}
+
+bool HttpCodeHasBody(uint32_t code) {
+  return !HttpCodeIsInformational(code)
+         && (code != HC_NO_CONTENT) && (code != HC_NOT_MODIFIED);
+}
+
+bool HttpCodeIsCacheable(uint32_t code) {
+  switch (code) {
+  case HC_OK:
+  case HC_NON_AUTHORITATIVE:
+  case HC_PARTIAL_CONTENT:
+  case HC_MULTIPLE_CHOICES:
+  case HC_MOVED_PERMANENTLY:
+  case HC_GONE:
+    return true;
+  default:
+    return false;
+  }
+}
+
+bool HttpHeaderIsEndToEnd(HttpHeader header) {
+  switch (header) {
+  case HH_CONNECTION:
+  case HH_KEEP_ALIVE:
+  case HH_PROXY_AUTHENTICATE:
+  case HH_PROXY_AUTHORIZATION:
+  case HH_PROXY_CONNECTION:  // Note part of RFC... this is non-standard header
+  case HH_TE:
+  case HH_TRAILERS:
+  case HH_TRANSFER_ENCODING:
+  case HH_UPGRADE:
+    return false;
+  default:
+    return true;
+  }
+}
+
+bool HttpHeaderIsCollapsible(HttpHeader header) {
+  switch (header) {
+  case HH_SET_COOKIE:
+  case HH_PROXY_AUTHENTICATE:
+  case HH_WWW_AUTHENTICATE:
+    return false;
+  default:
+    return true;
+  }
+}
+
+bool HttpShouldKeepAlive(const HttpData& data) {
+  std::string connection;
+  if ((data.hasHeader(HH_PROXY_CONNECTION, &connection)
+      || data.hasHeader(HH_CONNECTION, &connection))) {
+    return (_stricmp(connection.c_str(), "Keep-Alive") == 0);
+  }
+  return (data.version >= HVER_1_1);
+}
+
+namespace {
+
+inline bool IsEndOfAttributeName(size_t pos, size_t len, const char * data) {
+  if (pos >= len)
+    return true;
+  if (isspace(static_cast<unsigned char>(data[pos])))
+    return true;
+  // The reason for this complexity is that some attributes may contain trailing
+  // equal signs (like base64 tokens in Negotiate auth headers)
+  if ((pos+1 < len) && (data[pos] == '=') &&
+      !isspace(static_cast<unsigned char>(data[pos+1])) &&
+      (data[pos+1] != '=')) {
+    return true;
+  }
+  return false;
+}
+
+// TODO: unittest for EscapeAttribute and HttpComposeAttributes.
+
+std::string EscapeAttribute(const std::string& attribute) {
+  const size_t kMaxLength = attribute.length() * 2 + 1;
+  char* buffer = STACK_ARRAY(char, kMaxLength);
+  size_t len = escape(buffer, kMaxLength, attribute.data(), attribute.length(),
+                      "\"", '\\');
+  return std::string(buffer, len);
+}
+
+}  // anonymous namespace
+
+void HttpComposeAttributes(const HttpAttributeList& attributes, char separator,
+                           std::string* composed) {
+  std::stringstream ss;
+  for (size_t i=0; i<attributes.size(); ++i) {
+    if (i > 0) {
+      ss << separator << " ";
+    }
+    ss << attributes[i].first;
+    if (!attributes[i].second.empty()) {
+      ss << "=\"" << EscapeAttribute(attributes[i].second) << "\"";
+    }
+  }
+  *composed = ss.str();
+}
+
+void HttpParseAttributes(const char * data, size_t len,
+                         HttpAttributeList& attributes) {
+  size_t pos = 0;
+  while (true) {
+    // Skip leading whitespace
+    while ((pos < len) && isspace(static_cast<unsigned char>(data[pos]))) {
+      ++pos;
+    }
+
+    // End of attributes?
+    if (pos >= len)
+      return;
+
+    // Find end of attribute name
+    size_t start = pos;
+    while (!IsEndOfAttributeName(pos, len, data)) {
+      ++pos;
+    }
+
+    HttpAttribute attribute;
+    attribute.first.assign(data + start, data + pos);
+
+    // Attribute has value?
+    if ((pos < len) && (data[pos] == '=')) {
+      ++pos; // Skip '='
+      // Check if quoted value
+      if ((pos < len) && (data[pos] == '"')) {
+        while (++pos < len) {
+          if (data[pos] == '"') {
+            ++pos;
+            break;
+          }
+          if ((data[pos] == '\\') && (pos + 1 < len))
+            ++pos;
+          attribute.second.append(1, data[pos]);
+        }
+      } else {
+        while ((pos < len) &&
+            !isspace(static_cast<unsigned char>(data[pos])) &&
+            (data[pos] != ',')) {
+          attribute.second.append(1, data[pos++]);
+        }
+      }
+    }
+
+    attributes.push_back(attribute);
+    if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ','
+  }
+}
+
+bool HttpHasAttribute(const HttpAttributeList& attributes,
+                      const std::string& name,
+                      std::string* value) {
+  for (HttpAttributeList::const_iterator it = attributes.begin();
+       it != attributes.end(); ++it) {
+    if (it->first == name) {
+      if (value) {
+        *value = it->second;
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+bool HttpHasNthAttribute(HttpAttributeList& attributes,
+                         size_t index,
+                         std::string* name,
+                         std::string* value) {
+  if (index >= attributes.size())
+    return false;
+
+  if (name)
+    *name = attributes[index].first;
+  if (value)
+    *value = attributes[index].second;
+  return true;
+}
+
+bool HttpDateToSeconds(const std::string& date, time_t* seconds) {
+  const char* const kTimeZones[] = {
+    "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT",
+    "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M",
+    "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"
+  };
+  const int kTimeZoneOffsets[] = {
+     0,  0, -5, -4, -6, -5, -7, -6, -8, -7,
+    -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12,
+     1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11,  12
+  };
+
+  RTC_DCHECK(nullptr != seconds);
+  struct tm tval;
+  memset(&tval, 0, sizeof(tval));
+  char month[4], zone[6];
+  memset(month, 0, sizeof(month));
+  memset(zone, 0, sizeof(zone));
+
+  if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c",
+                  &tval.tm_mday, month, &tval.tm_year,
+                  &tval.tm_hour, &tval.tm_min, &tval.tm_sec, zone)) {
+    return false;
+  }
+  switch (toupper(month[2])) {
+  case 'N': tval.tm_mon = (month[1] == 'A') ? 0 : 5; break;
+  case 'B': tval.tm_mon = 1; break;
+  case 'R': tval.tm_mon = (month[0] == 'M') ? 2 : 3; break;
+  case 'Y': tval.tm_mon = 4; break;
+  case 'L': tval.tm_mon = 6; break;
+  case 'G': tval.tm_mon = 7; break;
+  case 'P': tval.tm_mon = 8; break;
+  case 'T': tval.tm_mon = 9; break;
+  case 'V': tval.tm_mon = 10; break;
+  case 'C': tval.tm_mon = 11; break;
+  }
+  tval.tm_year -= 1900;
+  time_t gmt, non_gmt = mktime(&tval);
+  if ((zone[0] == '+') || (zone[0] == '-')) {
+    if (!isdigit(zone[1]) || !isdigit(zone[2])
+        || !isdigit(zone[3]) || !isdigit(zone[4])) {
+      return false;
+    }
+    int hours = (zone[1] - '0') * 10 + (zone[2] - '0');
+    int minutes = (zone[3] - '0') * 10 + (zone[4] - '0');
+    int offset = (hours * 60 + minutes) * 60;
+    gmt = non_gmt + ((zone[0] == '+') ? offset : -offset);
+  } else {
+    size_t zindex;
+    if (!find_string(zindex, zone, kTimeZones, arraysize(kTimeZones))) {
+      return false;
+    }
+    gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60;
+  }
+  // TODO: Android should support timezone, see b/2441195
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) || defined(BSD)
+  tm *tm_for_timezone = localtime(&gmt);
+  *seconds = gmt + tm_for_timezone->tm_gmtoff;
+#else
+#if defined(_MSC_VER) && _MSC_VER >= 1900
+  long timezone = 0;
+  _get_timezone(&timezone);
+#endif
+  *seconds = gmt - timezone;
+#endif
+  return true;
+}
+
+std::string HttpAddress(const SocketAddress& address, bool secure) {
+  return (address.port() == HttpDefaultPort(secure))
+          ? address.hostname() : address.ToString();
+}
+
+//////////////////////////////////////////////////////////////////////
+// HttpData
+//////////////////////////////////////////////////////////////////////
+
+HttpData::HttpData() : version(HVER_1_1) {
+}
+
+HttpData::~HttpData() = default;
+
+void
+HttpData::clear(bool release_document) {
+  // Clear headers first, since releasing a document may have far-reaching
+  // effects.
+  headers_.clear();
+  if (release_document) {
+    document.reset();
+  }
+}
+
+void
+HttpData::copy(const HttpData& src) {
+  headers_ = src.headers_;
+}
+
+void
+HttpData::changeHeader(const std::string& name, const std::string& value,
+                       HeaderCombine combine) {
+  if (combine == HC_AUTO) {
+    HttpHeader header;
+    // Unrecognized headers are collapsible
+    combine = !FromString(header, name) || HttpHeaderIsCollapsible(header)
+              ? HC_YES : HC_NO;
+  } else if (combine == HC_REPLACE) {
+    headers_.erase(name);
+    combine = HC_NO;
+  }
+  // At this point, combine is one of (YES, NO, NEW)
+  if (combine != HC_NO) {
+    HeaderMap::iterator it = headers_.find(name);
+    if (it != headers_.end()) {
+      if (combine == HC_YES) {
+        it->second.append(",");
+        it->second.append(value);
+      }
+      return;
+    }
+  }
+  headers_.insert(HeaderMap::value_type(name, value));
+}
+
+size_t HttpData::clearHeader(const std::string& name) {
+  return headers_.erase(name);
+}
+
+HttpData::iterator HttpData::clearHeader(iterator header) {
+  iterator deprecated = header++;
+  headers_.erase(deprecated);
+  return header;
+}
+
+bool
+HttpData::hasHeader(const std::string& name, std::string* value) const {
+  HeaderMap::const_iterator it = headers_.find(name);
+  if (it == headers_.end()) {
+    return false;
+  } else if (value) {
+    *value = it->second;
+  }
+  return true;
+}
+
+void HttpData::setContent(const std::string& content_type,
+                          StreamInterface* document) {
+  setHeader(HH_CONTENT_TYPE, content_type);
+  setDocumentAndLength(document);
+}
+
+void HttpData::setDocumentAndLength(StreamInterface* document) {
+  // TODO: Consider calling Rewind() here?
+  RTC_DCHECK(!hasHeader(HH_CONTENT_LENGTH, nullptr));
+  RTC_DCHECK(!hasHeader(HH_TRANSFER_ENCODING, nullptr));
+  RTC_DCHECK(document != nullptr);
+  this->document.reset(document);
+  size_t content_length = 0;
+  if (this->document->GetAvailable(&content_length)) {
+    char buffer[32];
+    sprintfn(buffer, sizeof(buffer), "%d", content_length);
+    setHeader(HH_CONTENT_LENGTH, buffer);
+  } else {
+    setHeader(HH_TRANSFER_ENCODING, "chunked");
+  }
+}
+
+//
+// HttpRequestData
+//
+
+void
+HttpRequestData::clear(bool release_document) {
+  verb = HV_GET;
+  path.clear();
+  HttpData::clear(release_document);
+}
+
+void
+HttpRequestData::copy(const HttpRequestData& src) {
+  verb = src.verb;
+  path = src.path;
+  HttpData::copy(src);
+}
+
+size_t
+HttpRequestData::formatLeader(char* buffer, size_t size) const {
+  RTC_DCHECK(path.find(' ') == std::string::npos);
+  return sprintfn(buffer, size, "%s %.*s HTTP/%s", ToString(verb), path.size(),
+                  path.data(), ToString(version));
+}
+
+HttpError
+HttpRequestData::parseLeader(const char* line, size_t len) {
+  unsigned int vmajor, vminor;
+  int vend, dstart, dend;
+  // sscanf isn't safe with strings that aren't null-terminated, and there is
+  // no guarantee that |line| is. Create a local copy that is null-terminated.
+  std::string line_str(line, len);
+  line = line_str.c_str();
+  if ((sscanf(line, "%*s%n %n%*s%n HTTP/%u.%u",
+              &vend, &dstart, &dend, &vmajor, &vminor) != 2)
+      || (vmajor != 1)) {
+    return HE_PROTOCOL;
+  }
+  if (vminor == 0) {
+    version = HVER_1_0;
+  } else if (vminor == 1) {
+    version = HVER_1_1;
+  } else {
+    return HE_PROTOCOL;
+  }
+  std::string sverb(line, vend);
+  if (!FromString(verb, sverb.c_str())) {
+    return HE_PROTOCOL; // !?! HC_METHOD_NOT_SUPPORTED?
+  }
+  path.assign(line + dstart, line + dend);
+  return HE_NONE;
+}
+
+bool HttpRequestData::getAbsoluteUri(std::string* uri) const {
+  if (HV_CONNECT == verb)
+    return false;
+  Url<char> url(path);
+  if (url.valid()) {
+    uri->assign(path);
+    return true;
+  }
+  std::string host;
+  if (!hasHeader(HH_HOST, &host))
+    return false;
+  url.set_address(host);
+  url.set_full_path(path);
+  uri->assign(url.url());
+  return url.valid();
+}
+
+bool HttpRequestData::getRelativeUri(std::string* host,
+                                     std::string* path) const
+{
+  if (HV_CONNECT == verb)
+    return false;
+  Url<char> url(this->path);
+  if (url.valid()) {
+    host->assign(url.address());
+    path->assign(url.full_path());
+    return true;
+  }
+  if (!hasHeader(HH_HOST, host))
+    return false;
+  path->assign(this->path);
+  return true;
+}
+
+//
+// HttpResponseData
+//
+
+void
+HttpResponseData::clear(bool release_document) {
+  scode = HC_INTERNAL_SERVER_ERROR;
+  message.clear();
+  HttpData::clear(release_document);
+}
+
+void
+HttpResponseData::copy(const HttpResponseData& src) {
+  scode = src.scode;
+  message = src.message;
+  HttpData::copy(src);
+}
+
+void HttpResponseData::set_success(uint32_t scode) {
+  this->scode = scode;
+  message.clear();
+  setHeader(HH_CONTENT_LENGTH, "0", false);
+}
+
+void HttpResponseData::set_success(const std::string& content_type,
+                                   StreamInterface* document,
+                                   uint32_t scode) {
+  this->scode = scode;
+  message.erase(message.begin(), message.end());
+  setContent(content_type, document);
+}
+
+void HttpResponseData::set_redirect(const std::string& location,
+                                    uint32_t scode) {
+  this->scode = scode;
+  message.clear();
+  setHeader(HH_LOCATION, location);
+  setHeader(HH_CONTENT_LENGTH, "0", false);
+}
+
+void HttpResponseData::set_error(uint32_t scode) {
+  this->scode = scode;
+  message.clear();
+  setHeader(HH_CONTENT_LENGTH, "0", false);
+}
+
+size_t
+HttpResponseData::formatLeader(char* buffer, size_t size) const {
+  size_t len = sprintfn(buffer, size, "HTTP/%s %lu", ToString(version), scode);
+  if (!message.empty()) {
+    len += sprintfn(buffer + len, size - len, " %.*s",
+                    message.size(), message.data());
+  }
+  return len;
+}
+
+HttpError
+HttpResponseData::parseLeader(const char* line, size_t len) {
+  size_t pos = 0;
+  unsigned int vmajor, vminor, temp_scode;
+  int temp_pos;
+  // sscanf isn't safe with strings that aren't null-terminated, and there is
+  // no guarantee that |line| is. Create a local copy that is null-terminated.
+  std::string line_str(line, len);
+  line = line_str.c_str();
+  if (sscanf(line, "HTTP %u%n",
+             &temp_scode, &temp_pos) == 1) {
+    // This server's response has no version. :( NOTE: This happens for every
+    // response to requests made from Chrome plugins, regardless of the server's
+    // behaviour.
+    LOG(LS_VERBOSE) << "HTTP version missing from response";
+    version = HVER_UNKNOWN;
+  } else if ((sscanf(line, "HTTP/%u.%u %u%n",
+                     &vmajor, &vminor, &temp_scode, &temp_pos) == 3)
+             && (vmajor == 1)) {
+    // This server's response does have a version.
+    if (vminor == 0) {
+      version = HVER_1_0;
+    } else if (vminor == 1) {
+      version = HVER_1_1;
+    } else {
+      return HE_PROTOCOL;
+    }
+  } else {
+    return HE_PROTOCOL;
+  }
+  scode = temp_scode;
+  pos = static_cast<size_t>(temp_pos);
+  while ((pos < len) && isspace(static_cast<unsigned char>(line[pos]))) ++pos;
+  message.assign(line + pos, len - pos);
+  return HE_NONE;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Http Authentication
+//////////////////////////////////////////////////////////////////////
+
+std::string quote(const std::string& str) {
+  std::string result;
+  result.push_back('"');
+  for (size_t i=0; i<str.size(); ++i) {
+    if ((str[i] == '"') || (str[i] == '\\'))
+      result.push_back('\\');
+    result.push_back(str[i]);
+  }
+  result.push_back('"');
+  return result;
+}
+
+#if defined(WEBRTC_WIN)
+struct NegotiateAuthContext : public HttpAuthContext {
+  CredHandle cred;
+  CtxtHandle ctx;
+  size_t steps;
+  bool specified_credentials;
+
+  NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2)
+  : HttpAuthContext(auth), cred(c1), ctx(c2), steps(0),
+    specified_credentials(false)
+  { }
+
+  virtual ~NegotiateAuthContext() {
+    DeleteSecurityContext(&ctx);
+    FreeCredentialsHandle(&cred);
+  }
+};
+#endif // WEBRTC_WIN
+
+HttpAuthResult HttpAuthenticate(
+  const char * challenge, size_t len,
+  const SocketAddress& server,
+  const std::string& method, const std::string& uri,
+  const std::string& username, const CryptString& password,
+  HttpAuthContext *& context, std::string& response, std::string& auth_method)
+{
+  HttpAttributeList args;
+  HttpParseAttributes(challenge, len, args);
+  HttpHasNthAttribute(args, 0, &auth_method, nullptr);
+
+  if (context && (context->auth_method != auth_method))
+    return HAR_IGNORE;
+
+  // BASIC
+  if (_stricmp(auth_method.c_str(), "basic") == 0) {
+    if (context)
+      return HAR_CREDENTIALS; // Bad credentials
+    if (username.empty())
+      return HAR_CREDENTIALS; // Missing credentials
+
+    context = new HttpAuthContext(auth_method);
+
+    // TODO: convert sensitive to a secure buffer that gets securely deleted
+    //std::string decoded = username + ":" + password;
+    size_t len = username.size() + password.GetLength() + 2;
+    char * sensitive = new char[len];
+    size_t pos = strcpyn(sensitive, len, username.data(), username.size());
+    pos += strcpyn(sensitive + pos, len - pos, ":");
+    password.CopyTo(sensitive + pos, true);
+
+    response = auth_method;
+    response.append(" ");
+    // TODO: create a sensitive-source version of Base64::encode
+    response.append(Base64::Encode(sensitive));
+    memset(sensitive, 0, len);
+    delete [] sensitive;
+    return HAR_RESPONSE;
+  }
+
+  // DIGEST
+  if (_stricmp(auth_method.c_str(), "digest") == 0) {
+    if (context)
+      return HAR_CREDENTIALS; // Bad credentials
+    if (username.empty())
+      return HAR_CREDENTIALS; // Missing credentials
+
+    context = new HttpAuthContext(auth_method);
+
+    std::string cnonce, ncount;
+    char buffer[256];
+    sprintf(buffer, "%d", static_cast<int>(time(0)));
+    cnonce = MD5(buffer);
+    ncount = "00000001";
+
+    std::string realm, nonce, qop, opaque;
+    HttpHasAttribute(args, "realm", &realm);
+    HttpHasAttribute(args, "nonce", &nonce);
+    bool has_qop = HttpHasAttribute(args, "qop", &qop);
+    bool has_opaque = HttpHasAttribute(args, "opaque", &opaque);
+
+    // TODO: convert sensitive to be secure buffer
+    //std::string A1 = username + ":" + realm + ":" + password;
+    size_t len = username.size() + realm.size() + password.GetLength() + 3;
+    char * sensitive = new char[len];  // A1
+    size_t pos = strcpyn(sensitive, len, username.data(), username.size());
+    pos += strcpyn(sensitive + pos, len - pos, ":");
+    pos += strcpyn(sensitive + pos, len - pos, realm.c_str());
+    pos += strcpyn(sensitive + pos, len - pos, ":");
+    password.CopyTo(sensitive + pos, true);
+
+    std::string A2 = method + ":" + uri;
+    std::string middle;
+    if (has_qop) {
+      qop = "auth";
+      middle = nonce + ":" + ncount + ":" + cnonce + ":" + qop;
+    } else {
+      middle = nonce;
+    }
+    std::string HA1 = MD5(sensitive);
+    memset(sensitive, 0, len);
+    delete [] sensitive;
+    std::string HA2 = MD5(A2);
+    std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2);
+
+    std::stringstream ss;
+    ss << auth_method;
+    ss << " username=" << quote(username);
+    ss << ", realm=" << quote(realm);
+    ss << ", nonce=" << quote(nonce);
+    ss << ", uri=" << quote(uri);
+    if (has_qop) {
+      ss << ", qop=" << qop;
+      ss << ", nc="  << ncount;
+      ss << ", cnonce=" << quote(cnonce);
+    }
+    ss << ", response=\"" << dig_response << "\"";
+    if (has_opaque) {
+      ss << ", opaque=" << quote(opaque);
+    }
+    response = ss.str();
+    return HAR_RESPONSE;
+  }
+
+#if defined(WEBRTC_WIN)
+#if 1
+  bool want_negotiate = (_stricmp(auth_method.c_str(), "negotiate") == 0);
+  bool want_ntlm = (_stricmp(auth_method.c_str(), "ntlm") == 0);
+  // SPNEGO & NTLM
+  if (want_negotiate || want_ntlm) {
+    const size_t MAX_MESSAGE = 12000, MAX_SPN = 256;
+    char out_buf[MAX_MESSAGE], spn[MAX_SPN];
+
+#if 0 // Requires funky windows versions
+    DWORD len = MAX_SPN;
+    if (DsMakeSpn("HTTP", server.HostAsURIString().c_str(), nullptr,
+                  server.port(),
+                  0, &len, spn) != ERROR_SUCCESS) {
+      LOG_F(WARNING) << "(Negotiate) - DsMakeSpn failed";
+      return HAR_IGNORE;
+    }
+#else
+    sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str());
+#endif
+
+    SecBuffer out_sec;
+    out_sec.pvBuffer   = out_buf;
+    out_sec.cbBuffer   = sizeof(out_buf);
+    out_sec.BufferType = SECBUFFER_TOKEN;
+
+    SecBufferDesc out_buf_desc;
+    out_buf_desc.ulVersion = 0;
+    out_buf_desc.cBuffers  = 1;
+    out_buf_desc.pBuffers  = &out_sec;
+
+    const ULONG NEG_FLAGS_DEFAULT =
+      //ISC_REQ_ALLOCATE_MEMORY
+      ISC_REQ_CONFIDENTIALITY
+      //| ISC_REQ_EXTENDED_ERROR
+      //| ISC_REQ_INTEGRITY
+      | ISC_REQ_REPLAY_DETECT
+      | ISC_REQ_SEQUENCE_DETECT
+      //| ISC_REQ_STREAM
+      //| ISC_REQ_USE_SUPPLIED_CREDS
+      ;
+
+    ::TimeStamp lifetime;
+    SECURITY_STATUS ret = S_OK;
+    ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT;
+
+    bool specify_credentials = !username.empty();
+    size_t steps = 0;
+
+    // uint32_t now = Time();
+
+    NegotiateAuthContext * neg = static_cast<NegotiateAuthContext *>(context);
+    if (neg) {
+      const size_t max_steps = 10;
+      if (++neg->steps >= max_steps) {
+        LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries";
+        return HAR_ERROR;
+      }
+      steps = neg->steps;
+
+      std::string challenge, decoded_challenge;
+      if (HttpHasNthAttribute(args, 1, &challenge, nullptr) &&
+          Base64::Decode(challenge, Base64::DO_STRICT, &decoded_challenge,
+                         nullptr)) {
+        SecBuffer in_sec;
+        in_sec.pvBuffer   = const_cast<char *>(decoded_challenge.data());
+        in_sec.cbBuffer   = static_cast<unsigned long>(decoded_challenge.size());
+        in_sec.BufferType = SECBUFFER_TOKEN;
+
+        SecBufferDesc in_buf_desc;
+        in_buf_desc.ulVersion = 0;
+        in_buf_desc.cBuffers  = 1;
+        in_buf_desc.pBuffers  = &in_sec;
+
+        ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime);
+        //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeSince(now);
+        if (FAILED(ret)) {
+          LOG(LS_ERROR) << "InitializeSecurityContext returned: "
+                      << ErrorName(ret, SECURITY_ERRORS);
+          return HAR_ERROR;
+        }
+      } else if (neg->specified_credentials) {
+        // Try again with default credentials
+        specify_credentials = false;
+        delete context;
+        context = neg = 0;
+      } else {
+        return HAR_CREDENTIALS;
+      }
+    }
+
+    if (!neg) {
+      unsigned char userbuf[256], passbuf[256], domainbuf[16];
+      SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0;
+      if (specify_credentials) {
+        memset(&auth_id, 0, sizeof(auth_id));
+        size_t len = password.GetLength()+1;
+        char * sensitive = new char[len];
+        password.CopyTo(sensitive, true);
+        std::string::size_type pos = username.find('\\');
+        if (pos == std::string::npos) {
+          auth_id.UserLength = static_cast<unsigned long>(
+              std::min(sizeof(userbuf) - 1, username.size()));
+          memcpy(userbuf, username.c_str(), auth_id.UserLength);
+          userbuf[auth_id.UserLength] = 0;
+          auth_id.DomainLength = 0;
+          domainbuf[auth_id.DomainLength] = 0;
+          auth_id.PasswordLength = static_cast<unsigned long>(
+              std::min(sizeof(passbuf) - 1, password.GetLength()));
+          memcpy(passbuf, sensitive, auth_id.PasswordLength);
+          passbuf[auth_id.PasswordLength] = 0;
+        } else {
+          auth_id.UserLength = static_cast<unsigned long>(
+              std::min(sizeof(userbuf) - 1, username.size() - pos - 1));
+          memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength);
+          userbuf[auth_id.UserLength] = 0;
+          auth_id.DomainLength =
+              static_cast<unsigned long>(std::min(sizeof(domainbuf) - 1, pos));
+          memcpy(domainbuf, username.c_str(), auth_id.DomainLength);
+          domainbuf[auth_id.DomainLength] = 0;
+          auth_id.PasswordLength = static_cast<unsigned long>(
+              std::min(sizeof(passbuf) - 1, password.GetLength()));
+          memcpy(passbuf, sensitive, auth_id.PasswordLength);
+          passbuf[auth_id.PasswordLength] = 0;
+        }
+        memset(sensitive, 0, len);
+        delete [] sensitive;
+        auth_id.User = userbuf;
+        auth_id.Domain = domainbuf;
+        auth_id.Password = passbuf;
+        auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+        pauth_id = &auth_id;
+        LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials";
+      } else {
+        LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials";
+      }
+
+      CredHandle cred;
+      ret = AcquireCredentialsHandleA(
+          0, const_cast<char*>(want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A),
+          SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime);
+      //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << TimeSince(now);
+      if (ret != SEC_E_OK) {
+        LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
+                    << ErrorName(ret, SECURITY_ERRORS);
+        return HAR_IGNORE;
+      }
+
+      //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out;
+
+      CtxtHandle ctx;
+      ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime);
+      //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeSince(now);
+      if (FAILED(ret)) {
+        LOG(LS_ERROR) << "InitializeSecurityContext returned: "
+                    << ErrorName(ret, SECURITY_ERRORS);
+        FreeCredentialsHandle(&cred);
+        return HAR_IGNORE;
+      }
+
+      RTC_DCHECK(!context);
+      context = neg = new NegotiateAuthContext(auth_method, cred, ctx);
+      neg->specified_credentials = specify_credentials;
+      neg->steps = steps;
+    }
+
+    if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) {
+      ret = CompleteAuthToken(&neg->ctx, &out_buf_desc);
+      //LOG(INFO) << "$$$ CompleteAuthToken @ " << TimeSince(now);
+      LOG(LS_VERBOSE) << "CompleteAuthToken returned: "
+                      << ErrorName(ret, SECURITY_ERRORS);
+      if (FAILED(ret)) {
+        return HAR_ERROR;
+      }
+    }
+
+    //LOG(INFO) << "$$$ NEGOTIATE took " << TimeSince(now) << "ms";
+
+    std::string decoded(out_buf, out_buf + out_sec.cbBuffer);
+    response = auth_method;
+    response.append(" ");
+    response.append(Base64::Encode(decoded));
+    return HAR_RESPONSE;
+  }
+#endif
+#endif // WEBRTC_WIN
+
+  return HAR_IGNORE;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace rtc
diff --git a/base/httpcommon.h b/base/httpcommon.h
index 3946dfc..7182aa2 100644
--- a/base/httpcommon.h
+++ b/base/httpcommon.h
@@ -8,12 +8,451 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_HTTPCOMMON_H_
-#define WEBRTC_BASE_HTTPCOMMON_H_
+#ifndef WEBRTC_BASE_HTTPCOMMON_H__
+#define WEBRTC_BASE_HTTPCOMMON_H__
 
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/stream.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/httpcommon.h"
+namespace rtc {
 
-#endif // WEBRTC_BASE_HTTPCOMMON_H_
+class CryptString;
+class SocketAddress;
+
+//////////////////////////////////////////////////////////////////////
+// Constants
+//////////////////////////////////////////////////////////////////////
+
+enum HttpCode {
+  HC_OK = 200,
+  HC_NON_AUTHORITATIVE = 203,
+  HC_NO_CONTENT = 204,
+  HC_PARTIAL_CONTENT = 206,
+
+  HC_MULTIPLE_CHOICES = 300,
+  HC_MOVED_PERMANENTLY = 301,
+  HC_FOUND = 302,
+  HC_SEE_OTHER = 303,
+  HC_NOT_MODIFIED = 304,
+  HC_MOVED_TEMPORARILY = 307,
+
+  HC_BAD_REQUEST = 400,
+  HC_UNAUTHORIZED = 401,
+  HC_FORBIDDEN = 403,
+  HC_NOT_FOUND = 404,
+  HC_PROXY_AUTHENTICATION_REQUIRED = 407,
+  HC_GONE = 410,
+
+  HC_INTERNAL_SERVER_ERROR = 500,
+  HC_NOT_IMPLEMENTED = 501,
+  HC_SERVICE_UNAVAILABLE = 503,
+};
+
+enum HttpVersion {
+  HVER_1_0, HVER_1_1, HVER_UNKNOWN,
+  HVER_LAST = HVER_UNKNOWN
+};
+
+enum HttpVerb {
+  HV_GET, HV_POST, HV_PUT, HV_DELETE, HV_CONNECT, HV_HEAD,
+  HV_LAST = HV_HEAD
+};
+
+enum HttpError {
+  HE_NONE,
+  HE_PROTOCOL,            // Received non-valid HTTP data
+  HE_DISCONNECTED,        // Connection closed unexpectedly
+  HE_OVERFLOW,            // Received too much data for internal buffers
+  HE_CONNECT_FAILED,      // The socket failed to connect.
+  HE_SOCKET_ERROR,        // An error occurred on a connected socket
+  HE_SHUTDOWN,            // Http object is being destroyed
+  HE_OPERATION_CANCELLED, // Connection aborted locally
+  HE_AUTH,                // Proxy Authentication Required
+  HE_CERTIFICATE_EXPIRED, // During SSL negotiation
+  HE_STREAM,              // Problem reading or writing to the document
+  HE_CACHE,               // Problem reading from cache
+  HE_DEFAULT
+};
+
+enum HttpHeader {
+  HH_AGE,
+  HH_CACHE_CONTROL,
+  HH_CONNECTION,
+  HH_CONTENT_DISPOSITION,
+  HH_CONTENT_LENGTH,
+  HH_CONTENT_RANGE,
+  HH_CONTENT_TYPE,
+  HH_COOKIE,
+  HH_DATE,
+  HH_ETAG,
+  HH_EXPIRES,
+  HH_HOST,
+  HH_IF_MODIFIED_SINCE,
+  HH_IF_NONE_MATCH,
+  HH_KEEP_ALIVE,
+  HH_LAST_MODIFIED,
+  HH_LOCATION,
+  HH_PROXY_AUTHENTICATE,
+  HH_PROXY_AUTHORIZATION,
+  HH_PROXY_CONNECTION,
+  HH_RANGE,
+  HH_SET_COOKIE,
+  HH_TE,
+  HH_TRAILERS,
+  HH_TRANSFER_ENCODING,
+  HH_UPGRADE,
+  HH_USER_AGENT,
+  HH_WWW_AUTHENTICATE,
+  HH_LAST = HH_WWW_AUTHENTICATE
+};
+
+const uint16_t HTTP_DEFAULT_PORT = 80;
+const uint16_t HTTP_SECURE_PORT = 443;
+
+//////////////////////////////////////////////////////////////////////
+// Utility Functions
+//////////////////////////////////////////////////////////////////////
+
+inline HttpError mkerr(HttpError err, HttpError def_err = HE_DEFAULT) {
+  return (err != HE_NONE) ? err : def_err;
+}
+
+const char* ToString(HttpVersion version);
+bool FromString(HttpVersion& version, const std::string& str);
+
+const char* ToString(HttpVerb verb);
+bool FromString(HttpVerb& verb, const std::string& str);
+
+const char* ToString(HttpHeader header);
+bool FromString(HttpHeader& header, const std::string& str);
+
+inline bool HttpCodeIsInformational(uint32_t code) {
+  return ((code / 100) == 1);
+}
+inline bool HttpCodeIsSuccessful(uint32_t code) {
+  return ((code / 100) == 2);
+}
+inline bool HttpCodeIsRedirection(uint32_t code) {
+  return ((code / 100) == 3);
+}
+inline bool HttpCodeIsClientError(uint32_t code) {
+  return ((code / 100) == 4);
+}
+inline bool HttpCodeIsServerError(uint32_t code) {
+  return ((code / 100) == 5);
+}
+
+bool HttpCodeHasBody(uint32_t code);
+bool HttpCodeIsCacheable(uint32_t code);
+bool HttpHeaderIsEndToEnd(HttpHeader header);
+bool HttpHeaderIsCollapsible(HttpHeader header);
+
+struct HttpData;
+bool HttpShouldKeepAlive(const HttpData& data);
+
+typedef std::pair<std::string, std::string> HttpAttribute;
+typedef std::vector<HttpAttribute> HttpAttributeList;
+void HttpComposeAttributes(const HttpAttributeList& attributes, char separator,
+                           std::string* composed);
+void HttpParseAttributes(const char * data, size_t len,
+                         HttpAttributeList& attributes);
+bool HttpHasAttribute(const HttpAttributeList& attributes,
+                      const std::string& name,
+                      std::string* value);
+bool HttpHasNthAttribute(HttpAttributeList& attributes,
+                         size_t index,
+                         std::string* name,
+                         std::string* value);
+
+// Convert RFC1123 date (DoW, DD Mon YYYY HH:MM:SS TZ) to unix timestamp
+bool HttpDateToSeconds(const std::string& date, time_t* seconds);
+
+inline uint16_t HttpDefaultPort(bool secure) {
+  return secure ? HTTP_SECURE_PORT : HTTP_DEFAULT_PORT;
+}
+
+// Returns the http server notation for a given address
+std::string HttpAddress(const SocketAddress& address, bool secure);
+
+// functional for insensitive std::string compare
+struct iless {
+  bool operator()(const std::string& lhs, const std::string& rhs) const {
+    return (::_stricmp(lhs.c_str(), rhs.c_str()) < 0);
+  }
+};
+
+// put quotes around a string and escape any quotes inside it
+std::string quote(const std::string& str);
+
+//////////////////////////////////////////////////////////////////////
+// Url
+//////////////////////////////////////////////////////////////////////
+
+template<class CTYPE>
+class Url {
+public:
+  typedef typename Traits<CTYPE>::string string;
+
+  // TODO: Implement Encode/Decode
+  static int Encode(const CTYPE* source, CTYPE* destination, size_t len);
+  static int Encode(const string& source, string& destination);
+  static int Decode(const CTYPE* source, CTYPE* destination, size_t len);
+  static int Decode(const string& source, string& destination);
+
+  Url(const string& url) { do_set_url(url.c_str(), url.size()); }
+  Url(const string& path, const string& host, uint16_t port = HTTP_DEFAULT_PORT)
+      : host_(host), port_(port), secure_(HTTP_SECURE_PORT == port) {
+    set_full_path(path);
+  }
+
+  bool valid() const { return !host_.empty(); }
+  void clear() {
+    host_.clear();
+    port_ = HTTP_DEFAULT_PORT;
+    secure_ = false;
+    path_.assign(1, static_cast<CTYPE>('/'));
+    query_.clear();
+  }
+
+  void set_url(const string& val) {
+    do_set_url(val.c_str(), val.size());
+  }
+  string url() const {
+    string val; do_get_url(&val); return val;
+  }
+
+  void set_address(const string& val) {
+    do_set_address(val.c_str(), val.size());
+  }
+  string address() const {
+    string val; do_get_address(&val); return val;
+  }
+
+  void set_full_path(const string& val) {
+    do_set_full_path(val.c_str(), val.size());
+  }
+  string full_path() const {
+    string val; do_get_full_path(&val); return val;
+  }
+
+  void set_host(const string& val) { host_ = val; }
+  const string& host() const { return host_; }
+
+  void set_port(uint16_t val) { port_ = val; }
+  uint16_t port() const { return port_; }
+
+  void set_secure(bool val) { secure_ = val; }
+  bool secure() const { return secure_; }
+
+  void set_path(const string& val) {
+    if (val.empty()) {
+      path_.assign(1, static_cast<CTYPE>('/'));
+    } else {
+      RTC_DCHECK(val[0] == static_cast<CTYPE>('/'));
+      path_ = val;
+    }
+  }
+  const string& path() const { return path_; }
+
+  void set_query(const string& val) {
+    RTC_DCHECK(val.empty() || (val[0] == static_cast<CTYPE>('?')));
+    query_ = val;
+  }
+  const string& query() const { return query_; }
+
+  bool get_attribute(const string& name, string* value) const;
+
+private:
+  void do_set_url(const CTYPE* val, size_t len);
+  void do_set_address(const CTYPE* val, size_t len);
+  void do_set_full_path(const CTYPE* val, size_t len);
+
+  void do_get_url(string* val) const;
+  void do_get_address(string* val) const;
+  void do_get_full_path(string* val) const;
+
+  string host_, path_, query_;
+  uint16_t port_;
+  bool secure_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// HttpData
+//////////////////////////////////////////////////////////////////////
+
+struct HttpData {
+  typedef std::multimap<std::string, std::string, iless> HeaderMap;
+  typedef HeaderMap::const_iterator const_iterator;
+  typedef HeaderMap::iterator iterator;
+
+  HttpVersion version;
+  std::unique_ptr<StreamInterface> document;
+
+  HttpData();
+
+  enum HeaderCombine { HC_YES, HC_NO, HC_AUTO, HC_REPLACE, HC_NEW };
+  void changeHeader(const std::string& name, const std::string& value,
+                    HeaderCombine combine);
+  inline void addHeader(const std::string& name, const std::string& value,
+                        bool append = true) {
+    changeHeader(name, value, append ? HC_AUTO : HC_NO);
+  }
+  inline void setHeader(const std::string& name, const std::string& value,
+                        bool overwrite = true) {
+    changeHeader(name, value, overwrite ? HC_REPLACE : HC_NEW);
+  }
+  // Returns count of erased headers
+  size_t clearHeader(const std::string& name);
+  // Returns iterator to next header
+  iterator clearHeader(iterator header);
+
+  // keep in mind, this may not do what you want in the face of multiple headers
+  bool hasHeader(const std::string& name, std::string* value) const;
+
+  inline const_iterator begin() const {
+    return headers_.begin();
+  }
+  inline const_iterator end() const {
+    return headers_.end();
+  }
+  inline iterator begin() {
+    return headers_.begin();
+  }
+  inline iterator end() {
+    return headers_.end();
+  }
+  inline const_iterator begin(const std::string& name) const {
+    return headers_.lower_bound(name);
+  }
+  inline const_iterator end(const std::string& name) const {
+    return headers_.upper_bound(name);
+  }
+  inline iterator begin(const std::string& name) {
+    return headers_.lower_bound(name);
+  }
+  inline iterator end(const std::string& name) {
+    return headers_.upper_bound(name);
+  }
+
+  // Convenience methods using HttpHeader
+  inline void changeHeader(HttpHeader header, const std::string& value,
+                           HeaderCombine combine) {
+    changeHeader(ToString(header), value, combine);
+  }
+  inline void addHeader(HttpHeader header, const std::string& value,
+                        bool append = true) {
+    addHeader(ToString(header), value, append);
+  }
+  inline void setHeader(HttpHeader header, const std::string& value,
+                        bool overwrite = true) {
+    setHeader(ToString(header), value, overwrite);
+  }
+  inline void clearHeader(HttpHeader header) {
+    clearHeader(ToString(header));
+  }
+  inline bool hasHeader(HttpHeader header, std::string* value) const {
+    return hasHeader(ToString(header), value);
+  }
+  inline const_iterator begin(HttpHeader header) const {
+    return headers_.lower_bound(ToString(header));
+  }
+  inline const_iterator end(HttpHeader header) const {
+    return headers_.upper_bound(ToString(header));
+  }
+  inline iterator begin(HttpHeader header) {
+    return headers_.lower_bound(ToString(header));
+  }
+  inline iterator end(HttpHeader header) {
+    return headers_.upper_bound(ToString(header));
+  }
+
+  void setContent(const std::string& content_type, StreamInterface* document);
+  void setDocumentAndLength(StreamInterface* document);
+
+  virtual size_t formatLeader(char* buffer, size_t size) const = 0;
+  virtual HttpError parseLeader(const char* line, size_t len) = 0;
+
+protected:
+ virtual ~HttpData();
+  void clear(bool release_document);
+  void copy(const HttpData& src);
+
+private:
+  HeaderMap headers_;
+};
+
+struct HttpRequestData : public HttpData {
+  HttpVerb verb;
+  std::string path;
+
+  HttpRequestData() : verb(HV_GET) { }
+
+  void clear(bool release_document);
+  void copy(const HttpRequestData& src);
+
+  size_t formatLeader(char* buffer, size_t size) const override;
+  HttpError parseLeader(const char* line, size_t len) override;
+
+  bool getAbsoluteUri(std::string* uri) const;
+  bool getRelativeUri(std::string* host, std::string* path) const;
+};
+
+struct HttpResponseData : public HttpData {
+  uint32_t scode;
+  std::string message;
+
+  HttpResponseData() : scode(HC_INTERNAL_SERVER_ERROR) { }
+  void clear(bool release_document);
+  void copy(const HttpResponseData& src);
+
+  // Convenience methods
+  void set_success(uint32_t scode = HC_OK);
+  void set_success(const std::string& content_type,
+                   StreamInterface* document,
+                   uint32_t scode = HC_OK);
+  void set_redirect(const std::string& location,
+                    uint32_t scode = HC_MOVED_TEMPORARILY);
+  void set_error(uint32_t scode);
+
+  size_t formatLeader(char* buffer, size_t size) const override;
+  HttpError parseLeader(const char* line, size_t len) override;
+};
+
+struct HttpTransaction {
+  HttpRequestData request;
+  HttpResponseData response;
+};
+
+//////////////////////////////////////////////////////////////////////
+// Http Authentication
+//////////////////////////////////////////////////////////////////////
+
+struct HttpAuthContext {
+  std::string auth_method;
+  HttpAuthContext(const std::string& auth) : auth_method(auth) { }
+  virtual ~HttpAuthContext() { }
+};
+
+enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR };
+
+// 'context' is used by this function to record information between calls.
+// Start by passing a null pointer, then pass the same pointer each additional
+// call.  When the authentication attempt is finished, delete the context.
+HttpAuthResult HttpAuthenticate(
+  const char * challenge, size_t len,
+  const SocketAddress& server,
+  const std::string& method, const std::string& uri,
+  const std::string& username, const CryptString& password,
+  HttpAuthContext *& context, std::string& response, std::string& auth_method);
+
+//////////////////////////////////////////////////////////////////////
+
+} // namespace rtc
+
+#endif // WEBRTC_BASE_HTTPCOMMON_H__
diff --git a/base/httpcommon_unittest.cc b/base/httpcommon_unittest.cc
new file mode 100644
index 0000000..10e3789
--- /dev/null
+++ b/base/httpcommon_unittest.cc
@@ -0,0 +1,165 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/httpcommon-inl.h"
+#include "webrtc/base/httpcommon.h"
+
+namespace rtc {
+
+#define TEST_PROTOCOL "http://"
+#define TEST_HOST "www.google.com"
+#define TEST_PATH "/folder/file.html"
+#define TEST_QUERY "?query=x&attr=y"
+#define TEST_URL TEST_PROTOCOL TEST_HOST TEST_PATH TEST_QUERY
+
+TEST(Url, DecomposesUrls) {
+  Url<char> url(TEST_URL);
+  EXPECT_TRUE(url.valid());
+  EXPECT_FALSE(url.secure());
+  EXPECT_STREQ(TEST_HOST, url.host().c_str());
+  EXPECT_EQ(80, url.port());
+  EXPECT_STREQ(TEST_PATH, url.path().c_str());
+  EXPECT_STREQ(TEST_QUERY, url.query().c_str());
+  EXPECT_STREQ(TEST_HOST, url.address().c_str());
+  EXPECT_STREQ(TEST_PATH TEST_QUERY, url.full_path().c_str());
+  EXPECT_STREQ(TEST_URL, url.url().c_str());
+}
+
+TEST(Url, ComposesUrls) {
+  // Set in constructor
+  Url<char> url(TEST_PATH TEST_QUERY, TEST_HOST, 80);
+  EXPECT_TRUE(url.valid());
+  EXPECT_FALSE(url.secure());
+  EXPECT_STREQ(TEST_HOST, url.host().c_str());
+  EXPECT_EQ(80, url.port());
+  EXPECT_STREQ(TEST_PATH, url.path().c_str());
+  EXPECT_STREQ(TEST_QUERY, url.query().c_str());
+  EXPECT_STREQ(TEST_HOST, url.address().c_str());
+  EXPECT_STREQ(TEST_PATH TEST_QUERY, url.full_path().c_str());
+  EXPECT_STREQ(TEST_URL, url.url().c_str());
+
+  url.clear();
+  EXPECT_FALSE(url.valid());
+  EXPECT_FALSE(url.secure());
+  EXPECT_STREQ("", url.host().c_str());
+  EXPECT_EQ(80, url.port());
+  EXPECT_STREQ("/", url.path().c_str());
+  EXPECT_STREQ("", url.query().c_str());
+
+  // Set component-wise
+  url.set_host(TEST_HOST);
+  url.set_port(80);
+  url.set_path(TEST_PATH);
+  url.set_query(TEST_QUERY);
+  EXPECT_TRUE(url.valid());
+  EXPECT_FALSE(url.secure());
+  EXPECT_STREQ(TEST_HOST, url.host().c_str());
+  EXPECT_EQ(80, url.port());
+  EXPECT_STREQ(TEST_PATH, url.path().c_str());
+  EXPECT_STREQ(TEST_QUERY, url.query().c_str());
+  EXPECT_STREQ(TEST_HOST, url.address().c_str());
+  EXPECT_STREQ(TEST_PATH TEST_QUERY, url.full_path().c_str());
+  EXPECT_STREQ(TEST_URL, url.url().c_str());
+}
+
+TEST(Url, EnsuresNonEmptyPath) {
+  Url<char> url(TEST_PROTOCOL TEST_HOST);
+  EXPECT_TRUE(url.valid());
+  EXPECT_STREQ("/", url.path().c_str());
+
+  url.clear();
+  EXPECT_STREQ("/", url.path().c_str());
+  url.set_path("");
+  EXPECT_STREQ("/", url.path().c_str());
+
+  url.clear();
+  EXPECT_STREQ("/", url.path().c_str());
+  url.set_full_path("");
+  EXPECT_STREQ("/", url.path().c_str());
+}
+
+TEST(Url, GetQueryAttributes) {
+  Url<char> url(TEST_URL);
+  std::string value;
+  EXPECT_TRUE(url.get_attribute("query", &value));
+  EXPECT_STREQ("x", value.c_str());
+  value.clear();
+  EXPECT_TRUE(url.get_attribute("attr", &value));
+  EXPECT_STREQ("y", value.c_str());
+  value.clear();
+  EXPECT_FALSE(url.get_attribute("Query", &value));
+  EXPECT_TRUE(value.empty());
+}
+
+TEST(Url, SkipsUserAndPassword) {
+  Url<char> url("https://mail.google.com:pwd@badsite.com:12345/asdf");
+  EXPECT_TRUE(url.valid());
+  EXPECT_TRUE(url.secure());
+  EXPECT_STREQ("badsite.com", url.host().c_str());
+  EXPECT_EQ(12345, url.port());
+  EXPECT_STREQ("/asdf", url.path().c_str());
+  EXPECT_STREQ("badsite.com:12345", url.address().c_str());
+}
+
+TEST(Url, SkipsUser) {
+  Url<char> url("https://mail.google.com@badsite.com:12345/asdf");
+  EXPECT_TRUE(url.valid());
+  EXPECT_TRUE(url.secure());
+  EXPECT_STREQ("badsite.com", url.host().c_str());
+  EXPECT_EQ(12345, url.port());
+  EXPECT_STREQ("/asdf", url.path().c_str());
+  EXPECT_STREQ("badsite.com:12345", url.address().c_str());
+}
+
+TEST(HttpResponseData, parseLeaderHttp1_0) {
+  static const char kResponseString[] = "HTTP/1.0 200 OK";
+  HttpResponseData response;
+  EXPECT_EQ(HE_NONE, response.parseLeader(kResponseString,
+                                          sizeof(kResponseString) - 1));
+  EXPECT_EQ(HVER_1_0, response.version);
+  EXPECT_EQ(200U, response.scode);
+}
+
+TEST(HttpResponseData, parseLeaderHttp1_1) {
+  static const char kResponseString[] = "HTTP/1.1 200 OK";
+  HttpResponseData response;
+  EXPECT_EQ(HE_NONE, response.parseLeader(kResponseString,
+                                          sizeof(kResponseString) - 1));
+  EXPECT_EQ(HVER_1_1, response.version);
+  EXPECT_EQ(200U, response.scode);
+}
+
+TEST(HttpResponseData, parseLeaderHttpUnknown) {
+  static const char kResponseString[] = "HTTP 200 OK";
+  HttpResponseData response;
+  EXPECT_EQ(HE_NONE, response.parseLeader(kResponseString,
+                                          sizeof(kResponseString) - 1));
+  EXPECT_EQ(HVER_UNKNOWN, response.version);
+  EXPECT_EQ(200U, response.scode);
+}
+
+TEST(HttpResponseData, parseLeaderHttpFailure) {
+  static const char kResponseString[] = "HTTP/1.1 503 Service Unavailable";
+  HttpResponseData response;
+  EXPECT_EQ(HE_NONE, response.parseLeader(kResponseString,
+                                          sizeof(kResponseString) - 1));
+  EXPECT_EQ(HVER_1_1, response.version);
+  EXPECT_EQ(503U, response.scode);
+}
+
+TEST(HttpResponseData, parseLeaderHttpInvalid) {
+  static const char kResponseString[] = "Durrrrr, what's HTTP?";
+  HttpResponseData response;
+  EXPECT_EQ(HE_PROTOCOL, response.parseLeader(kResponseString,
+                                              sizeof(kResponseString) - 1));
+}
+
+} // namespace rtc
diff --git a/base/httpserver.cc b/base/httpserver.cc
new file mode 100644
index 0000000..b190691
--- /dev/null
+++ b/base/httpserver.cc
@@ -0,0 +1,288 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+
+#include "webrtc/base/httpcommon-inl.h"
+
+#include "webrtc/base/asyncsocket.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/httpserver.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/socketstream.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpServer
+///////////////////////////////////////////////////////////////////////////////
+
+HttpServer::HttpServer() : next_connection_id_(1), closing_(false) {
+}
+
+HttpServer::~HttpServer() {
+  if (closing_) {
+    LOG(LS_WARNING) << "HttpServer::CloseAll has not completed";
+  }
+  for (ConnectionMap::iterator it = connections_.begin();
+       it != connections_.end();
+       ++it) {
+    StreamInterface* stream = it->second->EndProcess();
+    delete stream;
+    delete it->second;
+  }
+}
+
+int
+HttpServer::HandleConnection(StreamInterface* stream) {
+  int connection_id = next_connection_id_++;
+  RTC_DCHECK(connection_id != HTTP_INVALID_CONNECTION_ID);
+  Connection* connection = new Connection(connection_id, this);
+  connections_.insert(ConnectionMap::value_type(connection_id, connection));
+  connection->BeginProcess(stream);
+  return connection_id;
+}
+
+void
+HttpServer::Respond(HttpServerTransaction* transaction) {
+  int connection_id = transaction->connection_id();
+  if (Connection* connection = Find(connection_id)) {
+    connection->Respond(transaction);
+  } else {
+    delete transaction;
+    // We may be tempted to SignalHttpComplete, but that implies that a
+    // connection still exists.
+  }
+}
+
+void
+HttpServer::Close(int connection_id, bool force) {
+  if (Connection* connection = Find(connection_id)) {
+    connection->InitiateClose(force);
+  }
+}
+
+void
+HttpServer::CloseAll(bool force) {
+  if (connections_.empty()) {
+    SignalCloseAllComplete(this);
+    return;
+  }
+  closing_ = true;
+  std::list<Connection*> connections;
+  for (ConnectionMap::const_iterator it = connections_.begin();
+       it != connections_.end(); ++it) {
+    connections.push_back(it->second);
+  }
+  for (std::list<Connection*>::const_iterator it = connections.begin();
+      it != connections.end(); ++it) {
+    (*it)->InitiateClose(force);
+  }
+}
+
+HttpServer::Connection*
+HttpServer::Find(int connection_id) {
+  ConnectionMap::iterator it = connections_.find(connection_id);
+  if (it == connections_.end())
+    return nullptr;
+  return it->second;
+}
+
+void
+HttpServer::Remove(int connection_id) {
+  ConnectionMap::iterator it = connections_.find(connection_id);
+  if (it == connections_.end()) {
+    RTC_NOTREACHED();
+    return;
+  }
+  Connection* connection = it->second;
+  connections_.erase(it);
+  SignalConnectionClosed(this, connection_id, connection->EndProcess());
+  delete connection;
+  if (closing_ && connections_.empty()) {
+    closing_ = false;
+    SignalCloseAllComplete(this);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpServer::Connection
+///////////////////////////////////////////////////////////////////////////////
+
+HttpServer::Connection::Connection(int connection_id, HttpServer* server)
+    : connection_id_(connection_id),
+      server_(server),
+      current_(nullptr),
+      signalling_(false),
+      close_(false) {}
+
+HttpServer::Connection::~Connection() {
+  // It's possible that an object hosted inside this transaction signalled
+  // an event which caused the connection to close.
+  Thread::Current()->Dispose(current_);
+}
+
+void
+HttpServer::Connection::BeginProcess(StreamInterface* stream) {
+  base_.notify(this);
+  base_.attach(stream);
+  current_ = new HttpServerTransaction(connection_id_);
+  if (base_.mode() != HM_CONNECT)
+    base_.recv(&current_->request);
+}
+
+StreamInterface*
+HttpServer::Connection::EndProcess() {
+  base_.notify(nullptr);
+  base_.abort(HE_DISCONNECTED);
+  return base_.detach();
+}
+
+void
+HttpServer::Connection::Respond(HttpServerTransaction* transaction) {
+  RTC_DCHECK(current_ == nullptr);
+  current_ = transaction;
+  if (current_->response.begin() == current_->response.end()) {
+    current_->response.set_error(HC_INTERNAL_SERVER_ERROR);
+  }
+  bool keep_alive = HttpShouldKeepAlive(current_->request);
+  current_->response.setHeader(HH_CONNECTION,
+                               keep_alive ? "Keep-Alive" : "Close",
+                               false);
+  close_ = !HttpShouldKeepAlive(current_->response);
+  base_.send(&current_->response);
+}
+
+void
+HttpServer::Connection::InitiateClose(bool force) {
+  bool request_in_progress = (HM_SEND == base_.mode()) || (nullptr == current_);
+  if (!signalling_ && (force || !request_in_progress)) {
+    server_->Remove(connection_id_);
+  } else {
+    close_ = true;
+  }
+}
+
+//
+// IHttpNotify Implementation
+//
+
+HttpError
+HttpServer::Connection::onHttpHeaderComplete(bool chunked, size_t& data_size) {
+  if (data_size == SIZE_UNKNOWN) {
+    data_size = 0;
+  }
+  RTC_DCHECK(current_ != nullptr);
+  bool custom_document = false;
+  server_->SignalHttpRequestHeader(server_, current_, &custom_document);
+  if (!custom_document) {
+    current_->request.document.reset(new MemoryStream);
+  }
+  return HE_NONE;
+}
+
+void
+HttpServer::Connection::onHttpComplete(HttpMode mode, HttpError err) {
+  if (mode == HM_SEND) {
+    RTC_DCHECK(current_ != nullptr);
+    signalling_ = true;
+    server_->SignalHttpRequestComplete(server_, current_, err);
+    signalling_ = false;
+    if (close_) {
+      // Force a close
+      err = HE_DISCONNECTED;
+    }
+  }
+  if (err != HE_NONE) {
+    server_->Remove(connection_id_);
+  } else if (mode == HM_CONNECT) {
+    base_.recv(&current_->request);
+  } else if (mode == HM_RECV) {
+    RTC_DCHECK(current_ != nullptr);
+    // TODO: do we need this?
+    //request_.document_->rewind();
+    HttpServerTransaction* transaction = current_;
+    current_ = nullptr;
+    server_->SignalHttpRequest(server_, transaction);
+  } else if (mode == HM_SEND) {
+    Thread::Current()->Dispose(current_->response.document.release());
+    current_->request.clear(true);
+    current_->response.clear(true);
+    base_.recv(&current_->request);
+  } else {
+    RTC_NOTREACHED();
+  }
+}
+
+void
+HttpServer::Connection::onHttpClosed(HttpError err) {
+  server_->Remove(connection_id_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// HttpListenServer
+///////////////////////////////////////////////////////////////////////////////
+
+HttpListenServer::HttpListenServer() {
+  SignalConnectionClosed.connect(this, &HttpListenServer::OnConnectionClosed);
+}
+
+HttpListenServer::~HttpListenServer() {
+}
+
+int HttpListenServer::Listen(const SocketAddress& address) {
+  AsyncSocket* sock =
+      Thread::Current()->socketserver()->CreateAsyncSocket(address.family(),
+                                                           SOCK_STREAM);
+  if (!sock) {
+    return SOCKET_ERROR;
+  }
+  listener_.reset(sock);
+  listener_->SignalReadEvent.connect(this, &HttpListenServer::OnReadEvent);
+  if ((listener_->Bind(address) != SOCKET_ERROR) &&
+      (listener_->Listen(5) != SOCKET_ERROR))
+    return 0;
+  return listener_->GetError();
+}
+
+bool HttpListenServer::GetAddress(SocketAddress* address) const {
+  if (!listener_) {
+    return false;
+  }
+  *address = listener_->GetLocalAddress();
+  return !address->IsNil();
+}
+
+void HttpListenServer::StopListening() {
+  if (listener_) {
+    listener_->Close();
+  }
+}
+
+void HttpListenServer::OnReadEvent(AsyncSocket* socket) {
+  RTC_DCHECK(socket == listener_.get());
+  AsyncSocket* incoming = listener_->Accept(nullptr);
+  if (incoming) {
+    StreamInterface* stream = new SocketStream(incoming);
+    //stream = new LoggingAdapter(stream, LS_VERBOSE, "HttpServer", false);
+    HandleConnection(stream);
+  }
+}
+
+void HttpListenServer::OnConnectionClosed(HttpServer* server,
+                                          int connection_id,
+                                          StreamInterface* stream) {
+  Thread::Current()->Dispose(stream);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
diff --git a/base/httpserver.h b/base/httpserver.h
index 4fd75a2..cbee734 100644
--- a/base/httpserver.h
+++ b/base/httpserver.h
@@ -8,12 +8,132 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_HTTPSERVER_H_
-#define WEBRTC_BASE_HTTPSERVER_H_
+#ifndef WEBRTC_BASE_HTTPSERVER_H__
+#define WEBRTC_BASE_HTTPSERVER_H__
 
+#include <map>
+#include <memory>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/httpserver.h"
+#include "webrtc/base/httpbase.h"
 
-#endif // WEBRTC_BASE_HTTPSERVER_H_
+namespace rtc {
+
+class AsyncSocket;
+class HttpServer;
+class SocketAddress;
+
+//////////////////////////////////////////////////////////////////////
+// HttpServer
+//////////////////////////////////////////////////////////////////////
+
+const int HTTP_INVALID_CONNECTION_ID = 0;
+
+struct HttpServerTransaction : public HttpTransaction {
+public:
+  HttpServerTransaction(int id) : connection_id_(id) { }
+  int connection_id() const { return connection_id_; }
+
+private:
+  int connection_id_;
+};
+
+class HttpServer {
+public:
+  HttpServer();
+  virtual ~HttpServer();
+
+  int HandleConnection(StreamInterface* stream);
+  // Due to sigslot issues, we can't destroy some streams at an arbitrary time.
+  sigslot::signal3<HttpServer*, int, StreamInterface*> SignalConnectionClosed;
+
+  // This signal occurs when the HTTP request headers have been received, but
+  // before the request body is written to the request document.  By default,
+  // the request document is a MemoryStream.  By handling this signal, the
+  // document can be overridden, in which case the third signal argument should
+  // be set to true.  In the case where the request body should be ignored,
+  // the document can be set to null.  Note that the transaction object is still
+  // owened by the HttpServer at this point.
+  sigslot::signal3<HttpServer*, HttpServerTransaction*, bool*>
+    SignalHttpRequestHeader;
+
+  // An HTTP request has been made, and is available in the transaction object.
+  // Populate the transaction's response, and then return the object via the
+  // Respond method.  Note that during this time, ownership of the transaction
+  // object is transferred, so it may be passed between threads, although
+  // respond must be called on the server's active thread.
+  sigslot::signal2<HttpServer*, HttpServerTransaction*> SignalHttpRequest;
+  void Respond(HttpServerTransaction* transaction);
+
+  // If you want to know when a request completes, listen to this event.
+  sigslot::signal3<HttpServer*, HttpServerTransaction*, int>
+    SignalHttpRequestComplete;
+
+  // Stop processing the connection indicated by connection_id.
+  // Unless force is true, the server will complete sending a response that is
+  // in progress.
+  void Close(int connection_id, bool force);
+  void CloseAll(bool force);
+
+  // After calling CloseAll, this event is signalled to indicate that all
+  // outstanding connections have closed.
+  sigslot::signal1<HttpServer*> SignalCloseAllComplete;
+
+private:
+  class Connection : private IHttpNotify {
+  public:
+    Connection(int connection_id, HttpServer* server);
+    ~Connection() override;
+
+    void BeginProcess(StreamInterface* stream);
+    StreamInterface* EndProcess();
+    
+    void Respond(HttpServerTransaction* transaction);
+    void InitiateClose(bool force);
+
+    // IHttpNotify Interface
+    HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) override;
+    void onHttpComplete(HttpMode mode, HttpError err) override;
+    void onHttpClosed(HttpError err) override;
+
+    int connection_id_;
+    HttpServer* server_;
+    HttpBase base_;
+    HttpServerTransaction* current_;
+    bool signalling_, close_;
+  };
+
+  Connection* Find(int connection_id);
+  void Remove(int connection_id);
+
+  friend class Connection;
+  typedef std::map<int,Connection*> ConnectionMap;
+
+  ConnectionMap connections_;
+  int next_connection_id_;
+  bool closing_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+class HttpListenServer : public HttpServer, public sigslot::has_slots<> {
+public:
+  HttpListenServer();
+  ~HttpListenServer() override;
+
+  int Listen(const SocketAddress& address);
+  bool GetAddress(SocketAddress* address) const;
+  void StopListening();
+
+private:
+  void OnReadEvent(AsyncSocket* socket);
+  void OnConnectionClosed(HttpServer* server, int connection_id,
+                          StreamInterface* stream);
+
+  std::unique_ptr<AsyncSocket> listener_;
+};
+
+//////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
+
+#endif // WEBRTC_BASE_HTTPSERVER_H__
diff --git a/base/httpserver_unittest.cc b/base/httpserver_unittest.cc
new file mode 100644
index 0000000..4db4ad7
--- /dev/null
+++ b/base/httpserver_unittest.cc
@@ -0,0 +1,130 @@
+/*
+ *  Copyright 2007 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/httpserver.h"
+#include "webrtc/base/testutils.h"
+
+using namespace webrtc::testing;
+
+namespace rtc {
+
+namespace {
+  const char* const kRequest =
+    "GET /index.html HTTP/1.1\r\n"
+    "Host: localhost\r\n"
+    "\r\n";
+
+  struct HttpServerMonitor : public sigslot::has_slots<> {
+    HttpServerTransaction* transaction;
+    bool server_closed, connection_closed;
+
+    HttpServerMonitor(HttpServer* server)
+        : transaction(nullptr), server_closed(false), connection_closed(false) {
+      server->SignalCloseAllComplete.connect(this,
+        &HttpServerMonitor::OnClosed);
+      server->SignalHttpRequest.connect(this, &HttpServerMonitor::OnRequest);
+      server->SignalHttpRequestComplete.connect(this,
+        &HttpServerMonitor::OnRequestComplete);
+      server->SignalConnectionClosed.connect(this,
+        &HttpServerMonitor::OnConnectionClosed);
+    }
+    void OnRequest(HttpServer*, HttpServerTransaction* t) {
+      ASSERT_FALSE(transaction);
+      transaction = t;
+      transaction->response.set_success();
+      transaction->response.setHeader(HH_CONNECTION, "Close");
+    }
+    void OnRequestComplete(HttpServer*, HttpServerTransaction* t, int) {
+      ASSERT_EQ(transaction, t);
+      transaction = nullptr;
+    }
+    void OnClosed(HttpServer*) {
+      server_closed = true;
+    }
+    void OnConnectionClosed(HttpServer*, int, StreamInterface* stream) {
+      connection_closed = true;
+      delete stream;
+    }
+  };
+
+  void CreateClientConnection(HttpServer& server,
+                              HttpServerMonitor& monitor,
+                              bool send_request) {
+    StreamSource* client = new StreamSource;
+    client->SetState(SS_OPEN);
+    server.HandleConnection(client);
+    EXPECT_FALSE(monitor.server_closed);
+    EXPECT_FALSE(monitor.transaction);
+
+    if (send_request) {
+      // Simulate a request
+      client->QueueString(kRequest);
+      EXPECT_FALSE(monitor.server_closed);
+    }
+  }
+}  // anonymous namespace
+
+TEST(HttpServer, DoesNotSignalCloseUnlessCloseAllIsCalled) {
+  HttpServer server;
+  HttpServerMonitor monitor(&server);
+  // Add an active client connection
+  CreateClientConnection(server, monitor, true);
+  // Simulate a response
+  ASSERT_TRUE(nullptr != monitor.transaction);
+  server.Respond(monitor.transaction);
+  EXPECT_FALSE(monitor.transaction);
+  // Connection has closed, but no server close signal
+  EXPECT_FALSE(monitor.server_closed);
+  EXPECT_TRUE(monitor.connection_closed);
+}
+
+TEST(HttpServer, SignalsCloseWhenNoConnectionsAreActive) {
+  HttpServer server;
+  HttpServerMonitor monitor(&server);
+  // Add an idle client connection
+  CreateClientConnection(server, monitor, false);
+  // Perform graceful close
+  server.CloseAll(false);
+  // Connections have all closed
+  EXPECT_TRUE(monitor.server_closed);
+  EXPECT_TRUE(monitor.connection_closed);
+}
+
+TEST(HttpServer, SignalsCloseAfterGracefulCloseAll) {
+  HttpServer server;
+  HttpServerMonitor monitor(&server);
+  // Add an active client connection
+  CreateClientConnection(server, monitor, true);
+  // Initiate a graceful close
+  server.CloseAll(false);
+  EXPECT_FALSE(monitor.server_closed);
+  // Simulate a response
+  ASSERT_TRUE(nullptr != monitor.transaction);
+  server.Respond(monitor.transaction);
+  EXPECT_FALSE(monitor.transaction);
+  // Connections have all closed
+  EXPECT_TRUE(monitor.server_closed);
+  EXPECT_TRUE(monitor.connection_closed);
+}
+
+TEST(HttpServer, SignalsCloseAfterForcedCloseAll) {
+  HttpServer server;
+  HttpServerMonitor monitor(&server);
+  // Add an active client connection
+  CreateClientConnection(server, monitor, true);
+  // Initiate a forceful close
+  server.CloseAll(true);
+  // Connections have all closed
+  EXPECT_TRUE(monitor.server_closed);
+  EXPECT_TRUE(monitor.connection_closed);
+}
+
+} // namespace rtc
diff --git a/base/ifaddrs-android.cc b/base/ifaddrs-android.cc
new file mode 100644
index 0000000..f3c7838
--- /dev/null
+++ b/base/ifaddrs-android.cc
@@ -0,0 +1,223 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#if defined(WEBRTC_ANDROID)
+#include "webrtc/base/ifaddrs-android.h"
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+namespace {
+
+struct netlinkrequest {
+  nlmsghdr header;
+  ifaddrmsg msg;
+};
+
+const int kMaxReadSize = 4096;
+
+}  // namespace
+
+namespace rtc {
+
+int set_ifname(struct ifaddrs* ifaddr, int interface) {
+  char buf[IFNAMSIZ] = {0};
+  char* name = if_indextoname(interface, buf);
+  if (name == nullptr) {
+    return -1;
+  }
+  ifaddr->ifa_name = new char[strlen(name) + 1];
+  strncpy(ifaddr->ifa_name, name, strlen(name) + 1);
+  return 0;
+}
+
+int set_flags(struct ifaddrs* ifaddr) {
+  int fd = socket(AF_INET, SOCK_DGRAM, 0);
+  if (fd == -1) {
+    return -1;
+  }
+  ifreq ifr;
+  memset(&ifr, 0, sizeof(ifr));
+  strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1);
+  int rc = ioctl(fd, SIOCGIFFLAGS, &ifr);
+  close(fd);
+  if (rc == -1) {
+    return -1;
+  }
+  ifaddr->ifa_flags = ifr.ifr_flags;
+  return 0;
+}
+
+int set_addresses(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* data,
+                  size_t len) {
+  if (msg->ifa_family == AF_INET) {
+    sockaddr_in* sa = new sockaddr_in;
+    sa->sin_family = AF_INET;
+    memcpy(&sa->sin_addr, data, len);
+    ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(sa);
+  } else if (msg->ifa_family == AF_INET6) {
+    sockaddr_in6* sa = new sockaddr_in6;
+    sa->sin6_family = AF_INET6;
+    sa->sin6_scope_id = msg->ifa_index;
+    memcpy(&sa->sin6_addr, data, len);
+    ifaddr->ifa_addr = reinterpret_cast<sockaddr*>(sa);
+  } else {
+    return -1;
+  }
+  return 0;
+}
+
+int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) {
+  char* prefix = nullptr;
+  if (family == AF_INET) {
+    sockaddr_in* mask = new sockaddr_in;
+    mask->sin_family = AF_INET;
+    memset(&mask->sin_addr, 0, sizeof(in_addr));
+    ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
+    if (prefixlen > 32) {
+      prefixlen = 32;
+    }
+    prefix = reinterpret_cast<char*>(&mask->sin_addr);
+  } else if (family == AF_INET6) {
+    sockaddr_in6* mask = new sockaddr_in6;
+    mask->sin6_family = AF_INET6;
+    memset(&mask->sin6_addr, 0, sizeof(in6_addr));
+    ifaddr->ifa_netmask = reinterpret_cast<sockaddr*>(mask);
+    if (prefixlen > 128) {
+      prefixlen = 128;
+    }
+    prefix = reinterpret_cast<char*>(&mask->sin6_addr);
+  } else {
+    return -1;
+  }
+  for (int i = 0; i < (prefixlen / 8); i++) {
+    *prefix++ = 0xFF;
+  }
+  char remainder = 0xff;
+  remainder <<= (8 - prefixlen % 8);
+  *prefix = remainder;
+  return 0;
+}
+
+int populate_ifaddrs(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* bytes,
+                     size_t len) {
+  if (set_ifname(ifaddr, msg->ifa_index) != 0) {
+    return -1;
+  }
+  if (set_flags(ifaddr) != 0) {
+    return -1;
+  }
+  if (set_addresses(ifaddr, msg, bytes, len) != 0) {
+    return -1;
+  }
+  if (make_prefixes(ifaddr, msg->ifa_family, msg->ifa_prefixlen) != 0) {
+    return -1;
+  }
+  return 0;
+}
+
+int getifaddrs(struct ifaddrs** result) {
+  int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+  if (fd < 0) {
+    return -1;
+  }
+
+  netlinkrequest ifaddr_request;
+  memset(&ifaddr_request, 0, sizeof(ifaddr_request));
+  ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
+  ifaddr_request.header.nlmsg_type = RTM_GETADDR;
+  ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(ifaddrmsg));
+
+  ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0);
+  if (static_cast<size_t>(count) != ifaddr_request.header.nlmsg_len) {
+    close(fd);
+    return -1;
+  }
+  struct ifaddrs* start = nullptr;
+  struct ifaddrs* current = nullptr;
+  char buf[kMaxReadSize];
+  ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0);
+  while (amount_read > 0) {
+    nlmsghdr* header = reinterpret_cast<nlmsghdr*>(&buf[0]);
+    size_t header_size = static_cast<size_t>(amount_read);
+    for ( ; NLMSG_OK(header, header_size);
+          header = NLMSG_NEXT(header, header_size)) {
+      switch (header->nlmsg_type) {
+        case NLMSG_DONE:
+          // Success. Return.
+          *result = start;
+          close(fd);
+          return 0;
+        case NLMSG_ERROR:
+          close(fd);
+          freeifaddrs(start);
+          return -1;
+        case RTM_NEWADDR: {
+          ifaddrmsg* address_msg =
+              reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(header));
+          rtattr* rta = IFA_RTA(address_msg);
+          ssize_t payload_len = IFA_PAYLOAD(header);
+          while (RTA_OK(rta, payload_len)) {
+            if (rta->rta_type == IFA_ADDRESS) {
+              int family = address_msg->ifa_family;
+              if (family == AF_INET || family == AF_INET6) {
+                ifaddrs* newest = new ifaddrs;
+                memset(newest, 0, sizeof(ifaddrs));
+                if (current) {
+                  current->ifa_next = newest;
+                } else {
+                  start = newest;
+                }
+                if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta),
+                                     RTA_PAYLOAD(rta)) != 0) {
+                  freeifaddrs(start);
+                  *result = nullptr;
+                  return -1;
+                }
+                current = newest;
+              }
+            }
+            rta = RTA_NEXT(rta, payload_len);
+          }
+          break;
+        }
+      }
+    }
+    amount_read = recv(fd, &buf, kMaxReadSize, 0);
+  }
+  close(fd);
+  freeifaddrs(start);
+  return -1;
+}
+
+void freeifaddrs(struct ifaddrs* addrs) {
+  struct ifaddrs* last = nullptr;
+  struct ifaddrs* cursor = addrs;
+  while (cursor) {
+    delete[] cursor->ifa_name;
+    delete cursor->ifa_addr;
+    delete cursor->ifa_netmask;
+    last = cursor;
+    cursor = cursor->ifa_next;
+    delete last;
+  }
+}
+
+}  // namespace rtc
+#endif  // defined(WEBRTC_ANDROID)
diff --git a/base/ifaddrs-android.h b/base/ifaddrs-android.h
index 9c49c9f..10890af 100644
--- a/base/ifaddrs-android.h
+++ b/base/ifaddrs-android.h
@@ -11,9 +11,29 @@
 #ifndef WEBRTC_BASE_IFADDRS_ANDROID_H_
 #define WEBRTC_BASE_IFADDRS_ANDROID_H_
 
+#include <stdio.h>
+#include <sys/socket.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/ifaddrs-android.h"
+
+// Implementation of getifaddrs for Android.
+// Fills out a list of ifaddr structs (see below) which contain information
+// about every network interface available on the host.
+// See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function).
+struct ifaddrs {
+  struct ifaddrs* ifa_next;
+  char* ifa_name;
+  unsigned int ifa_flags;
+  struct sockaddr* ifa_addr;
+  struct sockaddr* ifa_netmask;
+  // Real ifaddrs has broadcast, point to point and data members.
+  // We don't need them (yet?).
+};
+
+namespace rtc {
+
+int getifaddrs(struct ifaddrs** result);
+void freeifaddrs(struct ifaddrs* addrs);
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_IFADDRS_ANDROID_H_
diff --git a/base/ifaddrs_converter.cc b/base/ifaddrs_converter.cc
new file mode 100644
index 0000000..7dd3555
--- /dev/null
+++ b/base/ifaddrs_converter.cc
@@ -0,0 +1,60 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/ifaddrs_converter.h"
+
+namespace rtc {
+
+IfAddrsConverter::IfAddrsConverter() {}
+
+IfAddrsConverter::~IfAddrsConverter() {}
+
+bool IfAddrsConverter::ConvertIfAddrsToIPAddress(
+    const struct ifaddrs* interface,
+    InterfaceAddress* ip,
+    IPAddress* mask) {
+  switch (interface->ifa_addr->sa_family) {
+    case AF_INET: {
+      *ip = IPAddress(
+          reinterpret_cast<sockaddr_in*>(interface->ifa_addr)->sin_addr);
+      *mask = IPAddress(
+          reinterpret_cast<sockaddr_in*>(interface->ifa_netmask)->sin_addr);
+      return true;
+    }
+    case AF_INET6: {
+      int ip_attributes = IPV6_ADDRESS_FLAG_NONE;
+      if (!ConvertNativeAttributesToIPAttributes(interface, &ip_attributes)) {
+        return false;
+      }
+      *ip = InterfaceAddress(
+          reinterpret_cast<sockaddr_in6*>(interface->ifa_addr)->sin6_addr,
+          ip_attributes);
+      *mask = IPAddress(
+          reinterpret_cast<sockaddr_in6*>(interface->ifa_netmask)->sin6_addr);
+      return true;
+    }
+    default: { return false; }
+  }
+}
+
+bool IfAddrsConverter::ConvertNativeAttributesToIPAttributes(
+    const struct ifaddrs* interface,
+    int* ip_attributes) {
+  *ip_attributes = IPV6_ADDRESS_FLAG_NONE;
+  return true;
+}
+
+#if !defined(WEBRTC_MAC)
+// For MAC and IOS, it's defined in macifaddrs_converter.cc
+IfAddrsConverter* CreateIfAddrsConverter() {
+  return new IfAddrsConverter();
+}
+#endif
+}  // namespace rtc
diff --git a/base/ifaddrs_converter.h b/base/ifaddrs_converter.h
index de7ad87..0a1cdb9 100644
--- a/base/ifaddrs_converter.h
+++ b/base/ifaddrs_converter.h
@@ -11,9 +11,35 @@
 #ifndef WEBRTC_BASE_IFADDRS_CONVERTER_H_
 #define WEBRTC_BASE_IFADDRS_CONVERTER_H_
 
+#if defined(WEBRTC_ANDROID)
+#include "webrtc/base/ifaddrs-android.h"
+#else
+#include <ifaddrs.h>
+#endif  // WEBRTC_ANDROID
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/ifaddrs_converter.h"
+#include "webrtc/base/ipaddress.h"
+
+namespace rtc {
+
+// This class converts native interface addresses to our internal IPAddress
+// class. Subclasses should override ConvertNativeToIPAttributes to implement
+// the different ways of retrieving IPv6 attributes for various POSIX platforms.
+class IfAddrsConverter {
+ public:
+  IfAddrsConverter();
+  virtual ~IfAddrsConverter();
+  virtual bool ConvertIfAddrsToIPAddress(const struct ifaddrs* interface,
+                                         InterfaceAddress* ipaddress,
+                                         IPAddress* mask);
+
+ protected:
+  virtual bool ConvertNativeAttributesToIPAttributes(
+      const struct ifaddrs* interface,
+      int* ip_attributes);
+};
+
+IfAddrsConverter* CreateIfAddrsConverter();
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_IFADDRS_CONVERTER_H_
diff --git a/base/ignore_wundef.h b/base/ignore_wundef.h
index fdfba9b..b5bf7f7 100644
--- a/base/ignore_wundef.h
+++ b/base/ignore_wundef.h
@@ -11,9 +11,23 @@
 #ifndef WEBRTC_BASE_IGNORE_WUNDEF_H_
 #define WEBRTC_BASE_IGNORE_WUNDEF_H_
 
+// If a header file uses #if on possibly undefined macros (and it's for some
+// reason not possible to just fix the header file), include it like this:
+//
+//   RTC_PUSH_IGNORING_WUNDEF()
+//   #include "misbehaving_header.h"
+//   RTC_POP_IGNORING_WUNDEF()
+//
+// This will cause the compiler to not emit -Wundef warnings for that file.
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/ignore_wundef.h"
+#ifdef __clang__
+#define RTC_PUSH_IGNORING_WUNDEF() \
+  _Pragma("clang diagnostic push") \
+      _Pragma("clang diagnostic ignored \"-Wundef\"")
+#define RTC_POP_IGNORING_WUNDEF() _Pragma("clang diagnostic pop")
+#else
+#define RTC_PUSH_IGNORING_WUNDEF()
+#define RTC_POP_IGNORING_WUNDEF()
+#endif  // __clang__
 
 #endif  // WEBRTC_BASE_IGNORE_WUNDEF_H_
diff --git a/base/ipaddress.cc b/base/ipaddress.cc
new file mode 100644
index 0000000..c06a1a7
--- /dev/null
+++ b/base/ipaddress.cc
@@ -0,0 +1,527 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#if defined(WEBRTC_POSIX)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifdef OPENBSD
+#include <netinet/in_systm.h>
+#endif
+#ifndef __native_client__
+#include <netinet/ip.h>
+#endif
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+
+#include "webrtc/base/ipaddress.h"
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/nethelpers.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/win32.h"
+
+namespace rtc {
+
+// Prefixes used for categorizing IPv6 addresses.
+static const in6_addr kV4MappedPrefix = {{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                           0xFF, 0xFF, 0}}};
+static const in6_addr k6To4Prefix = {{{0x20, 0x02, 0}}};
+static const in6_addr kTeredoPrefix = {{{0x20, 0x01, 0x00, 0x00}}};
+static const in6_addr kV4CompatibilityPrefix = {{{0}}};
+static const in6_addr k6BonePrefix = {{{0x3f, 0xfe, 0}}};
+
+static bool IsPrivateV4(uint32_t ip);
+static in_addr ExtractMappedAddress(const in6_addr& addr);
+
+uint32_t IPAddress::v4AddressAsHostOrderInteger() const {
+  if (family_ == AF_INET) {
+    return NetworkToHost32(u_.ip4.s_addr);
+  } else {
+    return 0;
+  }
+}
+
+bool IPAddress::IsNil() const {
+  return IPIsUnspec(*this);
+}
+
+size_t IPAddress::Size() const {
+  switch (family_) {
+    case AF_INET:
+      return sizeof(in_addr);
+    case AF_INET6:
+      return sizeof(in6_addr);
+  }
+  return 0;
+}
+
+
+bool IPAddress::operator==(const IPAddress &other) const {
+  if (family_ != other.family_) {
+    return false;
+  }
+  if (family_ == AF_INET) {
+    return memcmp(&u_.ip4, &other.u_.ip4, sizeof(u_.ip4)) == 0;
+  }
+  if (family_ == AF_INET6) {
+    return memcmp(&u_.ip6, &other.u_.ip6, sizeof(u_.ip6)) == 0;
+  }
+  return family_ == AF_UNSPEC;
+}
+
+bool IPAddress::operator!=(const IPAddress &other) const {
+  return !((*this) == other);
+}
+
+bool IPAddress::operator >(const IPAddress &other) const {
+  return (*this) != other && !((*this) < other);
+}
+
+bool IPAddress::operator <(const IPAddress &other) const {
+  // IPv4 is 'less than' IPv6
+  if (family_ != other.family_) {
+    if (family_ == AF_UNSPEC) {
+      return true;
+    }
+    if (family_ == AF_INET && other.family_ == AF_INET6) {
+      return true;
+    }
+    return false;
+  }
+  // Comparing addresses of the same family.
+  switch (family_) {
+    case AF_INET: {
+      return NetworkToHost32(u_.ip4.s_addr) <
+          NetworkToHost32(other.u_.ip4.s_addr);
+    }
+    case AF_INET6: {
+      return memcmp(&u_.ip6.s6_addr, &other.u_.ip6.s6_addr, 16) < 0;
+    }
+  }
+  // Catches AF_UNSPEC and invalid addresses.
+  return false;
+}
+
+std::ostream& operator<<(std::ostream& os, const IPAddress& ip) {
+  os << ip.ToString();
+  return os;
+}
+
+in6_addr IPAddress::ipv6_address() const {
+  return u_.ip6;
+}
+
+in_addr IPAddress::ipv4_address() const {
+  return u_.ip4;
+}
+
+std::string IPAddress::ToString() const {
+  if (family_ != AF_INET && family_ != AF_INET6) {
+    return std::string();
+  }
+  char buf[INET6_ADDRSTRLEN] = {0};
+  const void* src = &u_.ip4;
+  if (family_ == AF_INET6) {
+    src = &u_.ip6;
+  }
+  if (!rtc::inet_ntop(family_, src, buf, sizeof(buf))) {
+    return std::string();
+  }
+  return std::string(buf);
+}
+
+std::string IPAddress::ToSensitiveString() const {
+#if !defined(NDEBUG)
+  // Return non-stripped in debug.
+  return ToString();
+#else
+  switch (family_) {
+    case AF_INET: {
+      std::string address = ToString();
+      size_t find_pos = address.rfind('.');
+      if (find_pos == std::string::npos)
+        return std::string();
+      address.resize(find_pos);
+      address += ".x";
+      return address;
+    }
+    case AF_INET6: {
+      std::string result;
+      result.resize(INET6_ADDRSTRLEN);
+      in6_addr addr = ipv6_address();
+      size_t len =
+          rtc::sprintfn(&(result[0]), result.size(), "%x:%x:%x:x:x:x:x:x",
+                        (addr.s6_addr[0] << 8) + addr.s6_addr[1],
+                        (addr.s6_addr[2] << 8) + addr.s6_addr[3],
+                        (addr.s6_addr[4] << 8) + addr.s6_addr[5]);
+      result.resize(len);
+      return result;
+    }
+  }
+  return std::string();
+#endif
+}
+
+IPAddress IPAddress::Normalized() const {
+  if (family_ != AF_INET6) {
+    return *this;
+  }
+  if (!IPIsV4Mapped(*this)) {
+    return *this;
+  }
+  in_addr addr = ExtractMappedAddress(u_.ip6);
+  return IPAddress(addr);
+}
+
+IPAddress IPAddress::AsIPv6Address() const {
+  if (family_ != AF_INET) {
+    return *this;
+  }
+  in6_addr v6addr = kV4MappedPrefix;
+  ::memcpy(&v6addr.s6_addr[12], &u_.ip4.s_addr, sizeof(u_.ip4.s_addr));
+  return IPAddress(v6addr);
+}
+
+bool InterfaceAddress::operator==(const InterfaceAddress &other) const {
+  return ipv6_flags_ == other.ipv6_flags() &&
+    static_cast<const IPAddress&>(*this) == other;
+}
+
+bool InterfaceAddress::operator!=(const InterfaceAddress &other) const {
+  return !((*this) == other);
+}
+
+const InterfaceAddress& InterfaceAddress::operator=(
+  const InterfaceAddress& other) {
+  ipv6_flags_ = other.ipv6_flags_;
+  static_cast<IPAddress&>(*this) = other;
+  return *this;
+}
+
+std::ostream& operator<<(std::ostream& os, const InterfaceAddress& ip) {
+  os << static_cast<const IPAddress&>(ip);
+
+  if (ip.family() == AF_INET6)
+    os << "|flags:0x" << std::hex << ip.ipv6_flags();
+
+  return os;
+}
+
+bool IsPrivateV4(uint32_t ip_in_host_order) {
+  return ((ip_in_host_order >> 24) == 127) ||
+      ((ip_in_host_order >> 24) == 10) ||
+      ((ip_in_host_order >> 20) == ((172 << 4) | 1)) ||
+      ((ip_in_host_order >> 16) == ((192 << 8) | 168)) ||
+      ((ip_in_host_order >> 16) == ((169 << 8) | 254));
+}
+
+in_addr ExtractMappedAddress(const in6_addr& in6) {
+  in_addr ipv4;
+  ::memcpy(&ipv4.s_addr, &in6.s6_addr[12], sizeof(ipv4.s_addr));
+  return ipv4;
+}
+
+bool IPFromAddrInfo(struct addrinfo* info, IPAddress* out) {
+  if (!info || !info->ai_addr) {
+    return false;
+  }
+  if (info->ai_addr->sa_family == AF_INET) {
+    sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(info->ai_addr);
+    *out = IPAddress(addr->sin_addr);
+    return true;
+  } else if (info->ai_addr->sa_family == AF_INET6) {
+    sockaddr_in6* addr = reinterpret_cast<sockaddr_in6*>(info->ai_addr);
+    *out = IPAddress(addr->sin6_addr);
+    return true;
+  }
+  return false;
+}
+
+bool IPFromString(const std::string& str, IPAddress* out) {
+  if (!out) {
+    return false;
+  }
+  in_addr addr;
+  if (rtc::inet_pton(AF_INET, str.c_str(), &addr) == 0) {
+    in6_addr addr6;
+    if (rtc::inet_pton(AF_INET6, str.c_str(), &addr6) == 0) {
+      *out = IPAddress();
+      return false;
+    }
+    *out = IPAddress(addr6);
+  } else {
+    *out = IPAddress(addr);
+  }
+  return true;
+}
+
+bool IPFromString(const std::string& str, int flags,
+                  InterfaceAddress* out) {
+  IPAddress ip;
+  if (!IPFromString(str, &ip)) {
+    return false;
+  }
+
+  *out = InterfaceAddress(ip, flags);
+  return true;
+}
+
+bool IPIsAny(const IPAddress& ip) {
+  switch (ip.family()) {
+    case AF_INET:
+      return ip == IPAddress(INADDR_ANY);
+    case AF_INET6:
+      return ip == IPAddress(in6addr_any) || ip == IPAddress(kV4MappedPrefix);
+    case AF_UNSPEC:
+      return false;
+  }
+  return false;
+}
+
+bool IPIsLoopback(const IPAddress& ip) {
+  switch (ip.family()) {
+    case AF_INET: {
+      return (ip.v4AddressAsHostOrderInteger() >> 24) == 127;
+    }
+    case AF_INET6: {
+      return ip == IPAddress(in6addr_loopback);
+    }
+  }
+  return false;
+}
+
+bool IPIsPrivate(const IPAddress& ip) {
+  switch (ip.family()) {
+    case AF_INET: {
+      return IsPrivateV4(ip.v4AddressAsHostOrderInteger());
+    }
+    case AF_INET6: {
+      return IPIsLinkLocal(ip) || IPIsLoopback(ip);
+    }
+  }
+  return false;
+}
+
+bool IPIsUnspec(const IPAddress& ip) {
+  return ip.family() == AF_UNSPEC;
+}
+
+size_t HashIP(const IPAddress& ip) {
+  switch (ip.family()) {
+    case AF_INET: {
+      return ip.ipv4_address().s_addr;
+    }
+    case AF_INET6: {
+      in6_addr v6addr = ip.ipv6_address();
+      const uint32_t* v6_as_ints =
+          reinterpret_cast<const uint32_t*>(&v6addr.s6_addr);
+      return v6_as_ints[0] ^ v6_as_ints[1] ^ v6_as_ints[2] ^ v6_as_ints[3];
+    }
+  }
+  return 0;
+}
+
+IPAddress TruncateIP(const IPAddress& ip, int length) {
+  if (length < 0) {
+    return IPAddress();
+  }
+  if (ip.family() == AF_INET) {
+    if (length > 31) {
+      return ip;
+    }
+    if (length == 0) {
+      return IPAddress(INADDR_ANY);
+    }
+    int mask = (0xFFFFFFFF << (32 - length));
+    uint32_t host_order_ip = NetworkToHost32(ip.ipv4_address().s_addr);
+    in_addr masked;
+    masked.s_addr = HostToNetwork32(host_order_ip & mask);
+    return IPAddress(masked);
+  } else if (ip.family() == AF_INET6) {
+    if (length > 127) {
+      return ip;
+    }
+    if (length == 0) {
+      return IPAddress(in6addr_any);
+    }
+    in6_addr v6addr = ip.ipv6_address();
+    int position = length / 32;
+    int inner_length = 32 - (length - (position * 32));
+    // Note: 64bit mask constant needed to allow possible 32-bit left shift.
+    uint32_t inner_mask = 0xFFFFFFFFLL << inner_length;
+    uint32_t* v6_as_ints = reinterpret_cast<uint32_t*>(&v6addr.s6_addr);
+    for (int i = 0; i < 4; ++i) {
+      if (i == position) {
+        uint32_t host_order_inner = NetworkToHost32(v6_as_ints[i]);
+        v6_as_ints[i] = HostToNetwork32(host_order_inner & inner_mask);
+      } else if (i > position) {
+        v6_as_ints[i] = 0;
+      }
+    }
+    return IPAddress(v6addr);
+  }
+  return IPAddress();
+}
+
+int CountIPMaskBits(IPAddress mask) {
+  uint32_t word_to_count = 0;
+  int bits = 0;
+  switch (mask.family()) {
+    case AF_INET: {
+      word_to_count = NetworkToHost32(mask.ipv4_address().s_addr);
+      break;
+    }
+    case AF_INET6: {
+      in6_addr v6addr = mask.ipv6_address();
+      const uint32_t* v6_as_ints =
+          reinterpret_cast<const uint32_t*>(&v6addr.s6_addr);
+      int i = 0;
+      for (; i < 4; ++i) {
+        if (v6_as_ints[i] != 0xFFFFFFFF) {
+          break;
+        }
+      }
+      if (i < 4) {
+        word_to_count = NetworkToHost32(v6_as_ints[i]);
+      }
+      bits = (i * 32);
+      break;
+    }
+    default: {
+      return 0;
+    }
+  }
+  if (word_to_count == 0) {
+    return bits;
+  }
+
+  // Public domain bit-twiddling hack from:
+  // http://graphics.stanford.edu/~seander/bithacks.html
+  // Counts the trailing 0s in the word.
+  unsigned int zeroes = 32;
+  // This could also be written word_to_count &= -word_to_count, but
+  // MSVC emits warning C4146 when negating an unsigned number.
+  word_to_count &= ~word_to_count + 1;  // Isolate lowest set bit.
+  if (word_to_count) zeroes--;
+  if (word_to_count & 0x0000FFFF) zeroes -= 16;
+  if (word_to_count & 0x00FF00FF) zeroes -= 8;
+  if (word_to_count & 0x0F0F0F0F) zeroes -= 4;
+  if (word_to_count & 0x33333333) zeroes -= 2;
+  if (word_to_count & 0x55555555) zeroes -= 1;
+
+  return bits + (32 - zeroes);
+}
+
+bool IPIsHelper(const IPAddress& ip, const in6_addr& tomatch, int length) {
+  // Helper method for checking IP prefix matches (but only on whole byte
+  // lengths). Length is in bits.
+  in6_addr addr = ip.ipv6_address();
+  return ::memcmp(&addr, &tomatch, (length >> 3)) == 0;
+}
+
+bool IPIs6Bone(const IPAddress& ip) {
+  return IPIsHelper(ip, k6BonePrefix, 16);
+}
+
+bool IPIs6To4(const IPAddress& ip) {
+  return IPIsHelper(ip, k6To4Prefix, 16);
+}
+
+bool IPIsLinkLocal(const IPAddress& ip) {
+  // Can't use the helper because the prefix is 10 bits.
+  in6_addr addr = ip.ipv6_address();
+  return addr.s6_addr[0] == 0xFE && addr.s6_addr[1] == 0x80;
+}
+
+// According to http://www.ietf.org/rfc/rfc2373.txt, Appendix A, page 19.  An
+// address which contains MAC will have its 11th and 12th bytes as FF:FE as well
+// as the U/L bit as 1.
+bool IPIsMacBased(const IPAddress& ip) {
+  in6_addr addr = ip.ipv6_address();
+  return ((addr.s6_addr[8] & 0x02) && addr.s6_addr[11] == 0xFF &&
+          addr.s6_addr[12] == 0xFE);
+}
+
+bool IPIsSiteLocal(const IPAddress& ip) {
+  // Can't use the helper because the prefix is 10 bits.
+  in6_addr addr = ip.ipv6_address();
+  return addr.s6_addr[0] == 0xFE && (addr.s6_addr[1] & 0xC0) == 0xC0;
+}
+
+bool IPIsULA(const IPAddress& ip) {
+  // Can't use the helper because the prefix is 7 bits.
+  in6_addr addr = ip.ipv6_address();
+  return (addr.s6_addr[0] & 0xFE) == 0xFC;
+}
+
+bool IPIsTeredo(const IPAddress& ip) {
+  return IPIsHelper(ip, kTeredoPrefix, 32);
+}
+
+bool IPIsV4Compatibility(const IPAddress& ip) {
+  return IPIsHelper(ip, kV4CompatibilityPrefix, 96);
+}
+
+bool IPIsV4Mapped(const IPAddress& ip) {
+  return IPIsHelper(ip, kV4MappedPrefix, 96);
+}
+
+int IPAddressPrecedence(const IPAddress& ip) {
+  // Precedence values from RFC 3484-bis. Prefers native v4 over 6to4/Teredo.
+  if (ip.family() == AF_INET) {
+    return 30;
+  } else if (ip.family() == AF_INET6) {
+    if (IPIsLoopback(ip)) {
+      return 60;
+    } else if (IPIsULA(ip)) {
+      return 50;
+    } else if (IPIsV4Mapped(ip)) {
+      return 30;
+    } else if (IPIs6To4(ip)) {
+      return 20;
+    } else if (IPIsTeredo(ip)) {
+      return 10;
+    } else if (IPIsV4Compatibility(ip) || IPIsSiteLocal(ip) || IPIs6Bone(ip)) {
+      return 1;
+    } else {
+      // A 'normal' IPv6 address.
+      return 40;
+    }
+  }
+  return 0;
+}
+
+IPAddress GetLoopbackIP(int family) {
+  if (family == AF_INET) {
+    return rtc::IPAddress(INADDR_LOOPBACK);
+  }
+  if (family == AF_INET6) {
+    return rtc::IPAddress(in6addr_loopback);
+  }
+  return rtc::IPAddress();
+}
+
+IPAddress GetAnyIP(int family) {
+  if (family == AF_INET) {
+    return rtc::IPAddress(INADDR_ANY);
+  }
+  if (family == AF_INET6) {
+    return rtc::IPAddress(in6addr_any);
+  }
+  return rtc::IPAddress();
+}
+
+}  // namespace rtc
diff --git a/base/ipaddress.h b/base/ipaddress.h
index 44e432d..ef1e3d8 100644
--- a/base/ipaddress.h
+++ b/base/ipaddress.h
@@ -11,9 +11,178 @@
 #ifndef WEBRTC_BASE_IPADDRESS_H_
 #define WEBRTC_BASE_IPADDRESS_H_
 
+#if defined(WEBRTC_POSIX)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+#if defined(WEBRTC_WIN)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+#include <string.h>
+#include <string>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/ipaddress.h"
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/byteorder.h"
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#endif
+
+namespace rtc {
+
+enum IPv6AddressFlag {
+  IPV6_ADDRESS_FLAG_NONE =           0x00,
+
+  // Temporary address is dynamic by nature and will not carry MAC
+  // address.
+  IPV6_ADDRESS_FLAG_TEMPORARY =      1 << 0,
+
+  // Temporary address could become deprecated once the preferred
+  // lifetime is reached. It is still valid but just shouldn't be used
+  // to create new connection.
+  IPV6_ADDRESS_FLAG_DEPRECATED =     1 << 1,
+};
+
+// Version-agnostic IP address class, wraps a union of in_addr and in6_addr.
+class IPAddress {
+ public:
+  IPAddress() : family_(AF_UNSPEC) {
+    ::memset(&u_, 0, sizeof(u_));
+  }
+
+  explicit IPAddress(const in_addr& ip4) : family_(AF_INET) {
+    memset(&u_, 0, sizeof(u_));
+    u_.ip4 = ip4;
+  }
+
+  explicit IPAddress(const in6_addr& ip6) : family_(AF_INET6) {
+    u_.ip6 = ip6;
+  }
+
+  explicit IPAddress(uint32_t ip_in_host_byte_order) : family_(AF_INET) {
+    memset(&u_, 0, sizeof(u_));
+    u_.ip4.s_addr = HostToNetwork32(ip_in_host_byte_order);
+  }
+
+  IPAddress(const IPAddress& other) : family_(other.family_) {
+    ::memcpy(&u_, &other.u_, sizeof(u_));
+  }
+
+  virtual ~IPAddress() {}
+
+  const IPAddress & operator=(const IPAddress& other) {
+    family_ = other.family_;
+    ::memcpy(&u_, &other.u_, sizeof(u_));
+    return *this;
+  }
+
+  bool operator==(const IPAddress& other) const;
+  bool operator!=(const IPAddress& other) const;
+  bool operator <(const IPAddress& other) const;
+  bool operator >(const IPAddress& other) const;
+  friend std::ostream& operator<<(std::ostream& os, const IPAddress& addr);
+
+  int family() const { return family_; }
+  in_addr ipv4_address() const;
+  in6_addr ipv6_address() const;
+
+  // Returns the number of bytes needed to store the raw address.
+  size_t Size() const;
+
+  // Wraps inet_ntop.
+  std::string ToString() const;
+
+  // Same as ToString but anonymizes it by hiding the last part.
+  std::string ToSensitiveString() const;
+
+  // Returns an unmapped address from a possibly-mapped address.
+  // Returns the same address if this isn't a mapped address.
+  IPAddress Normalized() const;
+
+  // Returns this address as an IPv6 address.
+  // Maps v4 addresses (as ::ffff:a.b.c.d), returns v6 addresses unchanged.
+  IPAddress AsIPv6Address() const;
+
+  // For socketaddress' benefit. Returns the IP in host byte order.
+  uint32_t v4AddressAsHostOrderInteger() const;
+
+  // Whether this is an unspecified IP address.
+  bool IsNil() const;
+
+ private:
+  int family_;
+  union {
+    in_addr ip4;
+    in6_addr ip6;
+  } u_;
+};
+
+// IP class which could represent IPv6 address flags which is only
+// meaningful in IPv6 case.
+class InterfaceAddress : public IPAddress {
+ public:
+  InterfaceAddress() : ipv6_flags_(IPV6_ADDRESS_FLAG_NONE) {}
+
+  InterfaceAddress(IPAddress ip)
+    : IPAddress(ip), ipv6_flags_(IPV6_ADDRESS_FLAG_NONE) {}
+
+  InterfaceAddress(IPAddress addr, int ipv6_flags)
+    : IPAddress(addr), ipv6_flags_(ipv6_flags) {}
+
+  InterfaceAddress(const in6_addr& ip6, int ipv6_flags)
+    : IPAddress(ip6), ipv6_flags_(ipv6_flags) {}
+
+  const InterfaceAddress & operator=(const InterfaceAddress& other);
+
+  bool operator==(const InterfaceAddress& other) const;
+  bool operator!=(const InterfaceAddress& other) const;
+
+  int ipv6_flags() const { return ipv6_flags_; }
+  friend std::ostream& operator<<(std::ostream& os,
+                                  const InterfaceAddress& addr);
+
+ private:
+  int ipv6_flags_;
+};
+
+bool IPFromAddrInfo(struct addrinfo* info, IPAddress* out);
+bool IPFromString(const std::string& str, IPAddress* out);
+bool IPFromString(const std::string& str, int flags,
+                  InterfaceAddress* out);
+bool IPIsAny(const IPAddress& ip);
+bool IPIsLoopback(const IPAddress& ip);
+bool IPIsPrivate(const IPAddress& ip);
+bool IPIsUnspec(const IPAddress& ip);
+size_t HashIP(const IPAddress& ip);
+
+// These are only really applicable for IPv6 addresses.
+bool IPIs6Bone(const IPAddress& ip);
+bool IPIs6To4(const IPAddress& ip);
+bool IPIsLinkLocal(const IPAddress& ip);
+bool IPIsMacBased(const IPAddress& ip);
+bool IPIsSiteLocal(const IPAddress& ip);
+bool IPIsTeredo(const IPAddress& ip);
+bool IPIsULA(const IPAddress& ip);
+bool IPIsV4Compatibility(const IPAddress& ip);
+bool IPIsV4Mapped(const IPAddress& ip);
+
+// Returns the precedence value for this IP as given in RFC3484.
+int IPAddressPrecedence(const IPAddress& ip);
+
+// Returns 'ip' truncated to be 'length' bits long.
+IPAddress TruncateIP(const IPAddress& ip, int length);
+
+IPAddress GetLoopbackIP(int family);
+IPAddress GetAnyIP(int family);
+
+// Returns the number of contiguously set bits, counting from the MSB in network
+// byte order, in this IPAddress. Bits after the first 0 encountered are not
+// counted.
+int CountIPMaskBits(IPAddress mask);
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_IPADDRESS_H_
diff --git a/base/ipaddress_unittest.cc b/base/ipaddress_unittest.cc
new file mode 100644
index 0000000..802b47b
--- /dev/null
+++ b/base/ipaddress_unittest.cc
@@ -0,0 +1,962 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/ipaddress.h"
+
+namespace rtc {
+
+static const unsigned int kIPv4AddrSize = 4;
+static const unsigned int kIPv6AddrSize = 16;
+static const unsigned int kIPv4RFC1918Addr = 0xC0A80701;
+static const unsigned int kIPv4PublicAddr = 0x01020304;
+static const in6_addr kIPv6LinkLocalAddr = {{{0xfe, 0x80, 0x00, 0x00,
+                                              0x00, 0x00, 0x00, 0x00,
+                                              0xbe, 0x30, 0x5b, 0xff,
+                                              0xfe, 0xe5, 0x00, 0xc3}}};
+static const in6_addr kIPv6PublicAddr = {{{0x24, 0x01, 0xfa, 0x00,
+                                           0x00, 0x04, 0x10, 0x00,
+                                           0xbe, 0x30, 0x5b, 0xff,
+                                           0xfe, 0xe5, 0x00, 0xc3}}};
+static const in6_addr kIPv6PublicAddr2 = {{{0x24, 0x01, 0x00, 0x00,
+                                            0x00, 0x00, 0x10, 0x00,
+                                            0xbe, 0x30, 0x5b, 0xff,
+                                            0xfe, 0xe5, 0x00, 0xc3}}};
+static const in6_addr kIPv4MappedAnyAddr = {{{0x00, 0x00, 0x00, 0x00,
+                                              0x00, 0x00, 0x00, 0x00,
+                                              0x00, 0x00, 0xff, 0xff,
+                                              0x00, 0x00, 0x00, 0x00}}};
+static const in6_addr kIPv4MappedRFC1918Addr = {{{0x00, 0x00, 0x00, 0x00,
+                                                  0x00, 0x00, 0x00, 0x00,
+                                                  0x00, 0x00, 0xff, 0xff,
+                                                  0xc0, 0xa8, 0x07, 0x01}}};
+static const in6_addr kIPv4MappedPublicAddr = {{{0x00, 0x00, 0x00, 0x00,
+                                                 0x00, 0x00, 0x00, 0x00,
+                                                 0x00, 0x00, 0xff, 0xff,
+                                                 0x01, 0x02, 0x03, 0x04}}};
+
+static const std::string kIPv4AnyAddrString = "0.0.0.0";
+static const std::string kIPv4LoopbackAddrString = "127.0.0.1";
+static const std::string kIPv4RFC1918AddrString = "192.168.7.1";
+static const std::string kIPv4PublicAddrString = "1.2.3.4";
+static const std::string kIPv4PublicAddrAnonymizedString = "1.2.3.x";
+static const std::string kIPv6AnyAddrString = "::";
+static const std::string kIPv6LoopbackAddrString = "::1";
+static const std::string kIPv6LinkLocalAddrString = "fe80::be30:5bff:fee5:c3";
+static const std::string kIPv6EuiAddrString =
+    "2620:0:1008:1201:a248:1cff:fe98:360";
+static const std::string kIPv6TemporaryAddrString =
+    "2620:0:1008:1201:2089:6dda:385e:80c0";
+static const std::string kIPv6PublicAddrString =
+    "2401:fa00:4:1000:be30:5bff:fee5:c3";
+static const std::string kIPv6PublicAddr2String =
+    "2401::1000:be30:5bff:fee5:c3";
+static const std::string kIPv6PublicAddrAnonymizedString =
+    "2401:fa00:4:x:x:x:x:x";
+static const std::string kIPv6PublicAddr2AnonymizedString =
+    "2401:0:0:x:x:x:x:x";
+static const std::string kIPv4MappedAnyAddrString = "::ffff:0:0";
+static const std::string kIPv4MappedRFC1918AddrString = "::ffff:c0a8:701";
+static const std::string kIPv4MappedLoopbackAddrString = "::ffff:7f00:1";
+static const std::string kIPv4MappedPublicAddrString = "::ffff:102:0304";
+static const std::string kIPv4MappedV4StyleAddrString = "::ffff:192.168.7.1";
+
+static const std::string kIPv4BrokenString1 = "192.168.7.";
+static const std::string kIPv4BrokenString2 = "192.168.7.1.1";
+static const std::string kIPv4BrokenString3 = "192.168.7.1:80";
+static const std::string kIPv4BrokenString4 = "192.168.7.ONE";
+static const std::string kIPv4BrokenString5 = "-192.168.7.1";
+static const std::string kIPv4BrokenString6 = "256.168.7.1";
+static const std::string kIPv6BrokenString1 = "2401:fa00:4:1000:be30";
+static const std::string kIPv6BrokenString2 =
+    "2401:fa00:4:1000:be30:5bff:fee5:c3:1";
+static const std::string kIPv6BrokenString3 =
+    "[2401:fa00:4:1000:be30:5bff:fee5:c3]:1";
+static const std::string kIPv6BrokenString4 =
+    "2401::4::be30";
+static const std::string kIPv6BrokenString5 =
+    "2401:::4:fee5:be30";
+static const std::string kIPv6BrokenString6 =
+    "2401f:fa00:4:1000:be30:5bff:fee5:c3";
+static const std::string kIPv6BrokenString7 =
+    "2401:ga00:4:1000:be30:5bff:fee5:c3";
+static const std::string kIPv6BrokenString8 =
+    "2401:fa000:4:1000:be30:5bff:fee5:c3";
+static const std::string kIPv6BrokenString9 =
+    "2401:fal0:4:1000:be30:5bff:fee5:c3";
+static const std::string kIPv6BrokenString10 =
+    "::ffff:192.168.7.";
+static const std::string kIPv6BrokenString11 =
+    "::ffff:192.168.7.1.1.1";
+static const std::string kIPv6BrokenString12 =
+    "::fffe:192.168.7.1";
+static const std::string kIPv6BrokenString13 =
+    "::ffff:192.168.7.ff";
+static const std::string kIPv6BrokenString14 =
+    "0x2401:fa00:4:1000:be30:5bff:fee5:c3";
+
+bool AreEqual(const IPAddress& addr,
+              const IPAddress& addr2) {
+  if ((IPIsAny(addr) != IPIsAny(addr2)) ||
+      (IPIsLoopback(addr) != IPIsLoopback(addr2)) ||
+      (IPIsPrivate(addr) != IPIsPrivate(addr2)) ||
+      (HashIP(addr) != HashIP(addr2)) ||
+      (addr.Size() != addr2.Size()) ||
+      (addr.family() != addr2.family()) ||
+      (addr.ToString() != addr2.ToString())) {
+    return false;
+  }
+  in_addr v4addr, v4addr2;
+  v4addr = addr.ipv4_address();
+  v4addr2 = addr2.ipv4_address();
+  if (0 != memcmp(&v4addr, &v4addr2, sizeof(v4addr))) {
+    return false;
+  }
+  in6_addr v6addr, v6addr2;
+  v6addr = addr.ipv6_address();
+  v6addr2 = addr2.ipv6_address();
+  if (0 != memcmp(&v6addr, &v6addr2, sizeof(v6addr))) {
+    return false;
+  }
+  return true;
+}
+
+bool BrokenIPStringFails(const std::string& broken) {
+  IPAddress addr(0);   // Intentionally make it v4.
+  if (IPFromString(kIPv4BrokenString1, &addr)) {
+    return false;
+  }
+  return addr.family() == AF_UNSPEC;
+}
+
+bool CheckMaskCount(const std::string& mask, int expected_length) {
+  IPAddress addr;
+  return IPFromString(mask, &addr) &&
+      (expected_length == CountIPMaskBits(addr));
+}
+
+bool TryInvalidMaskCount(const std::string& mask) {
+  // We don't care about the result at all, but we do want to know if
+  // CountIPMaskBits is going to crash or infinite loop or something.
+  IPAddress addr;
+  if (!IPFromString(mask, &addr)) {
+    return false;
+  }
+  CountIPMaskBits(addr);
+  return true;
+}
+
+bool CheckTruncateIP(const std::string& initial, int truncate_length,
+                     const std::string& expected_result) {
+  IPAddress addr, expected;
+  IPFromString(initial, &addr);
+  IPFromString(expected_result, &expected);
+  IPAddress truncated = TruncateIP(addr, truncate_length);
+  return truncated == expected;
+}
+
+TEST(IPAddressTest, TestDefaultCtor) {
+  IPAddress addr;
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_FALSE(IPIsPrivate(addr));
+
+  EXPECT_EQ(0U, addr.Size());
+  EXPECT_EQ(AF_UNSPEC, addr.family());
+  EXPECT_EQ("", addr.ToString());
+}
+
+TEST(IPAddressTest, TestInAddrCtor) {
+  in_addr v4addr;
+
+  // Test V4 Any address.
+  v4addr.s_addr = INADDR_ANY;
+  IPAddress addr(v4addr);
+  EXPECT_TRUE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_FALSE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv4AddrSize, addr.Size());
+  EXPECT_EQ(kIPv4AnyAddrString, addr.ToString());
+
+  // Test a V4 loopback address.
+  v4addr.s_addr = htonl(INADDR_LOOPBACK);
+  addr = IPAddress(v4addr);
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_TRUE(IPIsLoopback(addr));
+  EXPECT_TRUE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv4AddrSize, addr.Size());
+  EXPECT_EQ(kIPv4LoopbackAddrString, addr.ToString());
+
+  // Test an RFC1918 address.
+  v4addr.s_addr = htonl(kIPv4RFC1918Addr);
+  addr = IPAddress(v4addr);
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_TRUE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv4AddrSize, addr.Size());
+  EXPECT_EQ(kIPv4RFC1918AddrString, addr.ToString());
+
+  // Test a 'normal' v4 address.
+  v4addr.s_addr = htonl(kIPv4PublicAddr);
+  addr = IPAddress(v4addr);
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_FALSE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv4AddrSize, addr.Size());
+  EXPECT_EQ(kIPv4PublicAddrString, addr.ToString());
+}
+
+TEST(IPAddressTest, TestInAddr6Ctor) {
+  // Test v6 empty.
+  IPAddress addr(in6addr_any);
+  EXPECT_TRUE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_FALSE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv6AddrSize, addr.Size());
+  EXPECT_EQ(kIPv6AnyAddrString, addr.ToString());
+
+  // Test v6 loopback.
+  addr = IPAddress(in6addr_loopback);
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_TRUE(IPIsLoopback(addr));
+  EXPECT_TRUE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv6AddrSize, addr.Size());
+  EXPECT_EQ(kIPv6LoopbackAddrString, addr.ToString());
+
+  // Test v6 link-local.
+  addr = IPAddress(kIPv6LinkLocalAddr);
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_TRUE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv6AddrSize, addr.Size());
+  EXPECT_EQ(kIPv6LinkLocalAddrString, addr.ToString());
+
+  // Test v6 global address.
+  addr = IPAddress(kIPv6PublicAddr);
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_FALSE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv6AddrSize, addr.Size());
+  EXPECT_EQ(kIPv6PublicAddrString, addr.ToString());
+}
+
+TEST(IPAddressTest, TestUint32Ctor) {
+  // Test V4 Any address.
+  IPAddress addr(0);
+  EXPECT_TRUE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_FALSE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv4AddrSize, addr.Size());
+  EXPECT_EQ(kIPv4AnyAddrString, addr.ToString());
+
+  // Test a V4 loopback address.
+  addr = IPAddress(INADDR_LOOPBACK);
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_TRUE(IPIsLoopback(addr));
+  EXPECT_TRUE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv4AddrSize, addr.Size());
+  EXPECT_EQ(kIPv4LoopbackAddrString, addr.ToString());
+
+  // Test an RFC1918 address.
+  addr = IPAddress(kIPv4RFC1918Addr);
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_TRUE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv4AddrSize, addr.Size());
+  EXPECT_EQ(kIPv4RFC1918AddrString, addr.ToString());
+
+  // Test a 'normal' v4 address.
+  addr = IPAddress(kIPv4PublicAddr);
+  EXPECT_FALSE(IPIsAny(addr));
+  EXPECT_FALSE(IPIsLoopback(addr));
+  EXPECT_FALSE(IPIsPrivate(addr));
+  EXPECT_EQ(kIPv4AddrSize, addr.Size());
+  EXPECT_EQ(kIPv4PublicAddrString, addr.ToString());
+}
+
+TEST(IPAddressTest, TestCopyCtor) {
+  in_addr v4addr;
+  v4addr.s_addr = htonl(kIPv4PublicAddr);
+  IPAddress addr(v4addr);
+  IPAddress addr2(addr);
+
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr = IPAddress(INADDR_ANY);
+  addr2 = IPAddress(addr);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr = IPAddress(INADDR_LOOPBACK);
+  addr2 = IPAddress(addr);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr = IPAddress(kIPv4PublicAddr);
+  addr2 = IPAddress(addr);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr = IPAddress(kIPv4RFC1918Addr);
+  addr2 = IPAddress(addr);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr = IPAddress(in6addr_any);
+  addr2 = IPAddress(addr);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr = IPAddress(in6addr_loopback);
+  addr2 = IPAddress(addr);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr = IPAddress(kIPv6LinkLocalAddr);
+  addr2 = IPAddress(addr);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr = IPAddress(kIPv6PublicAddr);
+  addr2 = IPAddress(addr);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+}
+
+TEST(IPAddressTest, TestEquality) {
+  // Check v4 equality
+  in_addr v4addr, v4addr2;
+  v4addr.s_addr = htonl(kIPv4PublicAddr);
+  v4addr2.s_addr = htonl(kIPv4PublicAddr + 1);
+  IPAddress addr(v4addr);
+  IPAddress addr2(v4addr2);
+  IPAddress addr3(v4addr);
+
+  EXPECT_TRUE(addr == addr);
+  EXPECT_TRUE(addr2 == addr2);
+  EXPECT_TRUE(addr3 == addr3);
+  EXPECT_TRUE(addr == addr3);
+  EXPECT_TRUE(addr3 == addr);
+  EXPECT_FALSE(addr2 == addr);
+  EXPECT_FALSE(addr2 == addr3);
+  EXPECT_FALSE(addr == addr2);
+  EXPECT_FALSE(addr3 == addr2);
+
+  // Check v6 equality
+  IPAddress addr4(kIPv6PublicAddr);
+  IPAddress addr5(kIPv6LinkLocalAddr);
+  IPAddress addr6(kIPv6PublicAddr);
+
+  EXPECT_TRUE(addr4 == addr4);
+  EXPECT_TRUE(addr5 == addr5);
+  EXPECT_TRUE(addr4 == addr6);
+  EXPECT_TRUE(addr6 == addr4);
+  EXPECT_FALSE(addr4 == addr5);
+  EXPECT_FALSE(addr5 == addr4);
+  EXPECT_FALSE(addr6 == addr5);
+  EXPECT_FALSE(addr5 == addr6);
+
+  // Check v4/v6 cross-equality
+  EXPECT_FALSE(addr == addr4);
+  EXPECT_FALSE(addr == addr5);
+  EXPECT_FALSE(addr == addr6);
+  EXPECT_FALSE(addr4 == addr);
+  EXPECT_FALSE(addr5 == addr);
+  EXPECT_FALSE(addr6 == addr);
+  EXPECT_FALSE(addr2 == addr4);
+  EXPECT_FALSE(addr2 == addr5);
+  EXPECT_FALSE(addr2 == addr6);
+  EXPECT_FALSE(addr4 == addr2);
+  EXPECT_FALSE(addr5 == addr2);
+  EXPECT_FALSE(addr6 == addr2);
+  EXPECT_FALSE(addr3 == addr4);
+  EXPECT_FALSE(addr3 == addr5);
+  EXPECT_FALSE(addr3 == addr6);
+  EXPECT_FALSE(addr4 == addr3);
+  EXPECT_FALSE(addr5 == addr3);
+  EXPECT_FALSE(addr6 == addr3);
+
+  // Special cases: loopback and any.
+  // They're special but they're still not equal.
+  IPAddress v4loopback(htonl(INADDR_LOOPBACK));
+  IPAddress v6loopback(in6addr_loopback);
+  EXPECT_FALSE(v4loopback == v6loopback);
+
+  IPAddress v4any(0);
+  IPAddress v6any(in6addr_any);
+  EXPECT_FALSE(v4any == v6any);
+}
+
+TEST(IPAddressTest, TestComparison) {
+  // Defined in 'ascending' order.
+  // v6 > v4, and intra-family sorting is purely numerical
+  IPAddress addr0;  // AF_UNSPEC
+  IPAddress addr1(INADDR_ANY);  // 0.0.0.0
+  IPAddress addr2(kIPv4PublicAddr);  // 1.2.3.4
+  IPAddress addr3(INADDR_LOOPBACK);  // 127.0.0.1
+  IPAddress addr4(kIPv4RFC1918Addr);  // 192.168.7.1.
+  IPAddress addr5(in6addr_any);  // ::
+  IPAddress addr6(in6addr_loopback);  // ::1
+  IPAddress addr7(kIPv6PublicAddr);  // 2401....
+  IPAddress addr8(kIPv6LinkLocalAddr);  // fe80....
+
+  EXPECT_TRUE(addr0 < addr1);
+  EXPECT_TRUE(addr1 < addr2);
+  EXPECT_TRUE(addr2 < addr3);
+  EXPECT_TRUE(addr3 < addr4);
+  EXPECT_TRUE(addr4 < addr5);
+  EXPECT_TRUE(addr5 < addr6);
+  EXPECT_TRUE(addr6 < addr7);
+  EXPECT_TRUE(addr7 < addr8);
+
+  EXPECT_FALSE(addr0 > addr1);
+  EXPECT_FALSE(addr1 > addr2);
+  EXPECT_FALSE(addr2 > addr3);
+  EXPECT_FALSE(addr3 > addr4);
+  EXPECT_FALSE(addr4 > addr5);
+  EXPECT_FALSE(addr5 > addr6);
+  EXPECT_FALSE(addr6 > addr7);
+  EXPECT_FALSE(addr7 > addr8);
+
+  EXPECT_FALSE(addr0 > addr0);
+  EXPECT_FALSE(addr1 > addr1);
+  EXPECT_FALSE(addr2 > addr2);
+  EXPECT_FALSE(addr3 > addr3);
+  EXPECT_FALSE(addr4 > addr4);
+  EXPECT_FALSE(addr5 > addr5);
+  EXPECT_FALSE(addr6 > addr6);
+  EXPECT_FALSE(addr7 > addr7);
+  EXPECT_FALSE(addr8 > addr8);
+
+  EXPECT_FALSE(addr0 < addr0);
+  EXPECT_FALSE(addr1 < addr1);
+  EXPECT_FALSE(addr2 < addr2);
+  EXPECT_FALSE(addr3 < addr3);
+  EXPECT_FALSE(addr4 < addr4);
+  EXPECT_FALSE(addr5 < addr5);
+  EXPECT_FALSE(addr6 < addr6);
+  EXPECT_FALSE(addr7 < addr7);
+  EXPECT_FALSE(addr8 < addr8);
+}
+
+TEST(IPAddressTest, TestFromString) {
+  IPAddress addr;
+  IPAddress addr2;
+  addr2 = IPAddress(INADDR_ANY);
+
+  EXPECT_TRUE(IPFromString(kIPv4AnyAddrString, &addr));
+  EXPECT_EQ(addr.ToString(), kIPv4AnyAddrString);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr2 = IPAddress(INADDR_LOOPBACK);
+  EXPECT_TRUE(IPFromString(kIPv4LoopbackAddrString, &addr));
+  EXPECT_EQ(addr.ToString(), kIPv4LoopbackAddrString);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr2 = IPAddress(kIPv4RFC1918Addr);
+  EXPECT_TRUE(IPFromString(kIPv4RFC1918AddrString, &addr));
+  EXPECT_EQ(addr.ToString(), kIPv4RFC1918AddrString);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr2 = IPAddress(kIPv4PublicAddr);
+  EXPECT_TRUE(IPFromString(kIPv4PublicAddrString, &addr));
+  EXPECT_EQ(addr.ToString(), kIPv4PublicAddrString);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr2 = IPAddress(in6addr_any);
+  EXPECT_TRUE(IPFromString(kIPv6AnyAddrString, &addr));
+  EXPECT_EQ(addr.ToString(), kIPv6AnyAddrString);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr2 = IPAddress(in6addr_loopback);
+  EXPECT_TRUE(IPFromString(kIPv6LoopbackAddrString, &addr));
+  EXPECT_EQ(addr.ToString(), kIPv6LoopbackAddrString);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr2 = IPAddress(kIPv6LinkLocalAddr);
+  EXPECT_TRUE(IPFromString(kIPv6LinkLocalAddrString, &addr));
+  EXPECT_EQ(addr.ToString(), kIPv6LinkLocalAddrString);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr2 = IPAddress(kIPv6PublicAddr);
+  EXPECT_TRUE(IPFromString(kIPv6PublicAddrString, &addr));
+  EXPECT_EQ(addr.ToString(), kIPv6PublicAddrString);
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  addr2 = IPAddress(kIPv4MappedRFC1918Addr);
+  EXPECT_TRUE(IPFromString(kIPv4MappedV4StyleAddrString, &addr));
+  EXPECT_PRED2(AreEqual, addr, addr2);
+
+  // Broken cases, should set addr to AF_UNSPEC.
+  EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString1);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString2);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString3);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString4);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString5);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString6);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString1);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString2);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString3);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString4);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString5);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString6);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString7);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString8);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString9);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString10);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString11);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString12);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString13);
+  EXPECT_PRED1(BrokenIPStringFails, kIPv6BrokenString14);
+}
+
+TEST(IPAddressTest, TestIPFromAddrInfo) {
+  struct sockaddr_in expected4;
+  struct sockaddr_in6 expected6;
+  struct addrinfo test_info;
+  struct addrinfo next_info;
+  memset(&next_info, 'A', sizeof(next_info));
+  test_info.ai_next = &next_info;
+  // Check that we can get an IPv4 address out.
+  test_info.ai_addr = reinterpret_cast<struct sockaddr*>(&expected4);
+  expected4.sin_addr.s_addr = HostToNetwork32(kIPv4PublicAddr);
+  expected4.sin_family = AF_INET;
+  IPAddress expected(kIPv4PublicAddr);
+  IPAddress addr;
+  EXPECT_TRUE(IPFromAddrInfo(&test_info, &addr));
+  EXPECT_EQ(expected, addr);
+  // Check that we can get an IPv6 address out.
+  expected6.sin6_addr = kIPv6PublicAddr;
+  expected6.sin6_family = AF_INET6;
+  expected = IPAddress(kIPv6PublicAddr);
+  test_info.ai_addr = reinterpret_cast<struct sockaddr*>(&expected6);
+  EXPECT_TRUE(IPFromAddrInfo(&test_info, &addr));
+  EXPECT_EQ(expected, addr);
+  // Check that unspec fails.
+  expected6.sin6_family = AF_UNSPEC;
+  EXPECT_FALSE(IPFromAddrInfo(&test_info, &addr));
+  // Check a zeroed out addrinfo doesn't crash us.
+  memset(&next_info, 0, sizeof(next_info));
+  EXPECT_FALSE(IPFromAddrInfo(&next_info, &addr));
+}
+
+TEST(IPAddressTest, TestIsPrivate) {
+  EXPECT_FALSE(IPIsPrivate(IPAddress(INADDR_ANY)));
+  EXPECT_FALSE(IPIsPrivate(IPAddress(kIPv4PublicAddr)));
+  EXPECT_FALSE(IPIsPrivate(IPAddress(in6addr_any)));
+  EXPECT_FALSE(IPIsPrivate(IPAddress(kIPv6PublicAddr)));
+  EXPECT_FALSE(IPIsPrivate(IPAddress(kIPv4MappedAnyAddr)));
+  EXPECT_FALSE(IPIsPrivate(IPAddress(kIPv4MappedPublicAddr)));
+
+  EXPECT_TRUE(IPIsPrivate(IPAddress(kIPv4RFC1918Addr)));
+  EXPECT_TRUE(IPIsPrivate(IPAddress(INADDR_LOOPBACK)));
+  EXPECT_TRUE(IPIsPrivate(IPAddress(in6addr_loopback)));
+  EXPECT_TRUE(IPIsPrivate(IPAddress(kIPv6LinkLocalAddr)));
+}
+
+TEST(IPAddressTest, TestIsNil) {
+  IPAddress addr;
+  EXPECT_TRUE(IPAddress().IsNil());
+
+  EXPECT_TRUE(IPFromString(kIPv6AnyAddrString, &addr));
+  EXPECT_FALSE(addr.IsNil());
+
+  EXPECT_TRUE(IPFromString(kIPv4AnyAddrString, &addr));
+  EXPECT_FALSE(addr.IsNil());
+
+  EXPECT_FALSE(IPAddress(kIPv4PublicAddr).IsNil());
+}
+
+TEST(IPAddressTest, TestIsLoopback) {
+  EXPECT_FALSE(IPIsLoopback(IPAddress(INADDR_ANY)));
+  EXPECT_FALSE(IPIsLoopback(IPAddress(kIPv4PublicAddr)));
+  EXPECT_FALSE(IPIsLoopback(IPAddress(in6addr_any)));
+  EXPECT_FALSE(IPIsLoopback(IPAddress(kIPv6PublicAddr)));
+  EXPECT_FALSE(IPIsLoopback(IPAddress(kIPv4MappedAnyAddr)));
+  EXPECT_FALSE(IPIsLoopback(IPAddress(kIPv4MappedPublicAddr)));
+
+  EXPECT_TRUE(IPIsLoopback(IPAddress(INADDR_LOOPBACK)));
+  // Try an address in the loopback range (127.0.0.0/8) other than the typical
+  // 127.0.0.1.
+  EXPECT_TRUE(IPIsLoopback(IPAddress(0x7f010203)));
+  EXPECT_TRUE(IPIsLoopback(IPAddress(in6addr_loopback)));
+}
+
+// Verify that IPIsAny catches all cases of "any" address.
+TEST(IPAddressTest, TestIsAny) {
+  IPAddress addr;
+
+  EXPECT_TRUE(IPFromString(kIPv6AnyAddrString, &addr));
+  EXPECT_TRUE(IPIsAny(addr));
+
+  EXPECT_TRUE(IPFromString(kIPv4AnyAddrString, &addr));
+  EXPECT_TRUE(IPIsAny(addr));
+
+  EXPECT_TRUE(IPIsAny(IPAddress(kIPv4MappedAnyAddr)));
+}
+
+TEST(IPAddressTest, TestIsEui64) {
+  IPAddress addr;
+  EXPECT_TRUE(IPFromString(kIPv6EuiAddrString, &addr));
+  EXPECT_TRUE(IPIsMacBased(addr));
+
+  EXPECT_TRUE(IPFromString(kIPv6TemporaryAddrString, &addr));
+  EXPECT_FALSE(IPIsMacBased(addr));
+
+  EXPECT_TRUE(IPFromString(kIPv6LinkLocalAddrString, &addr));
+  EXPECT_TRUE(IPIsMacBased(addr));
+
+  EXPECT_TRUE(IPFromString(kIPv6AnyAddrString, &addr));
+  EXPECT_FALSE(IPIsMacBased(addr));
+
+  EXPECT_TRUE(IPFromString(kIPv6LoopbackAddrString, &addr));
+  EXPECT_FALSE(IPIsMacBased(addr));
+}
+
+TEST(IPAddressTest, TestNormalized) {
+  // Check normalizing a ::ffff:a.b.c.d address.
+  IPAddress addr;
+  EXPECT_TRUE(IPFromString(kIPv4MappedV4StyleAddrString, &addr));
+  IPAddress addr2(kIPv4RFC1918Addr);
+  addr = addr.Normalized();
+  EXPECT_EQ(addr2, addr);
+
+  // Check normalizing a ::ffff:aabb:ccdd address.
+  addr = IPAddress(kIPv4MappedPublicAddr);
+  addr2 = IPAddress(kIPv4PublicAddr);
+  addr = addr.Normalized();
+  EXPECT_EQ(addr, addr2);
+
+  // Check that a non-mapped v6 addresses isn't altered.
+  addr = IPAddress(kIPv6PublicAddr);
+  addr2 = IPAddress(kIPv6PublicAddr);
+  addr = addr.Normalized();
+  EXPECT_EQ(addr, addr2);
+
+  // Check that addresses that look a bit like mapped addresses aren't altered
+  EXPECT_TRUE(IPFromString("fe80::ffff:0102:0304", &addr));
+  addr2 = addr;
+  addr = addr.Normalized();
+  EXPECT_EQ(addr, addr2);
+  EXPECT_TRUE(IPFromString("::0102:0304", &addr));
+  addr2 = addr;
+  addr = addr.Normalized();
+  EXPECT_EQ(addr, addr2);
+  // This string should 'work' as an IP address but is not a mapped address,
+  // so it shouldn't change on normalization.
+  EXPECT_TRUE(IPFromString("::192.168.7.1", &addr));
+  addr2 = addr;
+  addr = addr.Normalized();
+  EXPECT_EQ(addr, addr2);
+
+  // Check that v4 addresses aren't altered.
+  addr = IPAddress(htonl(kIPv4PublicAddr));
+  addr2 = IPAddress(htonl(kIPv4PublicAddr));
+  addr = addr.Normalized();
+  EXPECT_EQ(addr, addr2);
+}
+
+TEST(IPAddressTest, TestAsIPv6Address) {
+  IPAddress addr(kIPv4PublicAddr);
+  IPAddress addr2(kIPv4MappedPublicAddr);
+  addr = addr.AsIPv6Address();
+  EXPECT_EQ(addr, addr2);
+
+  addr = IPAddress(kIPv4MappedPublicAddr);
+  addr2 = IPAddress(kIPv4MappedPublicAddr);
+  addr = addr.AsIPv6Address();
+  EXPECT_EQ(addr, addr2);
+
+  addr = IPAddress(kIPv6PublicAddr);
+  addr2 = IPAddress(kIPv6PublicAddr);
+  addr = addr.AsIPv6Address();
+  EXPECT_EQ(addr, addr2);
+}
+
+// Disabled for UBSan: https://bugs.chromium.org/p/webrtc/issues/detail?id=5491
+#ifdef UNDEFINED_SANITIZER
+#define MAYBE_TestCountIPMaskBits DISABLED_TestCountIPMaskBits
+#else
+#define MAYBE_TestCountIPMaskBits TestCountIPMaskBits
+#endif
+TEST(IPAddressTest, MAYBE_TestCountIPMaskBits) {
+  IPAddress mask;
+  // IPv4 on byte boundaries
+  EXPECT_PRED2(CheckMaskCount, "255.255.255.255", 32);
+  EXPECT_PRED2(CheckMaskCount, "255.255.255.0", 24);
+  EXPECT_PRED2(CheckMaskCount, "255.255.0.0", 16);
+  EXPECT_PRED2(CheckMaskCount, "255.0.0.0", 8);
+  EXPECT_PRED2(CheckMaskCount, "0.0.0.0", 0);
+
+  // IPv4 not on byte boundaries
+  EXPECT_PRED2(CheckMaskCount, "128.0.0.0", 1);
+  EXPECT_PRED2(CheckMaskCount, "224.0.0.0", 3);
+  EXPECT_PRED2(CheckMaskCount, "255.248.0.0", 13);
+  EXPECT_PRED2(CheckMaskCount, "255.255.224.0", 19);
+  EXPECT_PRED2(CheckMaskCount, "255.255.255.252", 30);
+
+  // V6 on byte boundaries
+  EXPECT_PRED2(CheckMaskCount, "::", 0);
+  EXPECT_PRED2(CheckMaskCount, "ff00::", 8);
+  EXPECT_PRED2(CheckMaskCount, "ffff::", 16);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ff00::", 24);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff::", 32);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ff00::", 40);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff::", 48);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ff00::", 56);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff::", 64);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ff00::", 72);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff::", 80);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ff00::", 88);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff::", 96);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ff00:0000", 104);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000", 112);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00", 120);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128);
+
+  // V6 not on byte boundaries.
+  EXPECT_PRED2(CheckMaskCount, "8000::", 1);
+  EXPECT_PRED2(CheckMaskCount, "ff80::", 9);
+  EXPECT_PRED2(CheckMaskCount, "ffff:fe00::", 23);
+  EXPECT_PRED2(CheckMaskCount, "ffff:fffe::", 31);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:e000::", 35);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffe0::", 43);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:f800::", 53);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:fff8::", 61);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:fc00::", 70);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:fffc::", 78);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:8000::", 81);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ff80::", 89);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:fe00::", 103);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:fffe:0000", 111);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fc00", 118);
+  EXPECT_PRED2(CheckMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc", 126);
+
+  // Non-contiguous ranges. These are invalid but lets test them
+  // to make sure they don't crash anything or infinite loop or something.
+  EXPECT_PRED1(TryInvalidMaskCount, "217.0.0.0");
+  EXPECT_PRED1(TryInvalidMaskCount, "255.185.0.0");
+  EXPECT_PRED1(TryInvalidMaskCount, "255.255.251.0");
+  EXPECT_PRED1(TryInvalidMaskCount, "255.255.251.255");
+  EXPECT_PRED1(TryInvalidMaskCount, "255.255.254.201");
+  EXPECT_PRED1(TryInvalidMaskCount, "::1");
+  EXPECT_PRED1(TryInvalidMaskCount, "fe80::1");
+  EXPECT_PRED1(TryInvalidMaskCount, "ff80::1");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff::1");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ff00:1::1");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff::ffff:1");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ff00:1::");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff::ff00");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ff00:1234::");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:0012::ffff");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ff01::");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:7f00::");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ff7a::");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:7f00:0000");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ff70:0000");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0211");
+  EXPECT_PRED1(TryInvalidMaskCount, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff7f");
+}
+
+TEST(IPAddressTest, TestTruncateIP) {
+  EXPECT_PRED3(CheckTruncateIP, "255.255.255.255", 24, "255.255.255.0");
+  EXPECT_PRED3(CheckTruncateIP, "255.255.255.255", 16, "255.255.0.0");
+  EXPECT_PRED3(CheckTruncateIP, "255.255.255.255", 8, "255.0.0.0");
+  EXPECT_PRED3(CheckTruncateIP, "202.67.7.255", 24, "202.67.7.0");
+  EXPECT_PRED3(CheckTruncateIP, "202.129.65.205", 16, "202.129.0.0");
+  EXPECT_PRED3(CheckTruncateIP, "55.25.2.77", 8, "55.0.0.0");
+  EXPECT_PRED3(CheckTruncateIP, "74.128.99.254", 1, "0.0.0.0");
+  EXPECT_PRED3(CheckTruncateIP, "106.55.99.254", 3, "96.0.0.0");
+  EXPECT_PRED3(CheckTruncateIP, "172.167.53.222", 13, "172.160.0.0");
+  EXPECT_PRED3(CheckTruncateIP, "255.255.224.0", 18, "255.255.192.0");
+  EXPECT_PRED3(CheckTruncateIP, "255.255.255.252", 28, "255.255.255.240");
+
+  EXPECT_PRED3(CheckTruncateIP, "fe80:1111:2222:3333:4444:5555:6666:7777", 1,
+               "8000::");
+  EXPECT_PRED3(CheckTruncateIP, "fff0:1111:2222:3333:4444:5555:6666:7777", 9,
+               "ff80::");
+  EXPECT_PRED3(CheckTruncateIP, "ffff:ff80:1111:2222:3333:4444:5555:6666", 23,
+               "ffff:fe00::");
+  EXPECT_PRED3(CheckTruncateIP, "ffff:ff80:1111:2222:3333:4444:5555:6666", 32,
+               "ffff:ff80::");
+  EXPECT_PRED3(CheckTruncateIP, "2400:f9af:e456:1111:2222:3333:4444:5555", 35,
+               "2400:f9af:e000::");
+  EXPECT_PRED3(CheckTruncateIP, "9999:1111:2233:4444:5555:6666:7777:8888", 53,
+               "9999:1111:2233:4000::");
+  EXPECT_PRED3(CheckTruncateIP, "9999:1111:2233:4567:5555:6666:7777:8888", 64,
+               "9999:1111:2233:4567::");
+  EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 68,
+               "1111:2222:3333:4444:5000::");
+  EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 92,
+               "1111:2222:3333:4444:5555:6660::");
+  EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 96,
+               "1111:2222:3333:4444:5555:6666::");
+  EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 105,
+               "1111:2222:3333:4444:5555:6666:7700::");
+  EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 124,
+               "1111:2222:3333:4444:5555:6666:7777:8880");
+
+  // Slightly degenerate cases
+  EXPECT_PRED3(CheckTruncateIP, "202.165.33.127", 32, "202.165.33.127");
+  EXPECT_PRED3(CheckTruncateIP, "235.105.77.12", 0, "0.0.0.0");
+  EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 128,
+               "1111:2222:3333:4444:5555:6666:7777:8888");
+  EXPECT_PRED3(CheckTruncateIP, "1111:2222:3333:4444:5555:6666:7777:8888", 0,
+               "::");
+}
+
+TEST(IPAddressTest, TestCategorizeIPv6) {
+  // Test determining if an IPAddress is 6Bone/6To4/Teredo/etc.
+  // IPv4 address, should be none of these (not even v4compat/v4mapped).
+  IPAddress v4_addr(kIPv4PublicAddr);
+  EXPECT_FALSE(IPIs6Bone(v4_addr));
+  EXPECT_FALSE(IPIs6To4(v4_addr));
+  EXPECT_FALSE(IPIsSiteLocal(v4_addr));
+  EXPECT_FALSE(IPIsTeredo(v4_addr));
+  EXPECT_FALSE(IPIsULA(v4_addr));
+  EXPECT_FALSE(IPIsV4Compatibility(v4_addr));
+  EXPECT_FALSE(IPIsV4Mapped(v4_addr));
+  // Linklocal (fe80::/16) adddress; should be none of these.
+  IPAddress linklocal_addr(kIPv6LinkLocalAddr);
+  EXPECT_FALSE(IPIs6Bone(linklocal_addr));
+  EXPECT_FALSE(IPIs6To4(linklocal_addr));
+  EXPECT_FALSE(IPIsSiteLocal(linklocal_addr));
+  EXPECT_FALSE(IPIsTeredo(linklocal_addr));
+  EXPECT_FALSE(IPIsULA(linklocal_addr));
+  EXPECT_FALSE(IPIsV4Compatibility(linklocal_addr));
+  EXPECT_FALSE(IPIsV4Mapped(linklocal_addr));
+  // 'Normal' IPv6 address, should also be none of these.
+  IPAddress normal_addr(kIPv6PublicAddr);
+  EXPECT_FALSE(IPIs6Bone(normal_addr));
+  EXPECT_FALSE(IPIs6To4(normal_addr));
+  EXPECT_FALSE(IPIsSiteLocal(normal_addr));
+  EXPECT_FALSE(IPIsTeredo(normal_addr));
+  EXPECT_FALSE(IPIsULA(normal_addr));
+  EXPECT_FALSE(IPIsV4Compatibility(normal_addr));
+  EXPECT_FALSE(IPIsV4Mapped(normal_addr));
+  // IPv4 mapped address (::ffff:123.123.123.123)
+  IPAddress v4mapped_addr(kIPv4MappedPublicAddr);
+  EXPECT_TRUE(IPIsV4Mapped(v4mapped_addr));
+  EXPECT_FALSE(IPIsV4Compatibility(v4mapped_addr));
+  EXPECT_FALSE(IPIs6Bone(v4mapped_addr));
+  EXPECT_FALSE(IPIs6To4(v4mapped_addr));
+  EXPECT_FALSE(IPIsSiteLocal(v4mapped_addr));
+  EXPECT_FALSE(IPIsTeredo(v4mapped_addr));
+  EXPECT_FALSE(IPIsULA(v4mapped_addr));
+  // IPv4 compatibility address (::123.123.123.123)
+  IPAddress v4compat_addr;
+  IPFromString("::192.168.7.1", &v4compat_addr);
+  EXPECT_TRUE(IPIsV4Compatibility(v4compat_addr));
+  EXPECT_FALSE(IPIs6Bone(v4compat_addr));
+  EXPECT_FALSE(IPIs6To4(v4compat_addr));
+  EXPECT_FALSE(IPIsSiteLocal(v4compat_addr));
+  EXPECT_FALSE(IPIsTeredo(v4compat_addr));
+  EXPECT_FALSE(IPIsULA(v4compat_addr));
+  EXPECT_FALSE(IPIsV4Mapped(v4compat_addr));
+  // 6Bone address (3FFE::/16)
+  IPAddress sixbone_addr;
+  IPFromString("3FFE:123:456::789:123", &sixbone_addr);
+  EXPECT_TRUE(IPIs6Bone(sixbone_addr));
+  EXPECT_FALSE(IPIs6To4(sixbone_addr));
+  EXPECT_FALSE(IPIsSiteLocal(sixbone_addr));
+  EXPECT_FALSE(IPIsTeredo(sixbone_addr));
+  EXPECT_FALSE(IPIsULA(sixbone_addr));
+  EXPECT_FALSE(IPIsV4Mapped(sixbone_addr));
+  EXPECT_FALSE(IPIsV4Compatibility(sixbone_addr));
+  // Unique Local Address (FC::/7)
+  IPAddress ula_addr;
+  IPFromString("FC00:123:456::789:123", &ula_addr);
+  EXPECT_TRUE(IPIsULA(ula_addr));
+  EXPECT_FALSE(IPIs6Bone(ula_addr));
+  EXPECT_FALSE(IPIs6To4(ula_addr));
+  EXPECT_FALSE(IPIsSiteLocal(ula_addr));
+  EXPECT_FALSE(IPIsTeredo(ula_addr));
+  EXPECT_FALSE(IPIsV4Mapped(ula_addr));
+  EXPECT_FALSE(IPIsV4Compatibility(ula_addr));
+  // 6To4 Address (2002::/16)
+  IPAddress sixtofour_addr;
+  IPFromString("2002:123:456::789:123", &sixtofour_addr);
+  EXPECT_TRUE(IPIs6To4(sixtofour_addr));
+  EXPECT_FALSE(IPIs6Bone(sixtofour_addr));
+  EXPECT_FALSE(IPIsSiteLocal(sixtofour_addr));
+  EXPECT_FALSE(IPIsTeredo(sixtofour_addr));
+  EXPECT_FALSE(IPIsULA(sixtofour_addr));
+  EXPECT_FALSE(IPIsV4Compatibility(sixtofour_addr));
+  EXPECT_FALSE(IPIsV4Mapped(sixtofour_addr));
+  // Site Local address (FEC0::/10)
+  IPAddress sitelocal_addr;
+  IPFromString("FEC0:123:456::789:123", &sitelocal_addr);
+  EXPECT_TRUE(IPIsSiteLocal(sitelocal_addr));
+  EXPECT_FALSE(IPIs6Bone(sitelocal_addr));
+  EXPECT_FALSE(IPIs6To4(sitelocal_addr));
+  EXPECT_FALSE(IPIsTeredo(sitelocal_addr));
+  EXPECT_FALSE(IPIsULA(sitelocal_addr));
+  EXPECT_FALSE(IPIsV4Compatibility(sitelocal_addr));
+  EXPECT_FALSE(IPIsV4Mapped(sitelocal_addr));
+  // Teredo Address (2001:0000::/32)
+  IPAddress teredo_addr;
+  IPFromString("2001:0000:123:456::789:123", &teredo_addr);
+  EXPECT_TRUE(IPIsTeredo(teredo_addr));
+  EXPECT_FALSE(IPIsSiteLocal(teredo_addr));
+  EXPECT_FALSE(IPIs6Bone(teredo_addr));
+  EXPECT_FALSE(IPIs6To4(teredo_addr));
+  EXPECT_FALSE(IPIsULA(teredo_addr));
+  EXPECT_FALSE(IPIsV4Compatibility(teredo_addr));
+  EXPECT_FALSE(IPIsV4Mapped(teredo_addr));
+}
+
+TEST(IPAddressTest, TestToSensitiveString) {
+  IPAddress addr_v4 = IPAddress(kIPv4PublicAddr);
+  IPAddress addr_v6 = IPAddress(kIPv6PublicAddr);
+  IPAddress addr_v6_2 = IPAddress(kIPv6PublicAddr2);
+  EXPECT_EQ(kIPv4PublicAddrString, addr_v4.ToString());
+  EXPECT_EQ(kIPv6PublicAddrString, addr_v6.ToString());
+  EXPECT_EQ(kIPv6PublicAddr2String, addr_v6_2.ToString());
+#if defined(NDEBUG)
+  EXPECT_EQ(kIPv4PublicAddrAnonymizedString, addr_v4.ToSensitiveString());
+  EXPECT_EQ(kIPv6PublicAddrAnonymizedString, addr_v6.ToSensitiveString());
+  EXPECT_EQ(kIPv6PublicAddr2AnonymizedString, addr_v6_2.ToSensitiveString());
+#else
+  EXPECT_EQ(kIPv4PublicAddrString, addr_v4.ToSensitiveString());
+  EXPECT_EQ(kIPv6PublicAddrString, addr_v6.ToSensitiveString());
+  EXPECT_EQ(kIPv6PublicAddr2String, addr_v6_2.ToSensitiveString());
+#endif  // defined(NDEBUG)
+}
+
+TEST(IPAddressTest, TestInterfaceAddress) {
+  in6_addr addr;
+  InterfaceAddress addr1(kIPv6PublicAddr,
+                         IPV6_ADDRESS_FLAG_TEMPORARY);
+  EXPECT_EQ(addr1.ipv6_flags(), IPV6_ADDRESS_FLAG_TEMPORARY);
+  EXPECT_EQ(addr1.family(), AF_INET6);
+
+  addr = addr1.ipv6_address();
+  EXPECT_TRUE(IN6_ARE_ADDR_EQUAL(&addr, &kIPv6PublicAddr));
+
+  InterfaceAddress addr2 = addr1;
+  EXPECT_EQ(addr1, addr2);
+  EXPECT_EQ(addr2.ipv6_flags(), IPV6_ADDRESS_FLAG_TEMPORARY);
+  addr = addr2.ipv6_address();
+  EXPECT_TRUE(IN6_ARE_ADDR_EQUAL(&addr, &kIPv6PublicAddr));
+
+  InterfaceAddress addr3(addr1);
+  EXPECT_EQ(addr1, addr3);
+  EXPECT_EQ(addr3.ipv6_flags(), IPV6_ADDRESS_FLAG_TEMPORARY);
+  addr = addr3.ipv6_address();
+  EXPECT_TRUE(IN6_ARE_ADDR_EQUAL(&addr, &kIPv6PublicAddr));
+
+  InterfaceAddress addr4(kIPv6PublicAddr,
+                         IPV6_ADDRESS_FLAG_DEPRECATED);
+  EXPECT_NE(addr1, addr4);
+
+  // When you compare them as IPAddress, since operator==
+  // is not virtual, it'll be equal.
+  IPAddress *paddr1 = &addr1;
+  IPAddress *paddr4 = &addr4;
+  EXPECT_EQ(*paddr1, *paddr4);
+
+  InterfaceAddress addr5(kIPv6LinkLocalAddr,
+                         IPV6_ADDRESS_FLAG_TEMPORARY);
+  EXPECT_NE(addr1, addr5);
+}
+
+}  // namespace rtc
diff --git a/base/java/src/org/webrtc/ContextUtils.java b/base/java/src/org/webrtc/ContextUtils.java
new file mode 100644
index 0000000..ec9af93
--- /dev/null
+++ b/base/java/src/org/webrtc/ContextUtils.java
@@ -0,0 +1,47 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import android.content.Context;
+import org.webrtc.Logging;
+
+/**
+ * Class for storing the application context and retrieving it in a static context. Similar to
+ * org.chromium.base.ContextUtils.
+ */
+public class ContextUtils {
+  private static final String TAG = "ContextUtils";
+  private static Context applicationContext;
+
+  /**
+   * Stores the application context that will be returned by getApplicationContext. This is called
+   * by PeerConnectionFactory.initializeAndroidGlobals.
+   */
+  public static void initialize(Context applicationContext) {
+    if (ContextUtils.applicationContext != null) {
+      // TODO(sakal): Re-enable after the migration period.
+      // throw new RuntimeException("Multiple ContextUtils.initialize calls.");
+      Logging.e(
+          TAG, "Calling ContextUtils.initialize multiple times, this will crash in the future!");
+    }
+    if (applicationContext == null) {
+      throw new RuntimeException("Application context cannot be null for ContextUtils.initialize.");
+    }
+    ContextUtils.applicationContext = applicationContext;
+  }
+
+  /**
+   * Returns the stored application context.
+   */
+  public static Context getApplicationContext() {
+    return applicationContext;
+  }
+}
diff --git a/base/java/src/org/webrtc/Logging.java b/base/java/src/org/webrtc/Logging.java
new file mode 100644
index 0000000..643b8cf
--- /dev/null
+++ b/base/java/src/org/webrtc/Logging.java
@@ -0,0 +1,200 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.EnumSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Java wrapper for WebRTC logging. Logging defaults to java.util.logging.Logger, but will switch to
+ * native logging (rtc::LogMessage) if one of the following static functions are called from the
+ * app:
+ * - Logging.enableLogThreads
+ * - Logging.enableLogTimeStamps
+ * - Logging.enableTracing
+ * - Logging.enableLogToDebugOutput
+ * Using native logging requires the presence of the jingle_peerconnection_so library.
+ */
+public class Logging {
+  private static final Logger fallbackLogger = createFallbackLogger();
+  private static volatile boolean tracingEnabled;
+  private static volatile boolean loggingEnabled;
+  private static enum NativeLibStatus { UNINITIALIZED, LOADED, FAILED }
+  private static volatile NativeLibStatus nativeLibStatus = NativeLibStatus.UNINITIALIZED;
+
+  private static Logger createFallbackLogger() {
+    final Logger fallbackLogger = Logger.getLogger("org.webrtc.Logging");
+    fallbackLogger.setLevel(Level.ALL);
+    return fallbackLogger;
+  }
+
+  private static boolean loadNativeLibrary() {
+    if (nativeLibStatus == NativeLibStatus.UNINITIALIZED) {
+      try {
+        System.loadLibrary("jingle_peerconnection_so");
+        nativeLibStatus = NativeLibStatus.LOADED;
+      } catch (UnsatisfiedLinkError t) {
+        nativeLibStatus = NativeLibStatus.FAILED;
+        fallbackLogger.log(Level.WARNING, "Failed to load jingle_peerconnection_so: ", t);
+      }
+    }
+    return nativeLibStatus == NativeLibStatus.LOADED;
+  }
+
+  // Keep in sync with webrtc/common_types.h:TraceLevel.
+  public enum TraceLevel {
+    TRACE_NONE(0x0000),
+    TRACE_STATEINFO(0x0001),
+    TRACE_WARNING(0x0002),
+    TRACE_ERROR(0x0004),
+    TRACE_CRITICAL(0x0008),
+    TRACE_APICALL(0x0010),
+    TRACE_DEFAULT(0x00ff),
+    TRACE_MODULECALL(0x0020),
+    TRACE_MEMORY(0x0100),
+    TRACE_TIMER(0x0200),
+    TRACE_STREAM(0x0400),
+    TRACE_DEBUG(0x0800),
+    TRACE_INFO(0x1000),
+    TRACE_TERSEINFO(0x2000),
+    TRACE_ALL(0xffff);
+
+    public final int level;
+    TraceLevel(int level) {
+      this.level = level;
+    }
+  }
+
+  // Keep in sync with webrtc/base/logging.h:LoggingSeverity.
+  public enum Severity { LS_SENSITIVE, LS_VERBOSE, LS_INFO, LS_WARNING, LS_ERROR, LS_NONE }
+
+  public static void enableLogThreads() {
+    if (!loadNativeLibrary()) {
+      fallbackLogger.log(Level.WARNING, "Cannot enable log thread because native lib not loaded.");
+      return;
+    }
+    nativeEnableLogThreads();
+  }
+
+  public static void enableLogTimeStamps() {
+    if (!loadNativeLibrary()) {
+      fallbackLogger.log(
+          Level.WARNING, "Cannot enable log timestamps because native lib not loaded.");
+      return;
+    }
+    nativeEnableLogTimeStamps();
+  }
+
+  // Enable tracing to |path| of messages of |levels|.
+  // On Android, use "logcat:" for |path| to send output there.
+  // Note: this function controls the output of the WEBRTC_TRACE() macros.
+  public static synchronized void enableTracing(String path, EnumSet<TraceLevel> levels) {
+    if (!loadNativeLibrary()) {
+      fallbackLogger.log(Level.WARNING, "Cannot enable tracing because native lib not loaded.");
+      return;
+    }
+
+    if (tracingEnabled) {
+      return;
+    }
+    int nativeLevel = 0;
+    for (TraceLevel level : levels) {
+      nativeLevel |= level.level;
+    }
+    nativeEnableTracing(path, nativeLevel);
+    tracingEnabled = true;
+  }
+
+  // Enable diagnostic logging for messages of |severity| to the platform debug
+  // output. On Android, the output will be directed to Logcat.
+  // Note: this function starts collecting the output of the LOG() macros.
+  public static synchronized void enableLogToDebugOutput(Severity severity) {
+    if (!loadNativeLibrary()) {
+      fallbackLogger.log(Level.WARNING, "Cannot enable logging because native lib not loaded.");
+      return;
+    }
+    nativeEnableLogToDebugOutput(severity.ordinal());
+    loggingEnabled = true;
+  }
+
+  public static void log(Severity severity, String tag, String message) {
+    if (loggingEnabled) {
+      nativeLog(severity.ordinal(), tag, message);
+      return;
+    }
+
+    // Fallback to system log.
+    Level level;
+    switch (severity) {
+      case LS_ERROR:
+        level = Level.SEVERE;
+        break;
+      case LS_WARNING:
+        level = Level.WARNING;
+        break;
+      case LS_INFO:
+        level = Level.INFO;
+        break;
+      default:
+        level = Level.FINE;
+        break;
+    }
+    fallbackLogger.log(level, tag + ": " + message);
+  }
+
+  public static void d(String tag, String message) {
+    log(Severity.LS_INFO, tag, message);
+  }
+
+  public static void e(String tag, String message) {
+    log(Severity.LS_ERROR, tag, message);
+  }
+
+  public static void w(String tag, String message) {
+    log(Severity.LS_WARNING, tag, message);
+  }
+
+  public static void e(String tag, String message, Throwable e) {
+    log(Severity.LS_ERROR, tag, message);
+    log(Severity.LS_ERROR, tag, e.toString());
+    log(Severity.LS_ERROR, tag, getStackTraceString(e));
+  }
+
+  public static void w(String tag, String message, Throwable e) {
+    log(Severity.LS_WARNING, tag, message);
+    log(Severity.LS_WARNING, tag, e.toString());
+    log(Severity.LS_WARNING, tag, getStackTraceString(e));
+  }
+
+  public static void v(String tag, String message) {
+    log(Severity.LS_VERBOSE, tag, message);
+  }
+
+  private static String getStackTraceString(Throwable e) {
+    if (e == null) {
+      return "";
+    }
+
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    e.printStackTrace(pw);
+    return sw.toString();
+  }
+
+  private static native void nativeEnableTracing(String path, int nativeLevels);
+  private static native void nativeEnableLogToDebugOutput(int nativeSeverity);
+  private static native void nativeEnableLogThreads();
+  private static native void nativeEnableLogTimeStamps();
+  private static native void nativeLog(int severity, String tag, String message);
+}
diff --git a/base/java/src/org/webrtc/OWNERS b/base/java/src/org/webrtc/OWNERS
new file mode 100644
index 0000000..4d31ffb
--- /dev/null
+++ b/base/java/src/org/webrtc/OWNERS
@@ -0,0 +1 @@
+magjed@webrtc.org
diff --git a/base/java/src/org/webrtc/Size.java b/base/java/src/org/webrtc/Size.java
new file mode 100644
index 0000000..a711b5d
--- /dev/null
+++ b/base/java/src/org/webrtc/Size.java
@@ -0,0 +1,45 @@
+/*
+ *  Copyright 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+/**
+ * Class for representing size of an object. Very similar to android.util.Size but available on all
+ * devices.
+ */
+public class Size {
+  public int width;
+  public int height;
+
+  public Size(int width, int height) {
+    this.width = width;
+    this.height = height;
+  }
+
+  @Override
+  public String toString() {
+    return width + "x" + height;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (!(other instanceof Size)) {
+      return false;
+    }
+    final Size otherSize = (Size) other;
+    return width == otherSize.width && height == otherSize.height;
+  }
+
+  @Override
+  public int hashCode() {
+    // Use prime close to 2^16 to avoid collisions for normal values less than 2^16.
+    return 1 + 65537 * width + height;
+  }
+}
diff --git a/base/java/src/org/webrtc/ThreadUtils.java b/base/java/src/org/webrtc/ThreadUtils.java
new file mode 100644
index 0000000..df2c2d0
--- /dev/null
+++ b/base/java/src/org/webrtc/ThreadUtils.java
@@ -0,0 +1,220 @@
+/*
+ *  Copyright 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ThreadUtils {
+  /**
+   * Utility class to be used for checking that a method is called on the correct thread.
+   */
+  public static class ThreadChecker {
+    private Thread thread = Thread.currentThread();
+
+    public void checkIsOnValidThread() {
+      if (thread == null) {
+        thread = Thread.currentThread();
+      }
+      if (Thread.currentThread() != thread) {
+        throw new IllegalStateException("Wrong thread");
+      }
+    }
+
+    public void detachThread() {
+      thread = null;
+    }
+  }
+
+  /**
+   * Throws exception if called from other than main thread.
+   */
+  public static void checkIsOnMainThread() {
+    if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
+      throw new IllegalStateException("Not on main thread!");
+    }
+  }
+
+  /**
+   * Utility interface to be used with executeUninterruptibly() to wait for blocking operations
+   * to complete without getting interrupted..
+   */
+  public interface BlockingOperation { void run() throws InterruptedException; }
+
+  /**
+   * Utility method to make sure a blocking operation is executed to completion without getting
+   * interrupted. This should be used in cases where the operation is waiting for some critical
+   * work, e.g. cleanup, that must complete before returning. If the thread is interrupted during
+   * the blocking operation, this function will re-run the operation until completion, and only then
+   * re-interrupt the thread.
+   */
+  public static void executeUninterruptibly(BlockingOperation operation) {
+    boolean wasInterrupted = false;
+    while (true) {
+      try {
+        operation.run();
+        break;
+      } catch (InterruptedException e) {
+        // Someone is asking us to return early at our convenience. We can't cancel this operation,
+        // but we should preserve the information and pass it along.
+        wasInterrupted = true;
+      }
+    }
+    // Pass interruption information along.
+    if (wasInterrupted) {
+      Thread.currentThread().interrupt();
+    }
+  }
+
+  public static boolean joinUninterruptibly(final Thread thread, long timeoutMs) {
+    final long startTimeMs = SystemClock.elapsedRealtime();
+    long timeRemainingMs = timeoutMs;
+    boolean wasInterrupted = false;
+    while (timeRemainingMs > 0) {
+      try {
+        thread.join(timeRemainingMs);
+        break;
+      } catch (InterruptedException e) {
+        // Someone is asking us to return early at our convenience. We can't cancel this operation,
+        // but we should preserve the information and pass it along.
+        wasInterrupted = true;
+        final long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs;
+        timeRemainingMs = timeoutMs - elapsedTimeMs;
+      }
+    }
+    // Pass interruption information along.
+    if (wasInterrupted) {
+      Thread.currentThread().interrupt();
+    }
+    return !thread.isAlive();
+  }
+
+  public static void joinUninterruptibly(final Thread thread) {
+    executeUninterruptibly(new BlockingOperation() {
+      @Override
+      public void run() throws InterruptedException {
+        thread.join();
+      }
+    });
+  }
+
+  public static void awaitUninterruptibly(final CountDownLatch latch) {
+    executeUninterruptibly(new BlockingOperation() {
+      @Override
+      public void run() throws InterruptedException {
+        latch.await();
+      }
+    });
+  }
+
+  public static boolean awaitUninterruptibly(CountDownLatch barrier, long timeoutMs) {
+    final long startTimeMs = SystemClock.elapsedRealtime();
+    long timeRemainingMs = timeoutMs;
+    boolean wasInterrupted = false;
+    boolean result = false;
+    do {
+      try {
+        result = barrier.await(timeRemainingMs, TimeUnit.MILLISECONDS);
+        break;
+      } catch (InterruptedException e) {
+        // Someone is asking us to return early at our convenience. We can't cancel this operation,
+        // but we should preserve the information and pass it along.
+        wasInterrupted = true;
+        final long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs;
+        timeRemainingMs = timeoutMs - elapsedTimeMs;
+      }
+    } while (timeRemainingMs > 0);
+    // Pass interruption information along.
+    if (wasInterrupted) {
+      Thread.currentThread().interrupt();
+    }
+    return result;
+  }
+
+  public static void waitUninterruptibly(final Object object) {
+    executeUninterruptibly(new BlockingOperation() {
+      @Override
+      public void run() throws InterruptedException {
+        object.wait();
+      }
+    });
+  }
+
+  /**
+   * Post |callable| to |handler| and wait for the result.
+   */
+  public static <V> V invokeAtFrontUninterruptibly(
+      final Handler handler, final Callable<V> callable) {
+    if (handler.getLooper().getThread() == Thread.currentThread()) {
+      try {
+        return callable.call();
+      } catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    }
+    // Place-holder classes that are assignable inside nested class.
+    class CaughtException {
+      Exception e;
+    }
+    class Result {
+      public V value;
+    }
+    final Result result = new Result();
+    final CaughtException caughtException = new CaughtException();
+    final CountDownLatch barrier = new CountDownLatch(1);
+    handler.post(new Runnable() {
+      @Override
+      public void run() {
+        try {
+          result.value = callable.call();
+        } catch (Exception e) {
+          caughtException.e = e;
+        }
+        barrier.countDown();
+      }
+    });
+    awaitUninterruptibly(barrier);
+    // Re-throw any runtime exception caught inside the other thread. Since this is an invoke, add
+    // stack trace for the waiting thread as well.
+    if (caughtException.e != null) {
+      final RuntimeException runtimeException = new RuntimeException(caughtException.e);
+      runtimeException.setStackTrace(
+          concatStackTraces(caughtException.e.getStackTrace(), runtimeException.getStackTrace()));
+      throw runtimeException;
+    }
+    return result.value;
+  }
+
+  /**
+   * Post |runner| to |handler|, at the front, and wait for completion.
+   */
+  public static void invokeAtFrontUninterruptibly(final Handler handler, final Runnable runner) {
+    invokeAtFrontUninterruptibly(handler, new Callable<Void>() {
+      @Override
+      public Void call() {
+        runner.run();
+        return null;
+      }
+    });
+  }
+
+  static StackTraceElement[] concatStackTraces(
+      StackTraceElement[] inner, StackTraceElement[] outer) {
+    final StackTraceElement[] combined = new StackTraceElement[inner.length + outer.length];
+    System.arraycopy(inner, 0, combined, 0, inner.length);
+    System.arraycopy(outer, 0, combined, inner.length, outer.length);
+    return combined;
+  }
+}
diff --git a/base/json.cc b/base/json.cc
new file mode 100644
index 0000000..c692534
--- /dev/null
+++ b/base/json.cc
@@ -0,0 +1,300 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/json.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <sstream>
+
+namespace rtc {
+
+bool GetStringFromJson(const Json::Value& in, std::string* out) {
+  if (!in.isString()) {
+    std::ostringstream s;
+    if (in.isBool()) {
+      s << std::boolalpha << in.asBool();
+    } else if (in.isInt()) {
+      s << in.asInt();
+    } else if (in.isUInt()) {
+      s << in.asUInt();
+    } else if (in.isDouble()) {
+      s << in.asDouble();
+    } else {
+      return false;
+    }
+    *out = s.str();
+  } else {
+    *out = in.asString();
+  }
+  return true;
+}
+
+bool GetIntFromJson(const Json::Value& in, int* out) {
+  bool ret;
+  if (!in.isString()) {
+    ret = in.isConvertibleTo(Json::intValue);
+    if (ret) {
+      *out = in.asInt();
+    }
+  } else {
+    long val;  // NOLINT
+    const char* c_str = in.asCString();
+    char* end_ptr;
+    errno = 0;
+    val = strtol(c_str, &end_ptr, 10);  // NOLINT
+    ret = (end_ptr != c_str && *end_ptr == '\0' && !errno &&
+           val >= INT_MIN && val <= INT_MAX);
+    *out = val;
+  }
+  return ret;
+}
+
+bool GetUIntFromJson(const Json::Value& in, unsigned int* out) {
+  bool ret;
+  if (!in.isString()) {
+    ret = in.isConvertibleTo(Json::uintValue);
+    if (ret) {
+      *out = in.asUInt();
+    }
+  } else {
+    unsigned long val;  // NOLINT
+    const char* c_str = in.asCString();
+    char* end_ptr;
+    errno = 0;
+    val = strtoul(c_str, &end_ptr, 10);  // NOLINT
+    ret = (end_ptr != c_str && *end_ptr == '\0' && !errno &&
+           val <= UINT_MAX);
+    *out = val;
+  }
+  return ret;
+}
+
+bool GetBoolFromJson(const Json::Value& in, bool* out) {
+  bool ret;
+  if (!in.isString()) {
+    ret = in.isConvertibleTo(Json::booleanValue);
+    if (ret) {
+      *out = in.asBool();
+    }
+  } else {
+    if (in.asString() == "true") {
+      *out = true;
+      ret = true;
+    } else if (in.asString() == "false") {
+      *out = false;
+      ret = true;
+    } else {
+      ret = false;
+    }
+  }
+  return ret;
+}
+
+bool GetDoubleFromJson(const Json::Value& in, double* out) {
+  bool ret;
+  if (!in.isString()) {
+    ret = in.isConvertibleTo(Json::realValue);
+    if (ret) {
+      *out = in.asDouble();
+    }
+  } else {
+    double val;
+    const char* c_str = in.asCString();
+    char* end_ptr;
+    errno = 0;
+    val = strtod(c_str, &end_ptr);
+    ret = (end_ptr != c_str && *end_ptr == '\0' && !errno);
+    *out = val;
+  }
+  return ret;
+}
+
+namespace {
+template<typename T>
+bool JsonArrayToVector(const Json::Value& value,
+                       bool (*getter)(const Json::Value& in, T* out),
+                       std::vector<T> *vec) {
+  vec->clear();
+  if (!value.isArray()) {
+    return false;
+  }
+
+  for (Json::Value::ArrayIndex i = 0; i < value.size(); ++i) {
+    T val;
+    if (!getter(value[i], &val)) {
+      return false;
+    }
+    vec->push_back(val);
+  }
+
+  return true;
+}
+// Trivial getter helper
+bool GetValueFromJson(const Json::Value& in, Json::Value* out) {
+  *out = in;
+  return true;
+}
+}  // unnamed namespace
+
+bool JsonArrayToValueVector(const Json::Value& in,
+                            std::vector<Json::Value>* out) {
+  return JsonArrayToVector(in, GetValueFromJson, out);
+}
+
+bool JsonArrayToIntVector(const Json::Value& in,
+                          std::vector<int>* out) {
+  return JsonArrayToVector(in, GetIntFromJson, out);
+}
+
+bool JsonArrayToUIntVector(const Json::Value& in,
+                           std::vector<unsigned int>* out) {
+  return JsonArrayToVector(in, GetUIntFromJson, out);
+}
+
+bool JsonArrayToStringVector(const Json::Value& in,
+                             std::vector<std::string>* out) {
+  return JsonArrayToVector(in, GetStringFromJson, out);
+}
+
+bool JsonArrayToBoolVector(const Json::Value& in,
+                           std::vector<bool>* out) {
+  return JsonArrayToVector(in, GetBoolFromJson, out);
+}
+
+bool JsonArrayToDoubleVector(const Json::Value& in,
+                             std::vector<double>* out) {
+  return JsonArrayToVector(in, GetDoubleFromJson, out);
+}
+
+namespace {
+template<typename T>
+Json::Value VectorToJsonArray(const std::vector<T>& vec) {
+  Json::Value result(Json::arrayValue);
+  for (size_t i = 0; i < vec.size(); ++i) {
+    result.append(Json::Value(vec[i]));
+  }
+  return result;
+}
+}  // unnamed namespace
+
+Json::Value ValueVectorToJsonArray(const std::vector<Json::Value>& in) {
+  return VectorToJsonArray(in);
+}
+
+Json::Value IntVectorToJsonArray(const std::vector<int>& in) {
+  return VectorToJsonArray(in);
+}
+
+Json::Value UIntVectorToJsonArray(const std::vector<unsigned int>& in) {
+  return VectorToJsonArray(in);
+}
+
+Json::Value StringVectorToJsonArray(const std::vector<std::string>& in) {
+  return VectorToJsonArray(in);
+}
+
+Json::Value BoolVectorToJsonArray(const std::vector<bool>& in) {
+  return VectorToJsonArray(in);
+}
+
+Json::Value DoubleVectorToJsonArray(const std::vector<double>& in) {
+  return VectorToJsonArray(in);
+}
+
+bool GetValueFromJsonArray(const Json::Value& in, size_t n,
+                           Json::Value* out) {
+  if (!in.isArray() || !in.isValidIndex(static_cast<int>(n))) {
+    return false;
+  }
+
+  *out = in[static_cast<Json::Value::ArrayIndex>(n)];
+  return true;
+}
+
+bool GetIntFromJsonArray(const Json::Value& in, size_t n,
+                         int* out) {
+  Json::Value x;
+  return GetValueFromJsonArray(in, n, &x) && GetIntFromJson(x, out);
+}
+
+bool GetUIntFromJsonArray(const Json::Value& in, size_t n,
+                          unsigned int* out)  {
+  Json::Value x;
+  return GetValueFromJsonArray(in, n, &x) && GetUIntFromJson(x, out);
+}
+
+bool GetStringFromJsonArray(const Json::Value& in, size_t n,
+                            std::string* out) {
+  Json::Value x;
+  return GetValueFromJsonArray(in, n, &x) && GetStringFromJson(x, out);
+}
+
+bool GetBoolFromJsonArray(const Json::Value& in, size_t n,
+                          bool* out) {
+  Json::Value x;
+  return GetValueFromJsonArray(in, n, &x) && GetBoolFromJson(x, out);
+}
+
+bool GetDoubleFromJsonArray(const Json::Value& in, size_t n,
+                            double* out) {
+  Json::Value x;
+  return GetValueFromJsonArray(in, n, &x) && GetDoubleFromJson(x, out);
+}
+
+bool GetValueFromJsonObject(const Json::Value& in, const std::string& k,
+                            Json::Value* out) {
+  if (!in.isObject() || !in.isMember(k)) {
+    return false;
+  }
+
+  *out = in[k];
+  return true;
+}
+
+bool GetIntFromJsonObject(const Json::Value& in, const std::string& k,
+                          int* out) {
+  Json::Value x;
+  return GetValueFromJsonObject(in, k, &x) && GetIntFromJson(x, out);
+}
+
+bool GetUIntFromJsonObject(const Json::Value& in, const std::string& k,
+                           unsigned int* out)  {
+  Json::Value x;
+  return GetValueFromJsonObject(in, k, &x) && GetUIntFromJson(x, out);
+}
+
+bool GetStringFromJsonObject(const Json::Value& in, const std::string& k,
+                             std::string* out)  {
+  Json::Value x;
+  return GetValueFromJsonObject(in, k, &x) && GetStringFromJson(x, out);
+}
+
+bool GetBoolFromJsonObject(const Json::Value& in, const std::string& k,
+                           bool* out) {
+  Json::Value x;
+  return GetValueFromJsonObject(in, k, &x) && GetBoolFromJson(x, out);
+}
+
+bool GetDoubleFromJsonObject(const Json::Value& in, const std::string& k,
+                             double* out) {
+  Json::Value x;
+  return GetValueFromJsonObject(in, k, &x) && GetDoubleFromJson(x, out);
+}
+
+std::string JsonValueToString(const Json::Value& json) {
+  Json::FastWriter w;
+  std::string value = w.write(json);
+  return value.substr(0, value.size() - 1);  // trim trailing newline
+}
+
+}  // namespace rtc
diff --git a/base/json.h b/base/json.h
index 175028f..4c0d222 100644
--- a/base/json.h
+++ b/base/json.h
@@ -11,9 +11,81 @@
 #ifndef WEBRTC_BASE_JSON_H_
 #define WEBRTC_BASE_JSON_H_
 
+#include <string>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/json.h"
+#if !defined(WEBRTC_EXTERNAL_JSON)
+#include "json/json.h"
+#else
+#include "third_party/jsoncpp/json.h"
+#endif
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// JSON Helpers
+///////////////////////////////////////////////////////////////////////////////
+
+// Robust conversion operators, better than the ones in JsonCpp.
+bool GetIntFromJson(const Json::Value& in, int* out);
+bool GetUIntFromJson(const Json::Value& in, unsigned int* out);
+bool GetStringFromJson(const Json::Value& in, std::string* out);
+bool GetBoolFromJson(const Json::Value& in, bool* out);
+bool GetDoubleFromJson(const Json::Value& in, double* out);
+
+// Pull values out of a JSON array.
+bool GetValueFromJsonArray(const Json::Value& in, size_t n,
+                           Json::Value* out);
+bool GetIntFromJsonArray(const Json::Value& in, size_t n,
+                         int* out);
+bool GetUIntFromJsonArray(const Json::Value& in, size_t n,
+                          unsigned int* out);
+bool GetStringFromJsonArray(const Json::Value& in, size_t n,
+                            std::string* out);
+bool GetBoolFromJsonArray(const Json::Value& in, size_t n,
+                          bool* out);
+bool GetDoubleFromJsonArray(const Json::Value& in, size_t n,
+                            double* out);
+
+// Convert json arrays to std::vector
+bool JsonArrayToValueVector(const Json::Value& in,
+                            std::vector<Json::Value>* out);
+bool JsonArrayToIntVector(const Json::Value& in,
+                          std::vector<int>* out);
+bool JsonArrayToUIntVector(const Json::Value& in,
+                           std::vector<unsigned int>* out);
+bool JsonArrayToStringVector(const Json::Value& in,
+                             std::vector<std::string>* out);
+bool JsonArrayToBoolVector(const Json::Value& in,
+                           std::vector<bool>* out);
+bool JsonArrayToDoubleVector(const Json::Value& in,
+                             std::vector<double>* out);
+
+// Convert std::vector to json array
+Json::Value ValueVectorToJsonArray(const std::vector<Json::Value>& in);
+Json::Value IntVectorToJsonArray(const std::vector<int>& in);
+Json::Value UIntVectorToJsonArray(const std::vector<unsigned int>& in);
+Json::Value StringVectorToJsonArray(const std::vector<std::string>& in);
+Json::Value BoolVectorToJsonArray(const std::vector<bool>& in);
+Json::Value DoubleVectorToJsonArray(const std::vector<double>& in);
+
+// Pull values out of a JSON object.
+bool GetValueFromJsonObject(const Json::Value& in, const std::string& k,
+                            Json::Value* out);
+bool GetIntFromJsonObject(const Json::Value& in, const std::string& k,
+                          int* out);
+bool GetUIntFromJsonObject(const Json::Value& in, const std::string& k,
+                           unsigned int* out);
+bool GetStringFromJsonObject(const Json::Value& in, const std::string& k,
+                             std::string* out);
+bool GetBoolFromJsonObject(const Json::Value& in, const std::string& k,
+                           bool* out);
+bool GetDoubleFromJsonObject(const Json::Value& in, const std::string& k,
+                             double* out);
+
+// Writes out a Json value as a string.
+std::string JsonValueToString(const Json::Value& json);
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_JSON_H_
diff --git a/base/json_unittest.cc b/base/json_unittest.cc
new file mode 100644
index 0000000..fe3cf3b
--- /dev/null
+++ b/base/json_unittest.cc
@@ -0,0 +1,283 @@
+/*
+ *  Copyright 2009 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/json.h"
+
+#include <vector>
+
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+static Json::Value in_s("foo");
+static Json::Value in_sn("99");
+static Json::Value in_si("-99");
+static Json::Value in_sb("true");
+static Json::Value in_sd("1.2");
+static Json::Value in_n(12);
+static Json::Value in_i(-12);
+static Json::Value in_u(34U);
+static Json::Value in_b(true);
+static Json::Value in_d(1.2);
+static Json::Value big_sn("12345678901234567890");
+static Json::Value big_si("-12345678901234567890");
+static Json::Value big_u(0xFFFFFFFF);
+static Json::Value bad_a(Json::arrayValue);
+static Json::Value bad_o(Json::objectValue);
+
+TEST(JsonTest, GetString) {
+  std::string out;
+  EXPECT_TRUE(GetStringFromJson(in_s, &out));
+  EXPECT_EQ("foo", out);
+  EXPECT_TRUE(GetStringFromJson(in_sn, &out));
+  EXPECT_EQ("99", out);
+  EXPECT_TRUE(GetStringFromJson(in_si, &out));
+  EXPECT_EQ("-99", out);
+  EXPECT_TRUE(GetStringFromJson(in_i, &out));
+  EXPECT_EQ("-12", out);
+  EXPECT_TRUE(GetStringFromJson(in_n, &out));
+  EXPECT_EQ("12", out);
+  EXPECT_TRUE(GetStringFromJson(in_u, &out));
+  EXPECT_EQ("34", out);
+  EXPECT_TRUE(GetStringFromJson(in_b, &out));
+  EXPECT_EQ("true", out);
+  // Not supported here yet.
+  EXPECT_FALSE(GetStringFromJson(bad_a, &out));
+  EXPECT_FALSE(GetStringFromJson(bad_o, &out));
+}
+
+TEST(JsonTest, GetInt) {
+  int out;
+  EXPECT_TRUE(GetIntFromJson(in_sn, &out));
+  EXPECT_EQ(99, out);
+  EXPECT_TRUE(GetIntFromJson(in_si, &out));
+  EXPECT_EQ(-99, out);
+  EXPECT_TRUE(GetIntFromJson(in_n, &out));
+  EXPECT_EQ(12, out);
+  EXPECT_TRUE(GetIntFromJson(in_i, &out));
+  EXPECT_EQ(-12, out);
+  EXPECT_TRUE(GetIntFromJson(in_u, &out));
+  EXPECT_EQ(34, out);
+  EXPECT_TRUE(GetIntFromJson(in_b, &out));
+  EXPECT_EQ(1, out);
+  EXPECT_FALSE(GetIntFromJson(in_s, &out));
+  EXPECT_FALSE(GetIntFromJson(big_sn, &out));
+  EXPECT_FALSE(GetIntFromJson(big_si, &out));
+  EXPECT_FALSE(GetIntFromJson(big_u, &out));
+  EXPECT_FALSE(GetIntFromJson(bad_a, &out));
+  EXPECT_FALSE(GetIntFromJson(bad_o, &out));
+}
+
+TEST(JsonTest, GetUInt) {
+  unsigned int out;
+  EXPECT_TRUE(GetUIntFromJson(in_sn, &out));
+  EXPECT_EQ(99U, out);
+  EXPECT_TRUE(GetUIntFromJson(in_n, &out));
+  EXPECT_EQ(12U, out);
+  EXPECT_TRUE(GetUIntFromJson(in_u, &out));
+  EXPECT_EQ(34U, out);
+  EXPECT_TRUE(GetUIntFromJson(in_b, &out));
+  EXPECT_EQ(1U, out);
+  EXPECT_TRUE(GetUIntFromJson(big_u, &out));
+  EXPECT_EQ(0xFFFFFFFFU, out);
+  EXPECT_FALSE(GetUIntFromJson(in_s, &out));
+  // TODO: Fail reading negative strings.
+  // EXPECT_FALSE(GetUIntFromJson(in_si, &out));
+  EXPECT_FALSE(GetUIntFromJson(in_i, &out));
+  EXPECT_FALSE(GetUIntFromJson(big_sn, &out));
+  EXPECT_FALSE(GetUIntFromJson(big_si, &out));
+  EXPECT_FALSE(GetUIntFromJson(bad_a, &out));
+  EXPECT_FALSE(GetUIntFromJson(bad_o, &out));
+}
+
+TEST(JsonTest, GetBool) {
+  bool out;
+  EXPECT_TRUE(GetBoolFromJson(in_sb, &out));
+  EXPECT_EQ(true, out);
+  EXPECT_TRUE(GetBoolFromJson(in_n, &out));
+  EXPECT_EQ(true, out);
+  EXPECT_TRUE(GetBoolFromJson(in_i, &out));
+  EXPECT_EQ(true, out);
+  EXPECT_TRUE(GetBoolFromJson(in_u, &out));
+  EXPECT_EQ(true, out);
+  EXPECT_TRUE(GetBoolFromJson(in_b, &out));
+  EXPECT_EQ(true, out);
+  EXPECT_TRUE(GetBoolFromJson(big_u, &out));
+  EXPECT_EQ(true, out);
+  EXPECT_FALSE(GetBoolFromJson(in_s, &out));
+  EXPECT_FALSE(GetBoolFromJson(in_sn, &out));
+  EXPECT_FALSE(GetBoolFromJson(in_si, &out));
+  EXPECT_FALSE(GetBoolFromJson(big_sn, &out));
+  EXPECT_FALSE(GetBoolFromJson(big_si, &out));
+  EXPECT_FALSE(GetBoolFromJson(bad_a, &out));
+  EXPECT_FALSE(GetBoolFromJson(bad_o, &out));
+}
+
+TEST(JsonTest, GetDouble) {
+  double out;
+  EXPECT_TRUE(GetDoubleFromJson(in_sn, &out));
+  EXPECT_EQ(99, out);
+  EXPECT_TRUE(GetDoubleFromJson(in_si, &out));
+  EXPECT_EQ(-99, out);
+  EXPECT_TRUE(GetDoubleFromJson(in_sd, &out));
+  EXPECT_EQ(1.2, out);
+  EXPECT_TRUE(GetDoubleFromJson(in_n, &out));
+  EXPECT_EQ(12, out);
+  EXPECT_TRUE(GetDoubleFromJson(in_i, &out));
+  EXPECT_EQ(-12, out);
+  EXPECT_TRUE(GetDoubleFromJson(in_u, &out));
+  EXPECT_EQ(34, out);
+  EXPECT_TRUE(GetDoubleFromJson(in_b, &out));
+  EXPECT_EQ(1, out);
+  EXPECT_TRUE(GetDoubleFromJson(in_d, &out));
+  EXPECT_EQ(1.2, out);
+  EXPECT_FALSE(GetDoubleFromJson(in_s, &out));
+}
+
+TEST(JsonTest, GetFromArray) {
+  Json::Value a, out;
+  a.append(in_s);
+  a.append(in_i);
+  a.append(in_u);
+  a.append(in_b);
+  EXPECT_TRUE(GetValueFromJsonArray(a, 0, &out));
+  EXPECT_TRUE(GetValueFromJsonArray(a, 3, &out));
+  EXPECT_FALSE(GetValueFromJsonArray(a, 99, &out));
+  EXPECT_FALSE(GetValueFromJsonArray(a, 0xFFFFFFFF, &out));
+}
+
+TEST(JsonTest, GetFromObject) {
+  Json::Value o, out;
+  o["string"] = in_s;
+  o["int"] = in_i;
+  o["uint"] = in_u;
+  o["bool"] = in_b;
+  EXPECT_TRUE(GetValueFromJsonObject(o, "int", &out));
+  EXPECT_TRUE(GetValueFromJsonObject(o, "bool", &out));
+  EXPECT_FALSE(GetValueFromJsonObject(o, "foo", &out));
+  EXPECT_FALSE(GetValueFromJsonObject(o, "", &out));
+}
+
+namespace {
+template <typename T>
+std::vector<T> VecOf3(const T& a, const T& b, const T& c) {
+  std::vector<T> in;
+  in.push_back(a);
+  in.push_back(b);
+  in.push_back(c);
+  return in;
+}
+template <typename T>
+Json::Value JsonVecOf3(const T& a, const T& b, const T& c) {
+  Json::Value in(Json::arrayValue);
+  in.append(a);
+  in.append(b);
+  in.append(c);
+  return in;
+}
+}  // unnamed namespace
+
+TEST(JsonTest, ValueVectorToFromArray) {
+  std::vector<Json::Value> in = VecOf3<Json::Value>("a", "b", "c");
+  Json::Value out = ValueVectorToJsonArray(in);
+  EXPECT_EQ(in.size(), out.size());
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) {
+    EXPECT_EQ(in[i].asString(), out[i].asString());
+  }
+  Json::Value inj = JsonVecOf3<Json::Value>("a", "b", "c");
+  EXPECT_EQ(inj, out);
+  std::vector<Json::Value> outj;
+  EXPECT_TRUE(JsonArrayToValueVector(inj, &outj));
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) {
+    EXPECT_EQ(in[i], outj[i]);
+  }
+}
+
+TEST(JsonTest, IntVectorToFromArray) {
+  std::vector<int> in = VecOf3<int>(1, 2, 3);
+  Json::Value out = IntVectorToJsonArray(in);
+  EXPECT_EQ(in.size(), out.size());
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) {
+    EXPECT_EQ(in[i], out[i].asInt());
+  }
+  Json::Value inj = JsonVecOf3<int>(1, 2, 3);
+  EXPECT_EQ(inj, out);
+  std::vector<int> outj;
+  EXPECT_TRUE(JsonArrayToIntVector(inj, &outj));
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) {
+    EXPECT_EQ(in[i], outj[i]);
+  }
+}
+
+TEST(JsonTest, UIntVectorToFromArray) {
+  std::vector<unsigned int> in = VecOf3<unsigned int>(1, 2, 3);
+  Json::Value out = UIntVectorToJsonArray(in);
+  EXPECT_EQ(in.size(), out.size());
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) {
+    EXPECT_EQ(in[i], out[i].asUInt());
+  }
+  Json::Value inj = JsonVecOf3<unsigned int>(1, 2, 3);
+  EXPECT_EQ(inj, out);
+  std::vector<unsigned int> outj;
+  EXPECT_TRUE(JsonArrayToUIntVector(inj, &outj));
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) {
+    EXPECT_EQ(in[i], outj[i]);
+  }
+}
+
+TEST(JsonTest, StringVectorToFromArray) {
+  std::vector<std::string> in = VecOf3<std::string>("a", "b", "c");
+  Json::Value out = StringVectorToJsonArray(in);
+  EXPECT_EQ(in.size(), out.size());
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) {
+    EXPECT_EQ(in[i], out[i].asString());
+  }
+  Json::Value inj = JsonVecOf3<std::string>("a", "b", "c");
+  EXPECT_EQ(inj, out);
+  std::vector<std::string> outj;
+  EXPECT_TRUE(JsonArrayToStringVector(inj, &outj));
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) {
+    EXPECT_EQ(in[i], outj[i]);
+  }
+}
+
+TEST(JsonTest, BoolVectorToFromArray) {
+  std::vector<bool> in = VecOf3<bool>(false, true, false);
+  Json::Value out = BoolVectorToJsonArray(in);
+  EXPECT_EQ(in.size(), out.size());
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) {
+    EXPECT_EQ(in[i], out[i].asBool());
+  }
+  Json::Value inj = JsonVecOf3<bool>(false, true, false);
+  EXPECT_EQ(inj, out);
+  std::vector<bool> outj;
+  EXPECT_TRUE(JsonArrayToBoolVector(inj, &outj));
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) {
+    EXPECT_EQ(in[i], outj[i]);
+  }
+}
+
+TEST(JsonTest, DoubleVectorToFromArray) {
+  std::vector<double> in = VecOf3<double>(1.0, 2.0, 3.0);
+  Json::Value out = DoubleVectorToJsonArray(in);
+  EXPECT_EQ(in.size(), out.size());
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); ++i) {
+    EXPECT_EQ(in[i], out[i].asDouble());
+  }
+  Json::Value inj = JsonVecOf3<double>(1.0, 2.0, 3.0);
+  EXPECT_EQ(inj, out);
+  std::vector<double> outj;
+  EXPECT_TRUE(JsonArrayToDoubleVector(inj, &outj));
+  for (Json::Value::ArrayIndex i = 0; i < in.size(); i++) {
+    EXPECT_EQ(in[i], outj[i]);
+  }
+}
+
+}  // namespace rtc
diff --git a/base/keep_ref_until_done.h b/base/keep_ref_until_done.h
index 171e048..269e1c8 100644
--- a/base/keep_ref_until_done.h
+++ b/base/keep_ref_until_done.h
@@ -11,9 +11,33 @@
 #ifndef WEBRTC_BASE_KEEP_REF_UNTIL_DONE_H_
 #define WEBRTC_BASE_KEEP_REF_UNTIL_DONE_H_
 
+#include "webrtc/base/bind.h"
+#include "webrtc/base/callback.h"
+#include "webrtc/base/refcount.h"
+#include "webrtc/base/scoped_ref_ptr.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/keep_ref_until_done.h"
+namespace rtc {
+
+namespace impl {
+template <class T>
+static inline void DoNothing(const scoped_refptr<T>& object) {}
+}  // namespace impl
+
+// KeepRefUntilDone keeps a reference to |object| until the returned
+// callback goes out of scope. If the returned callback is copied, the
+// reference will be released when the last callback goes out of scope.
+template <class ObjectT>
+static inline Callback0<void> KeepRefUntilDone(ObjectT* object) {
+  return rtc::Bind(&impl::DoNothing<ObjectT>, scoped_refptr<ObjectT>(object));
+}
+
+template <class ObjectT>
+static inline Callback0<void> KeepRefUntilDone(
+    const scoped_refptr<ObjectT>& object) {
+  return rtc::Bind(&impl::DoNothing<ObjectT>, object);
+}
+
+}  // namespace rtc
+
 
 #endif  // WEBRTC_BASE_KEEP_REF_UNTIL_DONE_H_
diff --git a/base/location.cc b/base/location.cc
new file mode 100644
index 0000000..c3dfe77
--- /dev/null
+++ b/base/location.cc
@@ -0,0 +1,38 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/location.h"
+
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+
+Location::Location(const char* function_name, const char* file_and_line)
+    : function_name_(function_name), file_and_line_(file_and_line) {}
+
+Location::Location() : function_name_("Unknown"), file_and_line_("Unknown") {}
+
+Location::Location(const Location& other)
+    : function_name_(other.function_name_),
+      file_and_line_(other.file_and_line_) {}
+
+Location& Location::operator=(const Location& other) {
+  function_name_ = other.function_name_;
+  file_and_line_ = other.file_and_line_;
+  return *this;
+}
+
+std::string Location::ToString() const {
+  char buf[256];
+  sprintfn(buf, sizeof(buf), "%s@%s", function_name_, file_and_line_);
+  return buf;
+}
+
+}  // namespace rtc
diff --git a/base/location.h b/base/location.h
index 432471c..541be9a 100644
--- a/base/location.h
+++ b/base/location.h
@@ -11,9 +11,47 @@
 #ifndef WEBRTC_BASE_LOCATION_H_
 #define WEBRTC_BASE_LOCATION_H_
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/location.h"
+#include "webrtc/base/stringize_macros.h"
+
+namespace rtc {
+
+// Location provides basic info where of an object was constructed, or was
+// significantly brought to life.
+// This is a stripped down version of:
+// https://code.google.com/p/chromium/codesearch#chromium/src/base/location.h
+class Location {
+ public:
+  // Constructor should be called with a long-lived char*, such as __FILE__.
+  // It assumes the provided value will persist as a global constant, and it
+  // will not make a copy of it.
+  //
+  // TODO(deadbeef): Tracing is currently limited to 2 arguments, which is
+  // why the file name and line number are combined into one argument.
+  //
+  // Once TracingV2 is available, separate the file name and line number.
+  Location(const char* function_name, const char* file_and_line);
+  Location();
+  Location(const Location& other);
+  Location& operator=(const Location& other);
+
+  const char* function_name() const { return function_name_; }
+  const char* file_and_line() const { return file_and_line_; }
+
+  std::string ToString() const;
+
+ private:
+  const char* function_name_;
+  const char* file_and_line_;
+};
+
+// Define a macro to record the current source location.
+#define RTC_FROM_HERE RTC_FROM_HERE_WITH_FUNCTION(__FUNCTION__)
+
+#define RTC_FROM_HERE_WITH_FUNCTION(function_name) \
+  ::rtc::Location(function_name, __FILE__ ":" STRINGIZE(__LINE__))
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_LOCATION_H_
diff --git a/base/logging.cc b/base/logging.cc
new file mode 100644
index 0000000..25f85c9
--- /dev/null
+++ b/base/logging.cc
@@ -0,0 +1,561 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#if defined(WEBRTC_WIN)
+#if !defined(WIN32_LEAN_AND_MEAN)
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#if _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+#undef ERROR  // wingdi.h
+#endif
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+#include <CoreServices/CoreServices.h>
+#elif defined(WEBRTC_ANDROID)
+#include <android/log.h>
+// Android has a 1024 limit on log inputs. We use 60 chars as an
+// approx for the header/tag portion.
+// See android/system/core/liblog/logd_write.c
+static const int kMaxLogLineSize = 1024 - 60;
+#endif  // WEBRTC_MAC && !defined(WEBRTC_IOS) || WEBRTC_ANDROID
+
+static const char kLibjingle[] = "libjingle";
+
+#include <time.h>
+#include <limits.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <ostream>
+#include <vector>
+
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+namespace {
+
+// Return the filename portion of the string (that following the last slash).
+const char* FilenameFromPath(const char* file) {
+  const char* end1 = ::strrchr(file, '/');
+  const char* end2 = ::strrchr(file, '\\');
+  if (!end1 && !end2)
+    return file;
+  else
+    return (end1 > end2) ? end1 + 1 : end2 + 1;
+}
+
+}  // namespace
+
+/////////////////////////////////////////////////////////////////////////////
+// Constant Labels
+/////////////////////////////////////////////////////////////////////////////
+
+const char* FindLabel(int value, const ConstantLabel entries[]) {
+  for (int i = 0; entries[i].label; ++i) {
+    if (value == entries[i].value) {
+      return entries[i].label;
+    }
+  }
+  return 0;
+}
+
+std::string ErrorName(int err, const ConstantLabel* err_table) {
+  if (err == 0)
+    return "No error";
+
+  if (err_table != 0) {
+    if (const char* value = FindLabel(err, err_table))
+      return value;
+  }
+
+  char buffer[16];
+  snprintf(buffer, sizeof(buffer), "0x%08x", err);
+  return buffer;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// LogMessage
+/////////////////////////////////////////////////////////////////////////////
+
+// By default, release builds don't log, debug builds at info level
+#if !defined(NDEBUG)
+LoggingSeverity LogMessage::min_sev_ = LS_INFO;
+LoggingSeverity LogMessage::dbg_sev_ = LS_INFO;
+#else
+LoggingSeverity LogMessage::min_sev_ = LS_NONE;
+LoggingSeverity LogMessage::dbg_sev_ = LS_NONE;
+#endif
+bool LogMessage::log_to_stderr_ = true;
+
+namespace {
+// Global lock for log subsystem, only needed to serialize access to streams_.
+CriticalSection g_log_crit;
+}  // namespace
+
+// The list of logging streams currently configured.
+// Note: we explicitly do not clean this up, because of the uncertain ordering
+// of destructors at program exit.  Let the person who sets the stream trigger
+// cleanup by setting to null, or let it leak (safe at program exit).
+LogMessage::StreamList LogMessage::streams_ GUARDED_BY(g_log_crit);
+
+// Boolean options default to false (0)
+bool LogMessage::thread_, LogMessage::timestamp_;
+
+LogMessage::LogMessage(const char* file,
+                       int line,
+                       LoggingSeverity sev,
+                       LogErrorContext err_ctx,
+                       int err,
+                       const char* module)
+    : severity_(sev), tag_(kLibjingle) {
+  if (timestamp_) {
+    // Use SystemTimeMillis so that even if tests use fake clocks, the timestamp
+    // in log messages represents the real system time.
+    int64_t time = TimeDiff(SystemTimeMillis(), LogStartTime());
+    // Also ensure WallClockStartTime is initialized, so that it matches
+    // LogStartTime.
+    WallClockStartTime();
+    print_stream_ << "[" << std::setfill('0') << std::setw(3) << (time / 1000)
+                  << ":" << std::setw(3) << (time % 1000) << std::setfill(' ')
+                  << "] ";
+  }
+
+  if (thread_) {
+    PlatformThreadId id = CurrentThreadId();
+    print_stream_ << "[" << std::dec << id << "] ";
+  }
+
+  if (file != nullptr)
+    print_stream_ << "(" << FilenameFromPath(file)  << ":" << line << "): ";
+
+  if (err_ctx != ERRCTX_NONE) {
+    std::ostringstream tmp;
+    tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]";
+    switch (err_ctx) {
+      case ERRCTX_ERRNO:
+        tmp << " " << strerror(err);
+        break;
+#ifdef WEBRTC_WIN
+      case ERRCTX_HRESULT: {
+        char msgbuf[256];
+        DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM;
+        HMODULE hmod = GetModuleHandleA(module);
+        if (hmod)
+          flags |= FORMAT_MESSAGE_FROM_HMODULE;
+        if (DWORD len = FormatMessageA(
+                flags, hmod, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), nullptr)) {
+          while ((len > 0) &&
+              isspace(static_cast<unsigned char>(msgbuf[len-1]))) {
+            msgbuf[--len] = 0;
+          }
+          tmp << " " << msgbuf;
+        }
+        break;
+      }
+#endif  // WEBRTC_WIN
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+      case ERRCTX_OSSTATUS: {
+        std::string desc(DescriptionFromOSStatus(err));
+        tmp << " " << (desc.empty() ? "Unknown error" : desc.c_str());
+        break;
+      }
+#endif  // WEBRTC_MAC && !defined(WEBRTC_IOS)
+      default:
+        break;
+    }
+    extra_ = tmp.str();
+  }
+}
+
+LogMessage::LogMessage(const char* file,
+                       int line,
+                       LoggingSeverity sev,
+                       const std::string& tag)
+    : LogMessage(file,
+                 line,
+                 sev,
+                 ERRCTX_NONE,
+                 0 /* err */,
+                 nullptr /* module */) {
+  tag_ = tag;
+  print_stream_ << tag << ": ";
+}
+
+LogMessage::~LogMessage() {
+  if (!extra_.empty())
+    print_stream_ << " : " << extra_;
+  print_stream_ << std::endl;
+
+  const std::string& str = print_stream_.str();
+  if (severity_ >= dbg_sev_) {
+    OutputToDebug(str, severity_, tag_);
+  }
+
+  CritScope cs(&g_log_crit);
+  for (auto& kv : streams_) {
+    if (severity_ >= kv.second) {
+      kv.first->OnLogMessage(str);
+    }
+  }
+}
+
+int64_t LogMessage::LogStartTime() {
+  static const int64_t g_start = SystemTimeMillis();
+  return g_start;
+}
+
+uint32_t LogMessage::WallClockStartTime() {
+  static const uint32_t g_start_wallclock = time(nullptr);
+  return g_start_wallclock;
+}
+
+void LogMessage::LogThreads(bool on) {
+  thread_ = on;
+}
+
+void LogMessage::LogTimestamps(bool on) {
+  timestamp_ = on;
+}
+
+void LogMessage::LogToDebug(LoggingSeverity min_sev) {
+  dbg_sev_ = min_sev;
+  CritScope cs(&g_log_crit);
+  UpdateMinLogSeverity();
+}
+
+void LogMessage::SetLogToStderr(bool log_to_stderr) {
+  log_to_stderr_ = log_to_stderr;
+}
+
+int LogMessage::GetLogToStream(LogSink* stream) {
+  CritScope cs(&g_log_crit);
+  LoggingSeverity sev = LS_NONE;
+  for (auto& kv : streams_) {
+    if (!stream || stream == kv.first) {
+      sev = std::min(sev, kv.second);
+    }
+  }
+  return sev;
+}
+
+void LogMessage::AddLogToStream(LogSink* stream, LoggingSeverity min_sev) {
+  CritScope cs(&g_log_crit);
+  streams_.push_back(std::make_pair(stream, min_sev));
+  UpdateMinLogSeverity();
+}
+
+void LogMessage::RemoveLogToStream(LogSink* stream) {
+  CritScope cs(&g_log_crit);
+  for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
+    if (stream == it->first) {
+      streams_.erase(it);
+      break;
+    }
+  }
+  UpdateMinLogSeverity();
+}
+
+void LogMessage::ConfigureLogging(const char* params) {
+  LoggingSeverity current_level = LS_VERBOSE;
+  LoggingSeverity debug_level = GetLogToDebug();
+
+  std::vector<std::string> tokens;
+  tokenize(params, ' ', &tokens);
+
+  for (const std::string& token : tokens) {
+    if (token.empty())
+      continue;
+
+    // Logging features
+    if (token == "tstamp") {
+      LogTimestamps();
+    } else if (token == "thread") {
+      LogThreads();
+
+    // Logging levels
+    } else if (token == "sensitive") {
+      current_level = LS_SENSITIVE;
+    } else if (token == "verbose") {
+      current_level = LS_VERBOSE;
+    } else if (token == "info") {
+      current_level = LS_INFO;
+    } else if (token == "warning") {
+      current_level = LS_WARNING;
+    } else if (token == "error") {
+      current_level = LS_ERROR;
+    } else if (token == "none") {
+      current_level = LS_NONE;
+
+    // Logging targets
+    } else if (token == "debug") {
+      debug_level = current_level;
+    }
+  }
+
+#if defined(WEBRTC_WIN)
+  if ((LS_NONE != debug_level) && !::IsDebuggerPresent()) {
+    // First, attempt to attach to our parent's console... so if you invoke
+    // from the command line, we'll see the output there.  Otherwise, create
+    // our own console window.
+    // Note: These methods fail if a console already exists, which is fine.
+    bool success = false;
+    typedef BOOL (WINAPI* PFN_AttachConsole)(DWORD);
+    if (HINSTANCE kernel32 = ::LoadLibrary(L"kernel32.dll")) {
+      // AttachConsole is defined on WinXP+.
+      if (PFN_AttachConsole attach_console = reinterpret_cast<PFN_AttachConsole>
+            (::GetProcAddress(kernel32, "AttachConsole"))) {
+        success = (FALSE != attach_console(ATTACH_PARENT_PROCESS));
+      }
+      ::FreeLibrary(kernel32);
+    }
+    if (!success) {
+      ::AllocConsole();
+    }
+  }
+#endif  // WEBRTC_WIN
+
+  LogToDebug(debug_level);
+}
+
+void LogMessage::UpdateMinLogSeverity() EXCLUSIVE_LOCKS_REQUIRED(g_log_crit) {
+  LoggingSeverity min_sev = dbg_sev_;
+  for (auto& kv : streams_) {
+    min_sev = std::min(dbg_sev_, kv.second);
+  }
+  min_sev_ = min_sev;
+}
+
+void LogMessage::OutputToDebug(const std::string& str,
+                               LoggingSeverity severity,
+                               const std::string& tag) {
+  bool log_to_stderr = log_to_stderr_;
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG)
+  // On the Mac, all stderr output goes to the Console log and causes clutter.
+  // So in opt builds, don't log to stderr unless the user specifically sets
+  // a preference to do so.
+  CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault,
+                                              "logToStdErr",
+                                              kCFStringEncodingUTF8);
+  CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle());
+  if (key != nullptr && domain != nullptr) {
+    Boolean exists_and_is_valid;
+    Boolean should_log =
+        CFPreferencesGetAppBooleanValue(key, domain, &exists_and_is_valid);
+    // If the key doesn't exist or is invalid or is false, we will not log to
+    // stderr.
+    log_to_stderr = exists_and_is_valid && should_log;
+  }
+  if (key != nullptr) {
+    CFRelease(key);
+  }
+#endif
+#if defined(WEBRTC_WIN)
+  // Always log to the debugger.
+  // Perhaps stderr should be controlled by a preference, as on Mac?
+  OutputDebugStringA(str.c_str());
+  if (log_to_stderr) {
+    // This handles dynamically allocated consoles, too.
+    if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) {
+      log_to_stderr = false;
+      DWORD written = 0;
+      ::WriteFile(error_handle, str.data(), static_cast<DWORD>(str.size()),
+                  &written, 0);
+    }
+  }
+#endif  // WEBRTC_WIN
+#if defined(WEBRTC_ANDROID)
+  // Android's logging facility uses severity to log messages but we
+  // need to map libjingle's severity levels to Android ones first.
+  // Also write to stderr which maybe available to executable started
+  // from the shell.
+  int prio;
+  switch (severity) {
+    case LS_SENSITIVE:
+      __android_log_write(ANDROID_LOG_INFO, tag.c_str(), "SENSITIVE");
+      if (log_to_stderr) {
+        fprintf(stderr, "SENSITIVE");
+        fflush(stderr);
+      }
+      return;
+    case LS_VERBOSE:
+      prio = ANDROID_LOG_VERBOSE;
+      break;
+    case LS_INFO:
+      prio = ANDROID_LOG_INFO;
+      break;
+    case LS_WARNING:
+      prio = ANDROID_LOG_WARN;
+      break;
+    case LS_ERROR:
+      prio = ANDROID_LOG_ERROR;
+      break;
+    default:
+      prio = ANDROID_LOG_UNKNOWN;
+  }
+
+  int size = str.size();
+  int line = 0;
+  int idx = 0;
+  const int max_lines = size / kMaxLogLineSize + 1;
+  if (max_lines == 1) {
+    __android_log_print(prio, tag.c_str(), "%.*s", size, str.c_str());
+  } else {
+    while (size > 0) {
+      const int len = std::min(size, kMaxLogLineSize);
+      // Use the size of the string in the format (str may have \0 in the
+      // middle).
+      __android_log_print(prio, tag.c_str(), "[%d/%d] %.*s",
+                          line + 1, max_lines,
+                          len, str.c_str() + idx);
+      idx += len;
+      size -= len;
+      ++line;
+    }
+  }
+#endif  // WEBRTC_ANDROID
+  if (log_to_stderr) {
+    fprintf(stderr, "%s", str.c_str());
+    fflush(stderr);
+  }
+}
+
+//////////////////////////////////////////////////////////////////////
+// Logging Helpers
+//////////////////////////////////////////////////////////////////////
+
+void LogMultiline(LoggingSeverity level, const char* label, bool input,
+                  const void* data, size_t len, bool hex_mode,
+                  LogMultilineState* state) {
+  if (!LOG_CHECK_LEVEL_V(level))
+    return;
+
+  const char * direction = (input ? " << " : " >> ");
+
+  // null data means to flush our count of unprintable characters.
+  if (!data) {
+    if (state && state->unprintable_count_[input]) {
+      LOG_V(level) << label << direction << "## "
+                   << state->unprintable_count_[input]
+                   << " consecutive unprintable ##";
+      state->unprintable_count_[input] = 0;
+    }
+    return;
+  }
+
+  // The ctype classification functions want unsigned chars.
+  const unsigned char* udata = static_cast<const unsigned char*>(data);
+
+  if (hex_mode) {
+    const size_t LINE_SIZE = 24;
+    char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1];
+    while (len > 0) {
+      memset(asc_line, ' ', sizeof(asc_line));
+      memset(hex_line, ' ', sizeof(hex_line));
+      size_t line_len = std::min(len, LINE_SIZE);
+      for (size_t i = 0; i < line_len; ++i) {
+        unsigned char ch = udata[i];
+        asc_line[i] = isprint(ch) ? ch : '.';
+        hex_line[i*2 + i/4] = hex_encode(ch >> 4);
+        hex_line[i*2 + i/4 + 1] = hex_encode(ch & 0xf);
+      }
+      asc_line[sizeof(asc_line)-1] = 0;
+      hex_line[sizeof(hex_line)-1] = 0;
+      LOG_V(level) << label << direction
+                   << asc_line << " " << hex_line << " ";
+      udata += line_len;
+      len -= line_len;
+    }
+    return;
+  }
+
+  size_t consecutive_unprintable = state ? state->unprintable_count_[input] : 0;
+
+  const unsigned char* end = udata + len;
+  while (udata < end) {
+    const unsigned char* line = udata;
+    const unsigned char* end_of_line = strchrn<unsigned char>(udata,
+                                                              end - udata,
+                                                              '\n');
+    if (!end_of_line) {
+      udata = end_of_line = end;
+    } else {
+      udata = end_of_line + 1;
+    }
+
+    bool is_printable = true;
+
+    // If we are in unprintable mode, we need to see a line of at least
+    // kMinPrintableLine characters before we'll switch back.
+    const ptrdiff_t kMinPrintableLine = 4;
+    if (consecutive_unprintable && ((end_of_line - line) < kMinPrintableLine)) {
+      is_printable = false;
+    } else {
+      // Determine if the line contains only whitespace and printable
+      // characters.
+      bool is_entirely_whitespace = true;
+      for (const unsigned char* pos = line; pos < end_of_line; ++pos) {
+        if (isspace(*pos))
+          continue;
+        is_entirely_whitespace = false;
+        if (!isprint(*pos)) {
+          is_printable = false;
+          break;
+        }
+      }
+      // Treat an empty line following unprintable data as unprintable.
+      if (consecutive_unprintable && is_entirely_whitespace) {
+        is_printable = false;
+      }
+    }
+    if (!is_printable) {
+      consecutive_unprintable += (udata - line);
+      continue;
+    }
+    // Print out the current line, but prefix with a count of prior unprintable
+    // characters.
+    if (consecutive_unprintable) {
+      LOG_V(level) << label << direction << "## " << consecutive_unprintable
+                  << " consecutive unprintable ##";
+      consecutive_unprintable = 0;
+    }
+    // Strip off trailing whitespace.
+    while ((end_of_line > line) && isspace(*(end_of_line-1))) {
+      --end_of_line;
+    }
+    // Filter out any private data
+    std::string substr(reinterpret_cast<const char*>(line), end_of_line - line);
+    std::string::size_type pos_private = substr.find("Email");
+    if (pos_private == std::string::npos) {
+      pos_private = substr.find("Passwd");
+    }
+    if (pos_private == std::string::npos) {
+      LOG_V(level) << label << direction << substr;
+    } else {
+      LOG_V(level) << label << direction << "## omitted for privacy ##";
+    }
+  }
+
+  if (state) {
+    state->unprintable_count_[input] = consecutive_unprintable;
+  }
+}
+
+//////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
diff --git a/base/logging.h b/base/logging.h
index 594d9c9..8f476a0 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -46,9 +46,325 @@
 #ifndef WEBRTC_BASE_LOGGING_H_
 #define WEBRTC_BASE_LOGGING_H_
 
+#include <errno.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/logging.h"
+#include <list>
+#include <sstream>
+#include <string>
+#include <utility>
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+#include <CoreServices/CoreServices.h>
+#endif
+
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/thread_annotations.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// ConstantLabel can be used to easily generate string names from constant
+// values.  This can be useful for logging descriptive names of error messages.
+// Usage:
+//   const ConstantLabel LIBRARY_ERRORS[] = {
+//     KLABEL(SOME_ERROR),
+//     KLABEL(SOME_OTHER_ERROR),
+//     ...
+//     LASTLABEL
+//   }
+//
+//   int err = LibraryFunc();
+//   LOG(LS_ERROR) << "LibraryFunc returned: "
+//                 << ErrorName(err, LIBRARY_ERRORS);
+
+struct ConstantLabel { int value; const char * label; };
+#define KLABEL(x) { x, #x }
+#define TLABEL(x, y) { x, y }
+#define LASTLABEL { 0, 0 }
+
+const char* FindLabel(int value, const ConstantLabel entries[]);
+std::string ErrorName(int err, const ConstantLabel* err_table);
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+// Returns a UTF8 description from an OS X Status error.
+std::string DescriptionFromOSStatus(OSStatus err);
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+// Note that the non-standard LoggingSeverity aliases exist because they are
+// still in broad use.  The meanings of the levels are:
+//  LS_SENSITIVE: Information which should only be logged with the consent
+//   of the user, due to privacy concerns.
+//  LS_VERBOSE: This level is for data which we do not want to appear in the
+//   normal debug log, but should appear in diagnostic logs.
+//  LS_INFO: Chatty level used in debugging for all sorts of things, the default
+//   in debug builds.
+//  LS_WARNING: Something that may warrant investigation.
+//  LS_ERROR: Something that should not have occurred.
+//  LS_NONE: Don't log.
+enum LoggingSeverity {
+  LS_SENSITIVE,
+  LS_VERBOSE,
+  LS_INFO,
+  LS_WARNING,
+  LS_ERROR,
+  LS_NONE,
+  INFO = LS_INFO,
+  WARNING = LS_WARNING,
+  LERROR = LS_ERROR
+};
+
+// LogErrorContext assists in interpreting the meaning of an error value.
+enum LogErrorContext {
+  ERRCTX_NONE,
+  ERRCTX_ERRNO,     // System-local errno
+  ERRCTX_HRESULT,   // Windows HRESULT
+  ERRCTX_OSSTATUS,  // MacOS OSStatus
+
+  // Abbreviations for LOG_E macro
+  ERRCTX_EN = ERRCTX_ERRNO,     // LOG_E(sev, EN, x)
+  ERRCTX_HR = ERRCTX_HRESULT,   // LOG_E(sev, HR, x)
+  ERRCTX_OS = ERRCTX_OSSTATUS,  // LOG_E(sev, OS, x)
+};
+
+// Virtual sink interface that can receive log messages.
+class LogSink {
+ public:
+  LogSink() {}
+  virtual ~LogSink() {}
+  virtual void OnLogMessage(const std::string& message) = 0;
+};
+
+class LogMessage {
+ public:
+  LogMessage(const char* file,
+             int line,
+             LoggingSeverity sev,
+             LogErrorContext err_ctx = ERRCTX_NONE,
+             int err = 0,
+             const char* module = nullptr);
+
+  LogMessage(const char* file,
+             int line,
+             LoggingSeverity sev,
+             const std::string& tag);
+
+  ~LogMessage();
+
+  static inline bool Loggable(LoggingSeverity sev) { return (sev >= min_sev_); }
+  std::ostream& stream() { return print_stream_; }
+
+  // Returns the time at which this function was called for the first time.
+  // The time will be used as the logging start time.
+  // If this is not called externally, the LogMessage ctor also calls it, in
+  // which case the logging start time will be the time of the first LogMessage
+  // instance is created.
+  static int64_t LogStartTime();
+
+  // Returns the wall clock equivalent of |LogStartTime|, in seconds from the
+  // epoch.
+  static uint32_t WallClockStartTime();
+
+  //  LogThreads: Display the thread identifier of the current thread
+  static void LogThreads(bool on = true);
+
+  //  LogTimestamps: Display the elapsed time of the program
+  static void LogTimestamps(bool on = true);
+
+  // These are the available logging channels
+  //  Debug: Debug console on Windows, otherwise stderr
+  static void LogToDebug(LoggingSeverity min_sev);
+  static LoggingSeverity GetLogToDebug() { return dbg_sev_; }
+
+  // Sets whether logs will be directed to stderr in debug mode.
+  static void SetLogToStderr(bool log_to_stderr);
+
+  //  Stream: Any non-blocking stream interface.  LogMessage takes ownership of
+  //   the stream. Multiple streams may be specified by using AddLogToStream.
+  //   LogToStream is retained for backwards compatibility; when invoked, it
+  //   will discard any previously set streams and install the specified stream.
+  //   GetLogToStream gets the severity for the specified stream, of if none
+  //   is specified, the minimum stream severity.
+  //   RemoveLogToStream removes the specified stream, without destroying it.
+  static int GetLogToStream(LogSink* stream = nullptr);
+  static void AddLogToStream(LogSink* stream, LoggingSeverity min_sev);
+  static void RemoveLogToStream(LogSink* stream);
+
+  // Testing against MinLogSeverity allows code to avoid potentially expensive
+  // logging operations by pre-checking the logging level.
+  static int GetMinLogSeverity() { return min_sev_; }
+
+  // Parses the provided parameter stream to configure the options above.
+  // Useful for configuring logging from the command line.
+  static void ConfigureLogging(const char* params);
+
+ private:
+  typedef std::pair<LogSink*, LoggingSeverity> StreamAndSeverity;
+  typedef std::list<StreamAndSeverity> StreamList;
+
+  // Updates min_sev_ appropriately when debug sinks change.
+  static void UpdateMinLogSeverity();
+
+  // These write out the actual log messages.
+  static void OutputToDebug(const std::string& msg,
+                            LoggingSeverity severity,
+                            const std::string& tag);
+
+  // The ostream that buffers the formatted message before output
+  std::ostringstream print_stream_;
+
+  // The severity level of this message
+  LoggingSeverity severity_;
+
+  // The Android debug output tag.
+  std::string tag_;
+
+  // String data generated in the constructor, that should be appended to
+  // the message before output.
+  std::string extra_;
+
+  // dbg_sev_ is the thresholds for those output targets
+  // min_sev_ is the minimum (most verbose) of those levels, and is used
+  //  as a short-circuit in the logging macros to identify messages that won't
+  //  be logged.
+  // ctx_sev_ is the minimum level at which file context is displayed
+  static LoggingSeverity min_sev_, dbg_sev_, ctx_sev_;
+
+  // The output streams and their associated severities
+  static StreamList streams_;
+
+  // Flags for formatting options
+  static bool thread_, timestamp_;
+
+  // Determines if logs will be directed to stderr in debug mode.
+  static bool log_to_stderr_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+//////////////////////////////////////////////////////////////////////
+// Logging Helpers
+//////////////////////////////////////////////////////////////////////
+
+class LogMultilineState {
+ public:
+  size_t unprintable_count_[2];
+  LogMultilineState() {
+    unprintable_count_[0] = unprintable_count_[1] = 0;
+  }
+};
+
+// When possible, pass optional state variable to track various data across
+// multiple calls to LogMultiline.  Otherwise, pass null.
+void LogMultiline(LoggingSeverity level, const char* label, bool input,
+                  const void* data, size_t len, bool hex_mode,
+                  LogMultilineState* state);
+
+#ifndef LOG
+
+// The following non-obvious technique for implementation of a
+// conditional log stream was stolen from google3/base/logging.h.
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros.  This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+
+class LogMessageVoidify {
+ public:
+  LogMessageVoidify() { }
+  // This has to be an operator with a precedence lower than << but
+  // higher than ?:
+  void operator&(std::ostream&) { }
+};
+
+#define LOG_SEVERITY_PRECONDITION(sev) \
+  !(rtc::LogMessage::Loggable(sev)) \
+    ? (void) 0 \
+    : rtc::LogMessageVoidify() &
+
+#define LOG(sev) \
+  LOG_SEVERITY_PRECONDITION(rtc::sev) \
+    rtc::LogMessage(__FILE__, __LINE__, rtc::sev).stream()
+
+// The _V version is for when a variable is passed in.  It doesn't do the
+// namespace concatination.
+#define LOG_V(sev) \
+  LOG_SEVERITY_PRECONDITION(sev) \
+    rtc::LogMessage(__FILE__, __LINE__, sev).stream()
+
+// The _F version prefixes the message with the current function name.
+#if (defined(__GNUC__) && !defined(NDEBUG)) || defined(WANT_PRETTY_LOG_F)
+#define LOG_F(sev) LOG(sev) << __PRETTY_FUNCTION__ << ": "
+#define LOG_T_F(sev) LOG(sev) << this << ": " << __PRETTY_FUNCTION__ << ": "
+#else
+#define LOG_F(sev) LOG(sev) << __FUNCTION__ << ": "
+#define LOG_T_F(sev) LOG(sev) << this << ": " << __FUNCTION__ << ": "
+#endif
+
+#define LOG_CHECK_LEVEL(sev) \
+  rtc::LogCheckLevel(rtc::sev)
+#define LOG_CHECK_LEVEL_V(sev) \
+  rtc::LogCheckLevel(sev)
+
+inline bool LogCheckLevel(LoggingSeverity sev) {
+  return (LogMessage::GetMinLogSeverity() <= sev);
+}
+
+#define LOG_E(sev, ctx, err, ...) \
+  LOG_SEVERITY_PRECONDITION(rtc::sev) \
+    rtc::LogMessage(__FILE__, __LINE__, rtc::sev, \
+                          rtc::ERRCTX_ ## ctx, err , ##__VA_ARGS__) \
+        .stream()
+
+#define LOG_T(sev) LOG(sev) << this << ": "
+
+#define LOG_ERRNO_EX(sev, err) \
+  LOG_E(sev, ERRNO, err)
+#define LOG_ERRNO(sev) \
+  LOG_ERRNO_EX(sev, errno)
+
+#if defined(WEBRTC_WIN)
+#define LOG_GLE_EX(sev, err) \
+  LOG_E(sev, HRESULT, err)
+#define LOG_GLE(sev) \
+  LOG_GLE_EX(sev, GetLastError())
+#define LOG_GLEM(sev, mod) \
+  LOG_E(sev, HRESULT, GetLastError(), mod)
+#define LOG_ERR_EX(sev, err) \
+  LOG_GLE_EX(sev, err)
+#define LOG_ERR(sev) \
+  LOG_GLE(sev)
+#define LAST_SYSTEM_ERROR \
+  (::GetLastError())
+#elif defined(__native_client__) && __native_client__
+#define LOG_ERR_EX(sev, err) \
+  LOG(sev)
+#define LOG_ERR(sev) \
+  LOG(sev)
+#define LAST_SYSTEM_ERROR \
+  (0)
+#elif defined(WEBRTC_POSIX)
+#define LOG_ERR_EX(sev, err) \
+  LOG_ERRNO_EX(sev, err)
+#define LOG_ERR(sev) \
+  LOG_ERRNO(sev)
+#define LAST_SYSTEM_ERROR \
+  (errno)
+#endif  // WEBRTC_WIN
+
+#define LOG_TAG(sev, tag)        \
+  LOG_SEVERITY_PRECONDITION(sev) \
+  rtc::LogMessage(nullptr, 0, sev, tag).stream()
+
+#define PLOG(sev, err) \
+  LOG_ERR_EX(sev, err)
+
+// TODO(?): Add an "assert" wrapper that logs in the same manner.
+
+#endif  // LOG
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_LOGGING_H_
diff --git a/base/logging_mac.mm b/base/logging_mac.mm
new file mode 100644
index 0000000..ffee354
--- /dev/null
+++ b/base/logging_mac.mm
@@ -0,0 +1,22 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/logging.h"
+
+#import <Foundation/Foundation.h>
+
+
+namespace rtc {
+std::string DescriptionFromOSStatus(OSStatus err) {
+  NSError* error =
+      [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil];
+  return error.description.UTF8String;
+}
+}  // namespace rtc
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
new file mode 100644
index 0000000..3d9fc9e
--- /dev/null
+++ b/base/logging_unittest.cc
@@ -0,0 +1,162 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+template <typename Base>
+class LogSinkImpl
+    : public LogSink,
+      public Base {
+ public:
+  LogSinkImpl() {}
+
+  template<typename P>
+  explicit LogSinkImpl(P* p) : Base(p) {}
+
+ private:
+  void OnLogMessage(const std::string& message) override {
+    static_cast<Base*>(this)->WriteAll(
+        message.data(), message.size(), nullptr, nullptr);
+  }
+};
+
+// Test basic logging operation. We should get the INFO log but not the VERBOSE.
+// We should restore the correct global state at the end.
+TEST(LogTest, SingleStream) {
+  int sev = LogMessage::GetLogToStream(nullptr);
+
+  std::string str;
+  LogSinkImpl<StringStream> stream(&str);
+  LogMessage::AddLogToStream(&stream, LS_INFO);
+  EXPECT_EQ(LS_INFO, LogMessage::GetLogToStream(&stream));
+
+  LOG(LS_INFO) << "INFO";
+  LOG(LS_VERBOSE) << "VERBOSE";
+  EXPECT_NE(std::string::npos, str.find("INFO"));
+  EXPECT_EQ(std::string::npos, str.find("VERBOSE"));
+
+  LogMessage::RemoveLogToStream(&stream);
+  EXPECT_EQ(LS_NONE, LogMessage::GetLogToStream(&stream));
+
+  EXPECT_EQ(sev, LogMessage::GetLogToStream(nullptr));
+}
+
+// Test using multiple log streams. The INFO stream should get the INFO message,
+// the VERBOSE stream should get the INFO and the VERBOSE.
+// We should restore the correct global state at the end.
+TEST(LogTest, MultipleStreams) {
+  int sev = LogMessage::GetLogToStream(nullptr);
+
+  std::string str1, str2;
+  LogSinkImpl<StringStream> stream1(&str1), stream2(&str2);
+  LogMessage::AddLogToStream(&stream1, LS_INFO);
+  LogMessage::AddLogToStream(&stream2, LS_VERBOSE);
+  EXPECT_EQ(LS_INFO, LogMessage::GetLogToStream(&stream1));
+  EXPECT_EQ(LS_VERBOSE, LogMessage::GetLogToStream(&stream2));
+
+  LOG(LS_INFO) << "INFO";
+  LOG(LS_VERBOSE) << "VERBOSE";
+
+  EXPECT_NE(std::string::npos, str1.find("INFO"));
+  EXPECT_EQ(std::string::npos, str1.find("VERBOSE"));
+  EXPECT_NE(std::string::npos, str2.find("INFO"));
+  EXPECT_NE(std::string::npos, str2.find("VERBOSE"));
+
+  LogMessage::RemoveLogToStream(&stream2);
+  LogMessage::RemoveLogToStream(&stream1);
+  EXPECT_EQ(LS_NONE, LogMessage::GetLogToStream(&stream2));
+  EXPECT_EQ(LS_NONE, LogMessage::GetLogToStream(&stream1));
+
+  EXPECT_EQ(sev, LogMessage::GetLogToStream(nullptr));
+}
+
+// Ensure we don't crash when adding/removing streams while threads are going.
+// We should restore the correct global state at the end.
+class LogThread : public Thread {
+ public:
+  ~LogThread() override {
+    Stop();
+  }
+
+ private:
+  void Run() override {
+    // LS_SENSITIVE to avoid cluttering up any real logging going on
+    LOG(LS_SENSITIVE) << "LOG";
+  }
+};
+
+TEST(LogTest, MultipleThreads) {
+  int sev = LogMessage::GetLogToStream(nullptr);
+
+  LogThread thread1, thread2, thread3;
+  thread1.Start();
+  thread2.Start();
+  thread3.Start();
+
+  LogSinkImpl<NullStream> stream1, stream2, stream3;
+  for (int i = 0; i < 1000; ++i) {
+    LogMessage::AddLogToStream(&stream1, LS_INFO);
+    LogMessage::AddLogToStream(&stream2, LS_VERBOSE);
+    LogMessage::AddLogToStream(&stream3, LS_SENSITIVE);
+    LogMessage::RemoveLogToStream(&stream1);
+    LogMessage::RemoveLogToStream(&stream2);
+    LogMessage::RemoveLogToStream(&stream3);
+  }
+
+  EXPECT_EQ(sev, LogMessage::GetLogToStream(nullptr));
+}
+
+
+TEST(LogTest, WallClockStartTime) {
+  uint32_t time = LogMessage::WallClockStartTime();
+  // Expect the time to be in a sensible range, e.g. > 2012-01-01.
+  EXPECT_GT(time, 1325376000u);
+}
+
+// Test the time required to write 1000 80-character logs to an unbuffered file.
+#if defined (WEBRTC_ANDROID)
+// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
+#define MAYBE_Perf DISABLED_Perf
+#else
+#define MAYBE_Perf Perf
+#endif
+
+TEST(LogTest, MAYBE_Perf) {
+  Pathname path;
+  EXPECT_TRUE(Filesystem::GetTemporaryFolder(path, true, nullptr));
+  path.SetPathname(Filesystem::TempFilename(path, "ut"));
+
+  LogSinkImpl<FileStream> stream;
+  EXPECT_TRUE(stream.Open(path.pathname(), "wb", nullptr));
+  stream.DisableBuffering();
+  LogMessage::AddLogToStream(&stream, LS_SENSITIVE);
+
+  int64_t start = TimeMillis(), finish;
+  std::string message('X', 80);
+  for (int i = 0; i < 1000; ++i) {
+    LOG(LS_SENSITIVE) << message;
+  }
+  finish = TimeMillis();
+
+  LogMessage::RemoveLogToStream(&stream);
+  stream.Close();
+  Filesystem::DeleteFile(path);
+
+  LOG(LS_INFO) << "Average log time: " << TimeDiff(finish, start) << " ms";
+}
+
+}  // namespace rtc
diff --git a/base/logsinks.cc b/base/logsinks.cc
new file mode 100644
index 0000000..5a6db45
--- /dev/null
+++ b/base/logsinks.cc
@@ -0,0 +1,64 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/logsinks.h"
+
+#include <iostream>
+#include <string>
+
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+FileRotatingLogSink::FileRotatingLogSink(const std::string& log_dir_path,
+                                         const std::string& log_prefix,
+                                         size_t max_log_size,
+                                         size_t num_log_files)
+    : FileRotatingLogSink(new FileRotatingStream(log_dir_path,
+                                                 log_prefix,
+                                                 max_log_size,
+                                                 num_log_files)) {
+}
+
+FileRotatingLogSink::FileRotatingLogSink(FileRotatingStream* stream)
+    : stream_(stream) {
+  RTC_DCHECK(stream);
+}
+
+FileRotatingLogSink::~FileRotatingLogSink() {
+}
+
+void FileRotatingLogSink::OnLogMessage(const std::string& message) {
+  if (stream_->GetState() != SS_OPEN) {
+    std::cerr << "Init() must be called before adding this sink." << std::endl;
+    return;
+  }
+  stream_->WriteAll(message.c_str(), message.size(), nullptr, nullptr);
+}
+
+bool FileRotatingLogSink::Init() {
+  return stream_->Open();
+}
+
+bool FileRotatingLogSink::DisableBuffering() {
+  return stream_->DisableBuffering();
+}
+
+CallSessionFileRotatingLogSink::CallSessionFileRotatingLogSink(
+    const std::string& log_dir_path,
+    size_t max_total_log_size)
+    : FileRotatingLogSink(
+          new CallSessionFileRotatingStream(log_dir_path, max_total_log_size)) {
+}
+
+CallSessionFileRotatingLogSink::~CallSessionFileRotatingLogSink() {
+}
+
+}  // namespace rtc
diff --git a/base/logsinks.h b/base/logsinks.h
index 95e6dc6..e75120e 100644
--- a/base/logsinks.h
+++ b/base/logsinks.h
@@ -8,12 +8,61 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_LOGSINKS_H_
-#define WEBRTC_BASE_LOGSINKS_H_
+#ifndef WEBRTC_BASE_FILE_ROTATING_LOG_SINK_H_
+#define WEBRTC_BASE_FILE_ROTATING_LOG_SINK_H_
 
+#include <memory>
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/logsinks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/filerotatingstream.h"
+#include "webrtc/base/logging.h"
 
-#endif  // WEBRTC_BASE_LOGSINKS_H_
+namespace rtc {
+
+// Log sink that uses a FileRotatingStream to write to disk.
+// Init() must be called before adding this sink.
+class FileRotatingLogSink : public LogSink {
+ public:
+  // |num_log_files| must be greater than 1 and |max_log_size| must be greater
+  // than 0.
+  FileRotatingLogSink(const std::string& log_dir_path,
+                      const std::string& log_prefix,
+                      size_t max_log_size,
+                      size_t num_log_files);
+  ~FileRotatingLogSink() override;
+
+  // Writes the message to the current file. It will spill over to the next
+  // file if needed.
+  void OnLogMessage(const std::string& message) override;
+
+  // Deletes any existing files in the directory and creates a new log file.
+  virtual bool Init();
+
+  // Disables buffering on the underlying stream.
+  bool DisableBuffering();
+
+ protected:
+  explicit FileRotatingLogSink(FileRotatingStream* stream);
+
+ private:
+  std::unique_ptr<FileRotatingStream> stream_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(FileRotatingLogSink);
+};
+
+// Log sink that uses a CallSessionFileRotatingStream to write to disk.
+// Init() must be called before adding this sink.
+class CallSessionFileRotatingLogSink : public FileRotatingLogSink {
+ public:
+  CallSessionFileRotatingLogSink(const std::string& log_dir_path,
+                                 size_t max_total_log_size);
+  ~CallSessionFileRotatingLogSink() override;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(CallSessionFileRotatingLogSink);
+};
+
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_FILE_ROTATING_LOG_SINK_H_
diff --git a/base/macifaddrs_converter.cc b/base/macifaddrs_converter.cc
new file mode 100644
index 0000000..2ad070e
--- /dev/null
+++ b/base/macifaddrs_converter.cc
@@ -0,0 +1,282 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/ifaddrs_converter.h"
+#include "webrtc/base/logging.h"
+
+#if !defined(WEBRTC_IOS)
+#include <net/if_media.h>
+#include <netinet/in_var.h>
+#else  // WEBRTC_IOS
+#define SCOPE6_ID_MAX 16
+
+struct in6_addrlifetime {
+  time_t ia6t_expire;    /* valid lifetime expiration time */
+  time_t ia6t_preferred; /* preferred lifetime expiration time */
+  u_int32_t ia6t_vltime; /* valid lifetime */
+  u_int32_t ia6t_pltime; /* prefix lifetime */
+};
+
+struct in6_ifstat {
+  u_quad_t ifs6_in_receive;      /* # of total input datagram */
+  u_quad_t ifs6_in_hdrerr;       /* # of datagrams with invalid hdr */
+  u_quad_t ifs6_in_toobig;       /* # of datagrams exceeded MTU */
+  u_quad_t ifs6_in_noroute;      /* # of datagrams with no route */
+  u_quad_t ifs6_in_addrerr;      /* # of datagrams with invalid dst */
+  u_quad_t ifs6_in_protounknown; /* # of datagrams with unknown proto */
+                                 /* NOTE: increment on final dst if */
+  u_quad_t ifs6_in_truncated;    /* # of truncated datagrams */
+  u_quad_t ifs6_in_discard;      /* # of discarded datagrams */
+                                 /* NOTE: fragment timeout is not here */
+  u_quad_t ifs6_in_deliver;      /* # of datagrams delivered to ULP */
+                                 /* NOTE: increment on final dst if */
+  u_quad_t ifs6_out_forward;     /* # of datagrams forwarded */
+                                 /* NOTE: increment on outgoing if */
+  u_quad_t ifs6_out_request;     /* # of outgoing datagrams from ULP */
+                                 /* NOTE: does not include forwrads */
+  u_quad_t ifs6_out_discard;     /* # of discarded datagrams */
+  u_quad_t ifs6_out_fragok;      /* # of datagrams fragmented */
+  u_quad_t ifs6_out_fragfail;    /* # of datagrams failed on fragment */
+  u_quad_t ifs6_out_fragcreat;   /* # of fragment datagrams */
+                                 /* NOTE: this is # after fragment */
+  u_quad_t ifs6_reass_reqd;      /* # of incoming fragmented packets */
+                                 /* NOTE: increment on final dst if */
+  u_quad_t ifs6_reass_ok;        /* # of reassembled packets */
+                                 /* NOTE: this is # after reass */
+                                 /* NOTE: increment on final dst if */
+  u_quad_t ifs6_reass_fail;      /* # of reass failures */
+                                 /* NOTE: may not be packet count */
+                                 /* NOTE: increment on final dst if */
+  u_quad_t ifs6_in_mcast;        /* # of inbound multicast datagrams */
+  u_quad_t ifs6_out_mcast;       /* # of outbound multicast datagrams */
+};
+struct icmp6_ifstat {
+  /*
+   * Input statistics
+   */
+  /* ipv6IfIcmpInMsgs, total # of input messages */
+  u_quad_t ifs6_in_msg;
+  /* ipv6IfIcmpInErrors, # of input error messages */
+  u_quad_t ifs6_in_error;
+  /* ipv6IfIcmpInDestUnreachs, # of input dest unreach errors */
+  u_quad_t ifs6_in_dstunreach;
+  /* ipv6IfIcmpInAdminProhibs, # of input admin. prohibited errs */
+  u_quad_t ifs6_in_adminprohib;
+  /* ipv6IfIcmpInTimeExcds, # of input time exceeded errors */
+  u_quad_t ifs6_in_timeexceed;
+  /* ipv6IfIcmpInParmProblems, # of input parameter problem errors */
+  u_quad_t ifs6_in_paramprob;
+  /* ipv6IfIcmpInPktTooBigs, # of input packet too big errors */
+  u_quad_t ifs6_in_pkttoobig;
+  /* ipv6IfIcmpInEchos, # of input echo requests */
+  u_quad_t ifs6_in_echo;
+  /* ipv6IfIcmpInEchoReplies, # of input echo replies */
+  u_quad_t ifs6_in_echoreply;
+  /* ipv6IfIcmpInRouterSolicits, # of input router solicitations */
+  u_quad_t ifs6_in_routersolicit;
+  /* ipv6IfIcmpInRouterAdvertisements, # of input router advertisements */
+  u_quad_t ifs6_in_routeradvert;
+  /* ipv6IfIcmpInNeighborSolicits, # of input neighbor solicitations */
+  u_quad_t ifs6_in_neighborsolicit;
+  /* ipv6IfIcmpInNeighborAdvertisements, # of input neighbor advs. */
+  u_quad_t ifs6_in_neighboradvert;
+  /* ipv6IfIcmpInRedirects, # of input redirects */
+  u_quad_t ifs6_in_redirect;
+  /* ipv6IfIcmpInGroupMembQueries, # of input MLD queries */
+  u_quad_t ifs6_in_mldquery;
+  /* ipv6IfIcmpInGroupMembResponses, # of input MLD reports */
+  u_quad_t ifs6_in_mldreport;
+  /* ipv6IfIcmpInGroupMembReductions, # of input MLD done */
+  u_quad_t ifs6_in_mlddone;
+
+  /*
+   * Output statistics. We should solve unresolved routing problem...
+   */
+  /* ipv6IfIcmpOutMsgs, total # of output messages */
+  u_quad_t ifs6_out_msg;
+  /* ipv6IfIcmpOutErrors, # of output error messages */
+  u_quad_t ifs6_out_error;
+  /* ipv6IfIcmpOutDestUnreachs, # of output dest unreach errors */
+  u_quad_t ifs6_out_dstunreach;
+  /* ipv6IfIcmpOutAdminProhibs, # of output admin. prohibited errs */
+  u_quad_t ifs6_out_adminprohib;
+  /* ipv6IfIcmpOutTimeExcds, # of output time exceeded errors */
+  u_quad_t ifs6_out_timeexceed;
+  /* ipv6IfIcmpOutParmProblems, # of output parameter problem errors */
+  u_quad_t ifs6_out_paramprob;
+  /* ipv6IfIcmpOutPktTooBigs, # of output packet too big errors */
+  u_quad_t ifs6_out_pkttoobig;
+  /* ipv6IfIcmpOutEchos, # of output echo requests */
+  u_quad_t ifs6_out_echo;
+  /* ipv6IfIcmpOutEchoReplies, # of output echo replies */
+  u_quad_t ifs6_out_echoreply;
+  /* ipv6IfIcmpOutRouterSolicits, # of output router solicitations */
+  u_quad_t ifs6_out_routersolicit;
+  /* ipv6IfIcmpOutRouterAdvertisements, # of output router advs. */
+  u_quad_t ifs6_out_routeradvert;
+  /* ipv6IfIcmpOutNeighborSolicits, # of output neighbor solicitations */
+  u_quad_t ifs6_out_neighborsolicit;
+  /* ipv6IfIcmpOutNeighborAdvertisements, # of output neighbor advs. */
+  u_quad_t ifs6_out_neighboradvert;
+  /* ipv6IfIcmpOutRedirects, # of output redirects */
+  u_quad_t ifs6_out_redirect;
+  /* ipv6IfIcmpOutGroupMembQueries, # of output MLD queries */
+  u_quad_t ifs6_out_mldquery;
+  /* ipv6IfIcmpOutGroupMembResponses, # of output MLD reports */
+  u_quad_t ifs6_out_mldreport;
+  /* ipv6IfIcmpOutGroupMembReductions, # of output MLD done */
+  u_quad_t ifs6_out_mlddone;
+};
+
+struct in6_ifreq {
+  char ifr_name[IFNAMSIZ];
+  union {
+    struct sockaddr_in6 ifru_addr;
+    struct sockaddr_in6 ifru_dstaddr;
+    int ifru_flags;
+    int ifru_flags6;
+    int ifru_metric;
+    int ifru_intval;
+    caddr_t ifru_data;
+    struct in6_addrlifetime ifru_lifetime;
+    struct in6_ifstat ifru_stat;
+    struct icmp6_ifstat ifru_icmp6stat;
+    u_int32_t ifru_scope_id[SCOPE6_ID_MAX];
+  } ifr_ifru;
+};
+
+#define SIOCGIFAFLAG_IN6 _IOWR('i', 73, struct in6_ifreq)
+
+#define IN6_IFF_ANYCAST 0x0001    /* anycast address */
+#define IN6_IFF_TENTATIVE 0x0002  /* tentative address */
+#define IN6_IFF_DUPLICATED 0x0004 /* DAD detected duplicate */
+#define IN6_IFF_DETACHED 0x0008   /* may be detached from the link */
+#define IN6_IFF_DEPRECATED 0x0010 /* deprecated address */
+#define IN6_IFF_TEMPORARY 0x0080  /* temporary (anonymous) address. */
+
+#endif  // WEBRTC_IOS
+
+namespace rtc {
+
+namespace {
+
+class IPv6AttributesGetter {
+ public:
+  IPv6AttributesGetter();
+  virtual ~IPv6AttributesGetter();
+  bool IsInitialized() const;
+  bool GetIPAttributes(const char* ifname,
+                       const sockaddr* sock_addr,
+                       int* native_attributes);
+
+ private:
+  // on MAC or IOS, we have to use ioctl with a socket to query an IPv6
+  // interface's attribute.
+  int ioctl_socket_;
+};
+
+IPv6AttributesGetter::IPv6AttributesGetter()
+    : ioctl_socket_(
+          socket(AF_INET6, SOCK_DGRAM, 0 /* unspecified protocol */)) {
+  RTC_DCHECK_GE(ioctl_socket_, 0);
+}
+
+bool IPv6AttributesGetter::IsInitialized() const {
+  return ioctl_socket_ >= 0;
+}
+
+IPv6AttributesGetter::~IPv6AttributesGetter() {
+  if (!IsInitialized()) {
+    return;
+  }
+  close(ioctl_socket_);
+}
+
+bool IPv6AttributesGetter::GetIPAttributes(const char* ifname,
+                                           const sockaddr* sock_addr,
+                                           int* native_attributes) {
+  if (!IsInitialized()) {
+    return false;
+  }
+
+  struct in6_ifreq ifr = {};
+  strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
+  memcpy(&ifr.ifr_ifru.ifru_addr, sock_addr, sock_addr->sa_len);
+  int rv = ioctl(ioctl_socket_, SIOCGIFAFLAG_IN6, &ifr);
+  if (rv >= 0) {
+    *native_attributes = ifr.ifr_ifru.ifru_flags;
+  } else {
+    LOG(LS_ERROR) << "ioctl returns " << errno;
+  }
+  return (rv >= 0);
+}
+
+// Converts native IPv6 address attributes to net IPv6 address attributes.  If
+// it returns false, the IP address isn't suitable for one-to-one communications
+// applications and should be ignored.
+bool ConvertNativeToIPAttributes(int native_attributes, int* net_attributes) {
+  // For MacOSX, we disallow addresses with attributes IN6_IFF_ANYCASE,
+  // IN6_IFF_DUPLICATED, IN6_IFF_TENTATIVE, and IN6_IFF_DETACHED as these are
+  // still progressing through duplicated address detection (DAD) or are not
+  // suitable for one-to-one communication applications.
+  if (native_attributes & (IN6_IFF_ANYCAST | IN6_IFF_DUPLICATED |
+                           IN6_IFF_TENTATIVE | IN6_IFF_DETACHED)) {
+    return false;
+  }
+
+  if (native_attributes & IN6_IFF_TEMPORARY) {
+    *net_attributes |= IPV6_ADDRESS_FLAG_TEMPORARY;
+  }
+
+  if (native_attributes & IN6_IFF_DEPRECATED) {
+    *net_attributes |= IPV6_ADDRESS_FLAG_DEPRECATED;
+  }
+
+  return true;
+}
+
+class MacIfAddrsConverter : public IfAddrsConverter {
+ public:
+  MacIfAddrsConverter() : ip_attribute_getter_(new IPv6AttributesGetter()) {}
+  ~MacIfAddrsConverter() override {}
+
+  bool ConvertNativeAttributesToIPAttributes(const struct ifaddrs* interface,
+                                             int* ip_attributes) override {
+    int native_attributes;
+    if (!ip_attribute_getter_->GetIPAttributes(
+            interface->ifa_name, interface->ifa_addr, &native_attributes)) {
+      return false;
+    }
+
+    if (!ConvertNativeToIPAttributes(native_attributes, ip_attributes)) {
+      return false;
+    }
+
+    return true;
+  }
+
+ private:
+  std::unique_ptr<IPv6AttributesGetter> ip_attribute_getter_;
+};
+
+}  // namespace
+
+IfAddrsConverter* CreateIfAddrsConverter() {
+  return new MacIfAddrsConverter();
+}
+
+}  // namespace rtc
diff --git a/base/macutils.cc b/base/macutils.cc
new file mode 100644
index 0000000..6e90722
--- /dev/null
+++ b/base/macutils.cc
@@ -0,0 +1,125 @@
+/*
+ *  Copyright 2007 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <cstring>
+#include <memory>
+#include <sstream>
+
+#include <sys/utsname.h>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/macutils.h"
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool ToUtf8(const CFStringRef str16, std::string* str8) {
+  if ((nullptr == str16) || (nullptr == str8)) {
+    return false;
+  }
+  size_t maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str16),
+                                                    kCFStringEncodingUTF8) + 1;
+  std::unique_ptr<char[]> buffer(new char[maxlen]);
+  if (!buffer || !CFStringGetCString(str16, buffer.get(), maxlen,
+                                     kCFStringEncodingUTF8)) {
+    return false;
+  }
+  str8->assign(buffer.get());
+  return true;
+}
+
+bool ToUtf16(const std::string& str8, CFStringRef* str16) {
+  if (nullptr == str16) {
+    return false;
+  }
+  *str16 = CFStringCreateWithBytes(kCFAllocatorDefault,
+                                   reinterpret_cast<const UInt8*>(str8.data()),
+                                   str8.length(), kCFStringEncodingUTF8,
+                                   false);
+  return nullptr != *str16;
+}
+
+void DecodeFourChar(UInt32 fc, std::string* out) {
+  std::stringstream ss;
+  ss << '\'';
+  bool printable = true;
+  for (int i = 3; i >= 0; --i) {
+    char ch = (fc >> (8 * i)) & 0xFF;
+    if (isprint(static_cast<unsigned char>(ch))) {
+      ss << ch;
+    } else {
+      printable = false;
+      break;
+    }
+  }
+  if (printable) {
+    ss << '\'';
+  } else {
+    ss.str("");
+    ss << "0x" << std::hex << fc;
+  }
+  out->append(ss.str());
+}
+
+static bool GetOSVersion(int* major, int* minor, int* bugfix) {
+  RTC_DCHECK(major && minor && bugfix);
+  struct utsname uname_info;
+  if (uname(&uname_info) != 0)
+    return false;
+
+  if (strcmp(uname_info.sysname, "Darwin") != 0)
+    return false;
+  *major = 10;
+
+  // The market version of macOS is always 4 lower than the internal version.
+  int minor_version = atoi(uname_info.release);
+  RTC_CHECK(minor_version >= 6);
+  *minor = minor_version - 4;
+
+  const char* dot = ::strchr(uname_info.release, '.');
+  if (!dot)
+    return false;
+  *bugfix = atoi(dot + 1);
+  return true;
+}
+
+MacOSVersionName GetOSVersionName() {
+  int major = 0, minor = 0, bugfix = 0;
+  if (!GetOSVersion(&major, &minor, &bugfix)) {
+    return kMacOSUnknown;
+  }
+  if (major > 10) {
+    return kMacOSNewer;
+  }
+  if ((major < 10) || (minor < 3)) {
+    return kMacOSOlder;
+  }
+  switch (minor) {
+    case 3:
+      return kMacOSPanther;
+    case 4:
+      return kMacOSTiger;
+    case 5:
+      return kMacOSLeopard;
+    case 6:
+      return kMacOSSnowLeopard;
+    case 7:
+      return kMacOSLion;
+    case 8:
+      return kMacOSMountainLion;
+    case 9:
+      return kMacOSMavericks;
+  }
+  return kMacOSNewer;
+}
+}  // namespace rtc
diff --git a/base/macutils.h b/base/macutils.h
index ed0c4f5..fdcb3ee 100644
--- a/base/macutils.h
+++ b/base/macutils.h
@@ -8,12 +8,43 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_MACUTILS_H_
-#define WEBRTC_BASE_MACUTILS_H_
+#ifndef WEBRTC_BASE_MACUTILS_H__
+#define WEBRTC_BASE_MACUTILS_H__
 
+#include <CoreFoundation/CoreFoundation.h>
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/macutils.h"
+namespace rtc {
 
-#endif  // WEBRTC_BASE_MACUTILS_H_
+///////////////////////////////////////////////////////////////////////////////
+
+// Note that some of these functions work for both iOS and Mac OS X.  The ones
+// that are specific to Mac are #ifdef'ed as such.
+
+bool ToUtf8(const CFStringRef str16, std::string* str8);
+bool ToUtf16(const std::string& str8, CFStringRef* str16);
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+void DecodeFourChar(UInt32 fc, std::string* out);
+
+enum MacOSVersionName {
+  kMacOSUnknown,       // ???
+  kMacOSOlder,         // 10.2-
+  kMacOSPanther,       // 10.3
+  kMacOSTiger,         // 10.4
+  kMacOSLeopard,       // 10.5
+  kMacOSSnowLeopard,   // 10.6
+  kMacOSLion,          // 10.7
+  kMacOSMountainLion,  // 10.8
+  kMacOSMavericks,     // 10.9
+  kMacOSNewer,         // 10.10+
+};
+
+MacOSVersionName GetOSVersionName();
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_MACUTILS_H__
diff --git a/base/macutils_unittest.cc b/base/macutils_unittest.cc
new file mode 100644
index 0000000..d3e33e4
--- /dev/null
+++ b/base/macutils_unittest.cc
@@ -0,0 +1,18 @@
+/*
+ *  Copyright 2009 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/macutils.h"
+
+TEST(MacUtilsTest, GetOsVersionName) {
+  rtc::MacOSVersionName ver = rtc::GetOSVersionName();
+  LOG(LS_INFO) << "GetOsVersionName " << ver;
+  EXPECT_NE(rtc::kMacOSUnknown, ver);
+}
diff --git a/base/mathutils.h b/base/mathutils.h
index 9e5c3ca..3c70e76 100644
--- a/base/mathutils.h
+++ b/base/mathutils.h
@@ -11,9 +11,29 @@
 #ifndef WEBRTC_BASE_MATHUTILS_H_
 #define WEBRTC_BASE_MATHUTILS_H_
 
+#include <math.h>
+#include <type_traits>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/mathutils.h"
+#include "webrtc/base/checks.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265359f
+#endif
+
+// Given two numbers |x| and |y| such that x >= y, computes the difference
+// x - y without causing undefined behavior due to signed overflow.
+template <typename T>
+typename std::make_unsigned<T>::type unsigned_difference(T x, T y) {
+  static_assert(
+      std::is_signed<T>::value,
+      "Function unsigned_difference is only meaningful for signed types.");
+  RTC_DCHECK_GE(x, y);
+  typedef typename std::make_unsigned<T>::type unsigned_type;
+  // int -> unsigned conversion repeatedly adds UINT_MAX + 1 until the number
+  // can be represented as an unsigned. Since we know that the actual
+  // difference x - y can be represented as an unsigned, it is sufficient to
+  // compute the difference modulo UINT_MAX + 1, i.e using unsigned arithmetic.
+  return static_cast<unsigned_type>(x) - static_cast<unsigned_type>(y);
+}
 
 #endif  // WEBRTC_BASE_MATHUTILS_H_
diff --git a/base/md5.cc b/base/md5.cc
new file mode 100644
index 0000000..fda6ddd
--- /dev/null
+++ b/base/md5.cc
@@ -0,0 +1,222 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+// Changes from original C code:
+// Ported to C++, type casting, Google code style.
+
+#include "webrtc/base/md5.h"
+
+// TODO: Avoid memcmpy - hash directly from memory.
+#include <string.h>  // for memcpy().
+
+#include "webrtc/base/byteorder.h"  // for RTC_ARCH_CPU_LITTLE_ENDIAN.
+
+namespace rtc {
+
+#ifdef RTC_ARCH_CPU_LITTLE_ENDIAN
+#define ByteReverse(buf, len)  // Nothing.
+#else  // RTC_ARCH_CPU_BIG_ENDIAN
+static void ByteReverse(uint32_t* buf, int len) {
+  for (int i = 0; i < len; ++i) {
+    buf[i] = rtc::GetLE32(&buf[i]);
+  }
+}
+#endif
+
+// Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+// initialization constants.
+void MD5Init(MD5Context* ctx) {
+  ctx->buf[0] = 0x67452301;
+  ctx->buf[1] = 0xefcdab89;
+  ctx->buf[2] = 0x98badcfe;
+  ctx->buf[3] = 0x10325476;
+  ctx->bits[0] = 0;
+  ctx->bits[1] = 0;
+}
+
+// Update context to reflect the concatenation of another buffer full of bytes.
+void MD5Update(MD5Context* ctx, const uint8_t* buf, size_t len) {
+  // Update bitcount.
+  uint32_t t = ctx->bits[0];
+  if ((ctx->bits[0] = t + (static_cast<uint32_t>(len) << 3)) < t) {
+    ctx->bits[1]++;  // Carry from low to high.
+  }
+  ctx->bits[1] += static_cast<uint32_t>(len >> 29);
+  t = (t >> 3) & 0x3f;  // Bytes already in shsInfo->data.
+
+  // Handle any leading odd-sized chunks.
+  if (t) {
+    uint8_t* p = reinterpret_cast<uint8_t*>(ctx->in) + t;
+
+    t = 64-t;
+    if (len < t) {
+      memcpy(p, buf, len);
+      return;
+    }
+    memcpy(p, buf, t);
+    ByteReverse(ctx->in, 16);
+    MD5Transform(ctx->buf, ctx->in);
+    buf += t;
+    len -= t;
+  }
+
+  // Process data in 64-byte chunks.
+  while (len >= 64) {
+    memcpy(ctx->in, buf, 64);
+    ByteReverse(ctx->in, 16);
+    MD5Transform(ctx->buf, ctx->in);
+    buf += 64;
+    len -= 64;
+  }
+
+  // Handle any remaining bytes of data.
+  memcpy(ctx->in, buf, len);
+}
+
+// Final wrapup - pad to 64-byte boundary with the bit pattern.
+// 1 0* (64-bit count of bits processed, MSB-first)
+void MD5Final(MD5Context* ctx, uint8_t digest[16]) {
+  // Compute number of bytes mod 64.
+  uint32_t count = (ctx->bits[0] >> 3) & 0x3F;
+
+  // Set the first char of padding to 0x80.  This is safe since there is
+  // always at least one byte free.
+  uint8_t* p = reinterpret_cast<uint8_t*>(ctx->in) + count;
+  *p++ = 0x80;
+
+  // Bytes of padding needed to make 64 bytes.
+  count = 64 - 1 - count;
+
+  // Pad out to 56 mod 64.
+  if (count < 8) {
+    // Two lots of padding:  Pad the first block to 64 bytes.
+    memset(p, 0, count);
+    ByteReverse(ctx->in, 16);
+    MD5Transform(ctx->buf, ctx->in);
+
+    // Now fill the next block with 56 bytes.
+    memset(ctx->in, 0, 56);
+  } else {
+    // Pad block to 56 bytes.
+    memset(p, 0, count - 8);
+  }
+  ByteReverse(ctx->in, 14);
+
+  // Append length in bits and transform.
+  ctx->in[14] = ctx->bits[0];
+  ctx->in[15] = ctx->bits[1];
+
+  MD5Transform(ctx->buf, ctx->in);
+  ByteReverse(ctx->buf, 4);
+  memcpy(digest, ctx->buf, 16);
+  memset(ctx, 0, sizeof(*ctx));  // In case it's sensitive.
+}
+
+// The four core functions - F1 is optimized somewhat.
+// #define F1(x, y, z) (x & y | ~x & z)
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+// This is the central step in the MD5 algorithm.
+#define MD5STEP(f, w, x, y, z, data, s) \
+    (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
+
+// The core of the MD5 algorithm, this alters an existing MD5 hash to
+// reflect the addition of 16 longwords of new data.  MD5Update blocks
+// the data and converts bytes into longwords for this routine.
+void MD5Transform(uint32_t buf[4], const uint32_t in[16]) {
+  uint32_t a = buf[0];
+  uint32_t b = buf[1];
+  uint32_t c = buf[2];
+  uint32_t d = buf[3];
+
+  MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
+  MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
+  MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
+  MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
+  MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
+  MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
+  MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
+  MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
+  MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
+  MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
+  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+  MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
+  MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
+  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+  MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
+  MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
+  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+  MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
+  MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
+  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+  MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
+  MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
+  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+  MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
+  MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
+  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+  MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
+  MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
+  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+  MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
+  MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
+  MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
+  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+  MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
+  MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
+  MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
+  MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
+  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+  MD5STEP(F3, b, c, d, a, in[ 2] + 0xc4ac5665, 23);
+
+  MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
+  MD5STEP(F4, d, a, b, c, in[ 7] + 0x432aff97, 10);
+  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+  MD5STEP(F4, b, c, d, a, in[ 5] + 0xfc93a039, 21);
+  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+  MD5STEP(F4, d, a, b, c, in[ 3] + 0x8f0ccc92, 10);
+  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+  MD5STEP(F4, b, c, d, a, in[ 1] + 0x85845dd1, 21);
+  MD5STEP(F4, a, b, c, d, in[ 8] + 0x6fa87e4f, 6);
+  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+  MD5STEP(F4, c, d, a, b, in[ 6] + 0xa3014314, 15);
+  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+  MD5STEP(F4, a, b, c, d, in[ 4] + 0xf7537e82, 6);
+  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+  MD5STEP(F4, c, d, a, b, in[ 2] + 0x2ad7d2bb, 15);
+  MD5STEP(F4, b, c, d, a, in[ 9] + 0xeb86d391, 21);
+  buf[0] += a;
+  buf[1] += b;
+  buf[2] += c;
+  buf[3] += d;
+}
+
+}  // namespace rtc
diff --git a/base/md5.h b/base/md5.h
index fd17541..45e00b7 100644
--- a/base/md5.h
+++ b/base/md5.h
@@ -23,9 +23,22 @@
 #ifndef WEBRTC_BASE_MD5_H_
 #define WEBRTC_BASE_MD5_H_
 
+#include <stdint.h>
+#include <stdlib.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/md5.h"
+namespace rtc {
+
+struct MD5Context {
+  uint32_t buf[4];
+  uint32_t bits[2];
+  uint32_t in[16];
+};
+
+void MD5Init(MD5Context* context);
+void MD5Update(MD5Context* context, const uint8_t* data, size_t len);
+void MD5Final(MD5Context* context, uint8_t digest[16]);
+void MD5Transform(uint32_t buf[4], const uint32_t in[16]);
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_MD5_H_
diff --git a/base/md5digest.cc b/base/md5digest.cc
new file mode 100644
index 0000000..74f6bed
--- /dev/null
+++ b/base/md5digest.cc
@@ -0,0 +1,32 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/md5digest.h"
+
+namespace rtc {
+
+size_t Md5Digest::Size() const {
+  return kSize;
+}
+
+void Md5Digest::Update(const void* buf, size_t len) {
+  MD5Update(&ctx_, static_cast<const uint8_t*>(buf), len);
+}
+
+size_t Md5Digest::Finish(void* buf, size_t len) {
+  if (len < kSize) {
+    return 0;
+  }
+  MD5Final(&ctx_, static_cast<uint8_t*>(buf));
+  MD5Init(&ctx_);  // Reset for next use.
+  return kSize;
+}
+
+};  // namespace rtc
diff --git a/base/md5digest.h b/base/md5digest.h
index 66d6ee1..3f8d656 100644
--- a/base/md5digest.h
+++ b/base/md5digest.h
@@ -11,9 +11,26 @@
 #ifndef WEBRTC_BASE_MD5DIGEST_H_
 #define WEBRTC_BASE_MD5DIGEST_H_
 
+#include "webrtc/base/md5.h"
+#include "webrtc/base/messagedigest.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/md5digest.h"
+namespace rtc {
+
+// A simple wrapper for our MD5 implementation.
+class Md5Digest : public MessageDigest {
+ public:
+  enum { kSize = 16 };
+  Md5Digest() {
+    MD5Init(&ctx_);
+  }
+  size_t Size() const override;
+  void Update(const void* buf, size_t len) override;
+  size_t Finish(void* buf, size_t len) override;
+
+ private:
+  MD5Context ctx_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_MD5DIGEST_H_
diff --git a/base/md5digest_unittest.cc b/base/md5digest_unittest.cc
new file mode 100644
index 0000000..67c62db
--- /dev/null
+++ b/base/md5digest_unittest.cc
@@ -0,0 +1,79 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/md5digest.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/stringencode.h"
+
+namespace rtc {
+
+std::string Md5(const std::string& input) {
+  Md5Digest md5;
+  return ComputeDigest(&md5, input);
+}
+
+TEST(Md5DigestTest, TestSize) {
+  Md5Digest md5;
+  EXPECT_EQ(16, static_cast<int>(Md5Digest::kSize));
+  EXPECT_EQ(16U, md5.Size());
+}
+
+TEST(Md5DigestTest, TestBasic) {
+  // These are the standard MD5 test vectors from RFC 1321.
+  EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e", Md5(""));
+  EXPECT_EQ("0cc175b9c0f1b6a831c399e269772661", Md5("a"));
+  EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", Md5("abc"));
+  EXPECT_EQ("f96b697d7cb7938d525a2f31aaf161d0", Md5("message digest"));
+  EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b",
+            Md5("abcdefghijklmnopqrstuvwxyz"));
+}
+
+TEST(Md5DigestTest, TestMultipleUpdates) {
+  Md5Digest md5;
+  std::string input = "abcdefghijklmnopqrstuvwxyz";
+  char output[Md5Digest::kSize];
+  for (size_t i = 0; i < input.size(); ++i) {
+    md5.Update(&input[i], 1);
+  }
+  EXPECT_EQ(md5.Size(), md5.Finish(output, sizeof(output)));
+  EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b",
+            hex_encode(output, sizeof(output)));
+}
+
+TEST(Md5DigestTest, TestReuse) {
+  Md5Digest md5;
+  std::string input = "message digest";
+  EXPECT_EQ("f96b697d7cb7938d525a2f31aaf161d0", ComputeDigest(&md5, input));
+  input = "abcdefghijklmnopqrstuvwxyz";
+  EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", ComputeDigest(&md5, input));
+}
+
+TEST(Md5DigestTest, TestBufferTooSmall) {
+  Md5Digest md5;
+  std::string input = "abcdefghijklmnopqrstuvwxyz";
+  char output[Md5Digest::kSize - 1];
+  md5.Update(input.c_str(), input.size());
+  EXPECT_EQ(0U, md5.Finish(output, sizeof(output)));
+}
+
+TEST(Md5DigestTest, TestBufferConst) {
+  Md5Digest md5;
+  const int kLongSize = 1000000;
+  std::string input(kLongSize, '\0');
+  for (int i = 0; i < kLongSize; ++i) {
+    input[i] = static_cast<char>(i);
+  }
+  md5.Update(input.c_str(), input.size());
+  for (int i = 0; i < kLongSize; ++i) {
+    EXPECT_EQ(static_cast<char>(i), input[i]);
+  }
+}
+
+}  // namespace rtc
diff --git a/base/memory_usage.cc b/base/memory_usage.cc
new file mode 100644
index 0000000..d37a702
--- /dev/null
+++ b/base/memory_usage.cc
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/memory_usage.h"
+
+#if defined(WEBRTC_LINUX)
+#include <unistd.h>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#elif defined(WEBRTC_MAC)
+#include <mach/mach.h>
+#elif defined(WEBRTC_WIN)
+#include <windows.h>
+#include <psapi.h>
+#endif
+
+#include "webrtc/base/logging.h"
+
+namespace rtc {
+
+int64_t GetProcessResidentSizeBytes() {
+#if defined(WEBRTC_LINUX)
+  FILE* file = fopen("/proc/self/statm", "r");
+  if (file == nullptr) {
+    LOG(LS_ERROR) << "Failed to open /proc/self/statm";
+    return -1;
+  }
+  int result = -1;
+  if (fscanf(file, "%*s%d", &result) != 1) {
+    fclose(file);
+    LOG(LS_ERROR) << "Failed to parse /proc/self/statm";
+    return -1;
+  }
+  fclose(file);
+  return static_cast<int64_t>(result) * sysconf(_SC_PAGESIZE);
+#elif defined(WEBRTC_MAC)
+  task_basic_info_64 info;
+  mach_msg_type_number_t info_count = TASK_BASIC_INFO_64_COUNT;
+  if (task_info(mach_task_self(), TASK_BASIC_INFO_64,
+                reinterpret_cast<task_info_t>(&info),
+                &info_count) != KERN_SUCCESS) {
+    LOG_ERR(LS_ERROR) << "task_info() failed";
+    return -1;
+  }
+  return info.resident_size;
+#elif defined(WEBRTC_WIN)
+  PROCESS_MEMORY_COUNTERS pmc;
+  if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)) == 0) {
+    LOG_ERR(LS_ERROR) << "GetProcessMemoryInfo() failed";
+    return -1;
+  }
+  return pmc.WorkingSetSize;
+#else
+  // Not implemented yet.
+  static_assert(false,
+                "GetProcessVirtualMemoryUsageBytes() platform support not yet "
+                "implemented.");
+#endif
+}
+
+}  // namespace rtc
diff --git a/base/memory_usage.h b/base/memory_usage.h
index 5c22559..2699082 100644
--- a/base/memory_usage.h
+++ b/base/memory_usage.h
@@ -10,9 +10,15 @@
 #ifndef WEBRTC_BASE_MEMORY_USAGE_H_
 #define WEBRTC_BASE_MEMORY_USAGE_H_
 
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/memory_usage.h"
+namespace rtc {
+
+// Returns current memory used by the process in bytes (working set size on
+// Windows and resident set size on other platforms).
+// Returns -1 on failure.
+int64_t GetProcessResidentSizeBytes();
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_MEMORY_USAGE_H_
diff --git a/base/memory_usage_unittest.cc b/base/memory_usage_unittest.cc
new file mode 100644
index 0000000..99857ec
--- /dev/null
+++ b/base/memory_usage_unittest.cc
@@ -0,0 +1,23 @@
+/*
+ *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/memory_usage.h"
+#include <cstdio>
+#include "webrtc/test/gtest.h"
+
+namespace rtc {
+
+TEST(GetMemoryUsage, SimpleTest) {
+  int64_t used_bytes = GetProcessResidentSizeBytes();
+  EXPECT_GE(used_bytes, 0);
+}
+
+}  // namespace rtc
+
diff --git a/base/messagedigest.cc b/base/messagedigest.cc
new file mode 100644
index 0000000..05c8c4f
--- /dev/null
+++ b/base/messagedigest.cc
@@ -0,0 +1,167 @@
+/*
+ *  Copyright 2011 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/messagedigest.h"
+
+#include <memory>
+
+#include <string.h>
+
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/openssldigest.h"
+#include "webrtc/base/stringencode.h"
+
+namespace rtc {
+
+// From RFC 4572.
+const char DIGEST_MD5[]     = "md5";
+const char DIGEST_SHA_1[]   = "sha-1";
+const char DIGEST_SHA_224[] = "sha-224";
+const char DIGEST_SHA_256[] = "sha-256";
+const char DIGEST_SHA_384[] = "sha-384";
+const char DIGEST_SHA_512[] = "sha-512";
+
+static const size_t kBlockSize = 64;  // valid for SHA-256 and down
+
+MessageDigest* MessageDigestFactory::Create(const std::string& alg) {
+  MessageDigest* digest = new OpenSSLDigest(alg);
+  if (digest->Size() == 0) {  // invalid algorithm
+    delete digest;
+    digest = nullptr;
+  }
+  return digest;
+}
+
+bool IsFips180DigestAlgorithm(const std::string& alg) {
+  // These are the FIPS 180 algorithms.  According to RFC 4572 Section 5,
+  // "Self-signed certificates (for which legacy certificates are not a
+  // consideration) MUST use one of the FIPS 180 algorithms (SHA-1,
+  // SHA-224, SHA-256, SHA-384, or SHA-512) as their signature algorithm,
+  // and thus also MUST use it to calculate certificate fingerprints."
+  return alg == DIGEST_SHA_1 ||
+         alg == DIGEST_SHA_224 ||
+         alg == DIGEST_SHA_256 ||
+         alg == DIGEST_SHA_384 ||
+         alg == DIGEST_SHA_512;
+}
+
+size_t ComputeDigest(MessageDigest* digest, const void* input, size_t in_len,
+                     void* output, size_t out_len) {
+  digest->Update(input, in_len);
+  return digest->Finish(output, out_len);
+}
+
+size_t ComputeDigest(const std::string& alg, const void* input, size_t in_len,
+                     void* output, size_t out_len) {
+  std::unique_ptr<MessageDigest> digest(MessageDigestFactory::Create(alg));
+  return (digest) ?
+      ComputeDigest(digest.get(), input, in_len, output, out_len) :
+      0;
+}
+
+std::string ComputeDigest(MessageDigest* digest, const std::string& input) {
+  std::unique_ptr<char[]> output(new char[digest->Size()]);
+  ComputeDigest(digest, input.data(), input.size(),
+                output.get(), digest->Size());
+  return hex_encode(output.get(), digest->Size());
+}
+
+bool ComputeDigest(const std::string& alg, const std::string& input,
+                   std::string* output) {
+  std::unique_ptr<MessageDigest> digest(MessageDigestFactory::Create(alg));
+  if (!digest) {
+    return false;
+  }
+  *output = ComputeDigest(digest.get(), input);
+  return true;
+}
+
+std::string ComputeDigest(const std::string& alg, const std::string& input) {
+  std::string output;
+  ComputeDigest(alg, input, &output);
+  return output;
+}
+
+// Compute a RFC 2104 HMAC: H(K XOR opad, H(K XOR ipad, text))
+size_t ComputeHmac(MessageDigest* digest,
+                   const void* key, size_t key_len,
+                   const void* input, size_t in_len,
+                   void* output, size_t out_len) {
+  // We only handle algorithms with a 64-byte blocksize.
+  // TODO: Add BlockSize() method to MessageDigest.
+  size_t block_len = kBlockSize;
+  if (digest->Size() > 32) {
+    return 0;
+  }
+  // Copy the key to a block-sized buffer to simplify padding.
+  // If the key is longer than a block, hash it and use the result instead.
+  std::unique_ptr<uint8_t[]> new_key(new uint8_t[block_len]);
+  if (key_len > block_len) {
+    ComputeDigest(digest, key, key_len, new_key.get(), block_len);
+    memset(new_key.get() + digest->Size(), 0, block_len - digest->Size());
+  } else {
+    memcpy(new_key.get(), key, key_len);
+    memset(new_key.get() + key_len, 0, block_len - key_len);
+  }
+  // Set up the padding from the key, salting appropriately for each padding.
+  std::unique_ptr<uint8_t[]> o_pad(new uint8_t[block_len]);
+  std::unique_ptr<uint8_t[]> i_pad(new uint8_t[block_len]);
+  for (size_t i = 0; i < block_len; ++i) {
+    o_pad[i] = 0x5c ^ new_key[i];
+    i_pad[i] = 0x36 ^ new_key[i];
+  }
+  // Inner hash; hash the inner padding, and then the input buffer.
+  std::unique_ptr<uint8_t[]> inner(new uint8_t[digest->Size()]);
+  digest->Update(i_pad.get(), block_len);
+  digest->Update(input, in_len);
+  digest->Finish(inner.get(), digest->Size());
+  // Outer hash; hash the outer padding, and then the result of the inner hash.
+  digest->Update(o_pad.get(), block_len);
+  digest->Update(inner.get(), digest->Size());
+  return digest->Finish(output, out_len);
+}
+
+size_t ComputeHmac(const std::string& alg, const void* key, size_t key_len,
+                   const void* input, size_t in_len,
+                   void* output, size_t out_len) {
+  std::unique_ptr<MessageDigest> digest(MessageDigestFactory::Create(alg));
+  if (!digest) {
+    return 0;
+  }
+  return ComputeHmac(digest.get(), key, key_len,
+                     input, in_len, output, out_len);
+}
+
+std::string ComputeHmac(MessageDigest* digest, const std::string& key,
+                        const std::string& input) {
+  std::unique_ptr<char[]> output(new char[digest->Size()]);
+  ComputeHmac(digest, key.data(), key.size(),
+              input.data(), input.size(), output.get(), digest->Size());
+  return hex_encode(output.get(), digest->Size());
+}
+
+bool ComputeHmac(const std::string& alg, const std::string& key,
+                 const std::string& input, std::string* output) {
+  std::unique_ptr<MessageDigest> digest(MessageDigestFactory::Create(alg));
+  if (!digest) {
+    return false;
+  }
+  *output = ComputeHmac(digest.get(), key, input);
+  return true;
+}
+
+std::string ComputeHmac(const std::string& alg, const std::string& key,
+                        const std::string& input) {
+  std::string output;
+  ComputeHmac(alg, key, input, &output);
+  return output;
+}
+
+}  // namespace rtc
diff --git a/base/messagedigest.h b/base/messagedigest.h
index b73f907..5cfcb47 100644
--- a/base/messagedigest.h
+++ b/base/messagedigest.h
@@ -11,9 +11,99 @@
 #ifndef WEBRTC_BASE_MESSAGEDIGEST_H_
 #define WEBRTC_BASE_MESSAGEDIGEST_H_
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/messagedigest.h"
+namespace rtc {
+
+// Definitions for the digest algorithms.
+extern const char DIGEST_MD5[];
+extern const char DIGEST_SHA_1[];
+extern const char DIGEST_SHA_224[];
+extern const char DIGEST_SHA_256[];
+extern const char DIGEST_SHA_384[];
+extern const char DIGEST_SHA_512[];
+
+// A general class for computing hashes.
+class MessageDigest {
+ public:
+  enum { kMaxSize = 64 };  // Maximum known size (SHA-512)
+  virtual ~MessageDigest() {}
+  // Returns the digest output size (e.g. 16 bytes for MD5).
+  virtual size_t Size() const = 0;
+  // Updates the digest with |len| bytes from |buf|.
+  virtual void Update(const void* buf, size_t len) = 0;
+  // Outputs the digest value to |buf| with length |len|.
+  // Returns the number of bytes written, i.e., Size().
+  virtual size_t Finish(void* buf, size_t len) = 0;
+};
+
+// A factory class for creating digest objects.
+class MessageDigestFactory {
+ public:
+  static MessageDigest* Create(const std::string& alg);
+};
+
+// A whitelist of approved digest algorithms from RFC 4572 (FIPS 180).
+bool IsFips180DigestAlgorithm(const std::string& alg);
+
+// Functions to create hashes.
+
+// Computes the hash of |in_len| bytes of |input|, using the |digest| hash
+// implementation, and outputs the hash to the buffer |output|, which is
+// |out_len| bytes long. Returns the number of bytes written to |output| if
+// successful, or 0 if |out_len| was too small.
+size_t ComputeDigest(MessageDigest* digest, const void* input, size_t in_len,
+                     void* output, size_t out_len);
+// Like the previous function, but creates a digest implementation based on
+// the desired digest name |alg|, e.g. DIGEST_SHA_1. Returns 0 if there is no
+// digest with the given name.
+size_t ComputeDigest(const std::string& alg, const void* input, size_t in_len,
+                     void* output, size_t out_len);
+// Computes the hash of |input| using the |digest| hash implementation, and
+// returns it as a hex-encoded string.
+std::string ComputeDigest(MessageDigest* digest, const std::string& input);
+// Like the previous function, but creates a digest implementation based on
+// the desired digest name |alg|, e.g. DIGEST_SHA_1. Returns empty string if
+// there is no digest with the given name.
+std::string ComputeDigest(const std::string& alg, const std::string& input);
+// Like the previous function, but returns an explicit result code.
+bool ComputeDigest(const std::string& alg, const std::string& input,
+                   std::string* output);
+
+// Shorthand way to compute a hex-encoded hash using MD5.
+inline std::string MD5(const std::string& input) {
+  return ComputeDigest(DIGEST_MD5, input);
+}
+
+// Functions to compute RFC 2104 HMACs.
+
+// Computes the HMAC of |in_len| bytes of |input|, using the |digest| hash
+// implementation and |key_len| bytes of |key| to key the HMAC, and outputs
+// the HMAC to the buffer |output|, which is |out_len| bytes long. Returns the
+// number of bytes written to |output| if successful, or 0 if |out_len| was too
+// small.
+size_t ComputeHmac(MessageDigest* digest, const void* key, size_t key_len,
+                   const void* input, size_t in_len,
+                   void* output, size_t out_len);
+// Like the previous function, but creates a digest implementation based on
+// the desired digest name |alg|, e.g. DIGEST_SHA_1. Returns 0 if there is no
+// digest with the given name.
+size_t ComputeHmac(const std::string& alg, const void* key, size_t key_len,
+                   const void* input, size_t in_len,
+                   void* output, size_t out_len);
+// Computes the HMAC of |input| using the |digest| hash implementation and |key|
+// to key the HMAC, and returns it as a hex-encoded string.
+std::string ComputeHmac(MessageDigest* digest, const std::string& key,
+                        const std::string& input);
+// Like the previous function, but creates a digest implementation based on
+// the desired digest name |alg|, e.g. DIGEST_SHA_1. Returns empty string if
+// there is no digest with the given name.
+std::string ComputeHmac(const std::string& alg, const std::string& key,
+                        const std::string& input);
+// Like the previous function, but returns an explicit result code.
+bool ComputeHmac(const std::string& alg, const std::string& key,
+                 const std::string& input, std::string* output);
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_MESSAGEDIGEST_H_
diff --git a/base/messagedigest_unittest.cc b/base/messagedigest_unittest.cc
new file mode 100644
index 0000000..86cf688
--- /dev/null
+++ b/base/messagedigest_unittest.cc
@@ -0,0 +1,151 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/messagedigest.h"
+#include "webrtc/base/stringencode.h"
+
+namespace rtc {
+
+// Test vectors from RFC 1321.
+TEST(MessageDigestTest, TestMd5Digest) {
+  // Test the string versions of the APIs.
+  EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e",
+      ComputeDigest(DIGEST_MD5, ""));
+  EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72",
+      ComputeDigest(DIGEST_MD5, "abc"));
+  EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b",
+      ComputeDigest(DIGEST_MD5, "abcdefghijklmnopqrstuvwxyz"));
+
+  // Test the raw buffer versions of the APIs; also check output buffer size.
+  char output[16];
+  EXPECT_EQ(sizeof(output),
+      ComputeDigest(DIGEST_MD5, "abc", 3, output, sizeof(output)));
+  EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72",
+      hex_encode(output, sizeof(output)));
+  EXPECT_EQ(0U,
+      ComputeDigest(DIGEST_MD5, "abc", 3, output, sizeof(output) - 1));
+}
+
+// Test vectors from RFC 3174.
+TEST(MessageDigestTest, TestSha1Digest) {
+  // Test the string versions of the APIs.
+  EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709",
+      ComputeDigest(DIGEST_SHA_1, ""));
+  EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d",
+      ComputeDigest(DIGEST_SHA_1, "abc"));
+  EXPECT_EQ("84983e441c3bd26ebaae4aa1f95129e5e54670f1",
+      ComputeDigest(DIGEST_SHA_1,
+          "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"));
+
+  // Test the raw buffer versions of the APIs; also check output buffer size.
+  char output[20];
+  EXPECT_EQ(sizeof(output),
+      ComputeDigest(DIGEST_SHA_1, "abc", 3, output, sizeof(output)));
+  EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d",
+      hex_encode(output, sizeof(output)));
+  EXPECT_EQ(0U,
+      ComputeDigest(DIGEST_SHA_1, "abc", 3, output, sizeof(output) - 1));
+}
+
+// Test that we fail properly if a bad digest algorithm is specified.
+TEST(MessageDigestTest, TestBadDigest) {
+  std::string output;
+  EXPECT_FALSE(ComputeDigest("sha-9000", "abc", &output));
+  EXPECT_EQ("", ComputeDigest("sha-9000", "abc"));
+}
+
+// Test vectors from RFC 2202.
+TEST(MessageDigestTest, TestMd5Hmac) {
+  // Test the string versions of the APIs.
+  EXPECT_EQ("9294727a3638bb1c13f48ef8158bfc9d",
+      ComputeHmac(DIGEST_MD5, std::string(16, '\x0b'), "Hi There"));
+  EXPECT_EQ("750c783e6ab0b503eaa86e310a5db738",
+      ComputeHmac(DIGEST_MD5, "Jefe", "what do ya want for nothing?"));
+  EXPECT_EQ("56be34521d144c88dbb8c733f0e8b3f6",
+      ComputeHmac(DIGEST_MD5, std::string(16, '\xaa'),
+          std::string(50, '\xdd')));
+  EXPECT_EQ("697eaf0aca3a3aea3a75164746ffaa79",
+      ComputeHmac(DIGEST_MD5,
+          "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+          "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+          std::string(50, '\xcd')));
+  EXPECT_EQ("56461ef2342edc00f9bab995690efd4c",
+      ComputeHmac(DIGEST_MD5, std::string(16, '\x0c'),
+          "Test With Truncation"));
+  EXPECT_EQ("6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd",
+      ComputeHmac(DIGEST_MD5, std::string(80, '\xaa'),
+          "Test Using Larger Than Block-Size Key - Hash Key First"));
+  EXPECT_EQ("6f630fad67cda0ee1fb1f562db3aa53e",
+      ComputeHmac(DIGEST_MD5, std::string(80, '\xaa'),
+          "Test Using Larger Than Block-Size Key and Larger "
+          "Than One Block-Size Data"));
+
+  // Test the raw buffer versions of the APIs; also check output buffer size.
+  std::string key(16, '\x0b');
+  std::string input("Hi There");
+  char output[16];
+  EXPECT_EQ(sizeof(output),
+      ComputeHmac(DIGEST_MD5, key.c_str(), key.size(),
+          input.c_str(), input.size(), output, sizeof(output)));
+  EXPECT_EQ("9294727a3638bb1c13f48ef8158bfc9d",
+      hex_encode(output, sizeof(output)));
+  EXPECT_EQ(0U,
+      ComputeHmac(DIGEST_MD5, key.c_str(), key.size(),
+          input.c_str(), input.size(), output, sizeof(output) - 1));
+}
+
+// Test vectors from RFC 2202.
+TEST(MessageDigestTest, TestSha1Hmac) {
+  // Test the string versions of the APIs.
+  EXPECT_EQ("b617318655057264e28bc0b6fb378c8ef146be00",
+      ComputeHmac(DIGEST_SHA_1, std::string(20, '\x0b'), "Hi There"));
+  EXPECT_EQ("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
+      ComputeHmac(DIGEST_SHA_1, "Jefe", "what do ya want for nothing?"));
+  EXPECT_EQ("125d7342b9ac11cd91a39af48aa17b4f63f175d3",
+      ComputeHmac(DIGEST_SHA_1, std::string(20, '\xaa'),
+          std::string(50, '\xdd')));
+  EXPECT_EQ("4c9007f4026250c6bc8414f9bf50c86c2d7235da",
+      ComputeHmac(DIGEST_SHA_1,
+          "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+          "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+          std::string(50, '\xcd')));
+  EXPECT_EQ("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04",
+      ComputeHmac(DIGEST_SHA_1, std::string(20, '\x0c'),
+          "Test With Truncation"));
+  EXPECT_EQ("aa4ae5e15272d00e95705637ce8a3b55ed402112",
+      ComputeHmac(DIGEST_SHA_1, std::string(80, '\xaa'),
+          "Test Using Larger Than Block-Size Key - Hash Key First"));
+  EXPECT_EQ("e8e99d0f45237d786d6bbaa7965c7808bbff1a91",
+      ComputeHmac(DIGEST_SHA_1, std::string(80, '\xaa'),
+          "Test Using Larger Than Block-Size Key and Larger "
+          "Than One Block-Size Data"));
+
+  // Test the raw buffer versions of the APIs; also check output buffer size.
+  std::string key(20, '\x0b');
+  std::string input("Hi There");
+  char output[20];
+  EXPECT_EQ(sizeof(output),
+      ComputeHmac(DIGEST_SHA_1, key.c_str(), key.size(),
+          input.c_str(), input.size(), output, sizeof(output)));
+  EXPECT_EQ("b617318655057264e28bc0b6fb378c8ef146be00",
+      hex_encode(output, sizeof(output)));
+  EXPECT_EQ(0U,
+      ComputeHmac(DIGEST_SHA_1, key.c_str(), key.size(),
+          input.c_str(), input.size(), output, sizeof(output) - 1));
+}
+
+TEST(MessageDigestTest, TestBadHmac) {
+  std::string output;
+  EXPECT_FALSE(ComputeHmac("sha-9000", "key", "abc", &output));
+  EXPECT_EQ("", ComputeHmac("sha-9000", "key", "abc"));
+}
+
+}  // namespace rtc
diff --git a/base/messagehandler.cc b/base/messagehandler.cc
new file mode 100644
index 0000000..be5bb7f
--- /dev/null
+++ b/base/messagehandler.cc
@@ -0,0 +1,20 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/messagehandler.h"
+#include "webrtc/base/messagequeue.h"
+
+namespace rtc {
+
+MessageHandler::~MessageHandler() {
+  MessageQueueManager::Clear(this);
+}
+
+} // namespace rtc
diff --git a/base/messagehandler.h b/base/messagehandler.h
index 943d0d7..72c0dc6 100644
--- a/base/messagehandler.h
+++ b/base/messagehandler.h
@@ -11,9 +11,65 @@
 #ifndef WEBRTC_BASE_MESSAGEHANDLER_H_
 #define WEBRTC_BASE_MESSAGEHANDLER_H_
 
+#include <memory>
+#include <utility>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/messagehandler.h"
+#include "webrtc/base/constructormagic.h"
+
+namespace rtc {
+
+struct Message;
+
+// Messages get dispatched to a MessageHandler
+
+class MessageHandler {
+ public:
+  virtual ~MessageHandler();
+  virtual void OnMessage(Message* msg) = 0;
+
+ protected:
+  MessageHandler() {}
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(MessageHandler);
+};
+
+// Helper class to facilitate executing a functor on a thread.
+template <class ReturnT, class FunctorT>
+class FunctorMessageHandler : public MessageHandler {
+ public:
+  explicit FunctorMessageHandler(const FunctorT& functor)
+      : functor_(functor) {}
+  virtual void OnMessage(Message* msg) {
+    result_ = functor_();
+  }
+  const ReturnT& result() const { return result_; }
+
+  // Returns moved result. Should not call result() or MoveResult() again
+  // after this.
+  ReturnT MoveResult() { return std::move(result_); }
+
+ private:
+  FunctorT functor_;
+  ReturnT result_;
+};
+
+// Specialization for ReturnT of void.
+template <class FunctorT>
+class FunctorMessageHandler<void, FunctorT> : public MessageHandler {
+ public:
+  explicit FunctorMessageHandler(const FunctorT& functor)
+      : functor_(functor) {}
+  virtual void OnMessage(Message* msg) {
+    functor_();
+  }
+  void result() const {}
+  void MoveResult() {}
+
+ private:
+  FunctorT functor_;
+};
+
+} // namespace rtc
 
 #endif // WEBRTC_BASE_MESSAGEHANDLER_H_
diff --git a/base/messagequeue.cc b/base/messagequeue.cc
new file mode 100644
index 0000000..cafb70b
--- /dev/null
+++ b/base/messagequeue.cc
@@ -0,0 +1,532 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include <algorithm>
+
+#include "webrtc/base/atomicops.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/messagequeue.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/trace_event.h"
+
+namespace rtc {
+namespace {
+
+const int kMaxMsgLatency = 150;  // 150 ms
+const int kSlowDispatchLoggingThreshold = 50;  // 50 ms
+
+class SCOPED_LOCKABLE DebugNonReentrantCritScope {
+ public:
+  DebugNonReentrantCritScope(const CriticalSection* cs, bool* locked)
+      EXCLUSIVE_LOCK_FUNCTION(cs)
+      : cs_(cs), locked_(locked) {
+    cs_->Enter();
+    RTC_DCHECK(!*locked_);
+    *locked_ = true;
+  }
+
+  ~DebugNonReentrantCritScope() UNLOCK_FUNCTION() {
+    *locked_ = false;
+    cs_->Leave();
+  }
+
+ private:
+  const CriticalSection* const cs_;
+  bool* locked_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(DebugNonReentrantCritScope);
+};
+}  // namespace
+
+//------------------------------------------------------------------
+// MessageQueueManager
+
+MessageQueueManager* MessageQueueManager::instance_ = nullptr;
+
+MessageQueueManager* MessageQueueManager::Instance() {
+  // Note: This is not thread safe, but it is first called before threads are
+  // spawned.
+  if (!instance_)
+    instance_ = new MessageQueueManager;
+  return instance_;
+}
+
+bool MessageQueueManager::IsInitialized() {
+  return instance_ != nullptr;
+}
+
+MessageQueueManager::MessageQueueManager() : locked_(false) {}
+
+MessageQueueManager::~MessageQueueManager() {
+}
+
+void MessageQueueManager::Add(MessageQueue *message_queue) {
+  return Instance()->AddInternal(message_queue);
+}
+void MessageQueueManager::AddInternal(MessageQueue *message_queue) {
+  DebugNonReentrantCritScope cs(&crit_, &locked_);
+  message_queues_.push_back(message_queue);
+}
+
+void MessageQueueManager::Remove(MessageQueue *message_queue) {
+  // If there isn't a message queue manager instance, then there isn't a queue
+  // to remove.
+  if (!instance_) return;
+  return Instance()->RemoveInternal(message_queue);
+}
+void MessageQueueManager::RemoveInternal(MessageQueue *message_queue) {
+  // If this is the last MessageQueue, destroy the manager as well so that
+  // we don't leak this object at program shutdown. As mentioned above, this is
+  // not thread-safe, but this should only happen at program termination (when
+  // the ThreadManager is destroyed, and threads are no longer active).
+  bool destroy = false;
+  {
+    DebugNonReentrantCritScope cs(&crit_, &locked_);
+    std::vector<MessageQueue *>::iterator iter;
+    iter = std::find(message_queues_.begin(), message_queues_.end(),
+                     message_queue);
+    if (iter != message_queues_.end()) {
+      message_queues_.erase(iter);
+    }
+    destroy = message_queues_.empty();
+  }
+  if (destroy) {
+    instance_ = nullptr;
+    delete this;
+  }
+}
+
+void MessageQueueManager::Clear(MessageHandler *handler) {
+  // If there isn't a message queue manager instance, then there aren't any
+  // queues to remove this handler from.
+  if (!instance_) return;
+  return Instance()->ClearInternal(handler);
+}
+void MessageQueueManager::ClearInternal(MessageHandler *handler) {
+  DebugNonReentrantCritScope cs(&crit_, &locked_);
+  std::vector<MessageQueue *>::iterator iter;
+  for (iter = message_queues_.begin(); iter != message_queues_.end(); iter++)
+    (*iter)->Clear(handler);
+}
+
+void MessageQueueManager::ProcessAllMessageQueues() {
+  if (!instance_) {
+    return;
+  }
+  return Instance()->ProcessAllMessageQueuesInternal();
+}
+
+void MessageQueueManager::ProcessAllMessageQueuesInternal() {
+  // This works by posting a delayed message at the current time and waiting
+  // for it to be dispatched on all queues, which will ensure that all messages
+  // that came before it were also dispatched.
+  volatile int queues_not_done = 0;
+
+  // This class is used so that whether the posted message is processed, or the
+  // message queue is simply cleared, queues_not_done gets decremented.
+  class ScopedIncrement : public MessageData {
+   public:
+    ScopedIncrement(volatile int* value) : value_(value) {
+      AtomicOps::Increment(value_);
+    }
+    ~ScopedIncrement() override { AtomicOps::Decrement(value_); }
+
+   private:
+    volatile int* value_;
+  };
+
+  {
+    DebugNonReentrantCritScope cs(&crit_, &locked_);
+    for (MessageQueue* queue : message_queues_) {
+      if (!queue->IsProcessingMessages()) {
+        // If the queue is not processing messages, it can
+        // be ignored. If we tried to post a message to it, it would be dropped
+        // or ignored.
+        continue;
+      }
+      queue->PostDelayed(RTC_FROM_HERE, 0, nullptr, MQID_DISPOSE,
+                         new ScopedIncrement(&queues_not_done));
+    }
+  }
+  // Note: One of the message queues may have been on this thread, which is why
+  // we can't synchronously wait for queues_not_done to go to 0; we need to
+  // process messages as well.
+  while (AtomicOps::AcquireLoad(&queues_not_done) > 0) {
+    rtc::Thread::Current()->ProcessMessages(0);
+  }
+}
+
+//------------------------------------------------------------------
+// MessageQueue
+MessageQueue::MessageQueue(SocketServer* ss, bool init_queue)
+    : fPeekKeep_(false),
+      dmsgq_next_num_(0),
+      fInitialized_(false),
+      fDestroyed_(false),
+      stop_(0),
+      ss_(ss) {
+  RTC_DCHECK(ss);
+  // Currently, MessageQueue holds a socket server, and is the base class for
+  // Thread.  It seems like it makes more sense for Thread to hold the socket
+  // server, and provide it to the MessageQueue, since the Thread controls
+  // the I/O model, and MQ is agnostic to those details.  Anyway, this causes
+  // messagequeue_unittest to depend on network libraries... yuck.
+  ss_->SetMessageQueue(this);
+  if (init_queue) {
+    DoInit();
+  }
+}
+
+MessageQueue::MessageQueue(std::unique_ptr<SocketServer> ss, bool init_queue)
+    : MessageQueue(ss.get(), init_queue) {
+  own_ss_ = std::move(ss);
+}
+
+MessageQueue::~MessageQueue() {
+  DoDestroy();
+}
+
+void MessageQueue::DoInit() {
+  if (fInitialized_) {
+    return;
+  }
+
+  fInitialized_ = true;
+  MessageQueueManager::Add(this);
+}
+
+void MessageQueue::DoDestroy() {
+  if (fDestroyed_) {
+    return;
+  }
+
+  fDestroyed_ = true;
+  // The signal is done from here to ensure
+  // that it always gets called when the queue
+  // is going away.
+  SignalQueueDestroyed();
+  MessageQueueManager::Remove(this);
+  Clear(nullptr);
+
+  if (ss_) {
+    ss_->SetMessageQueue(nullptr);
+  }
+}
+
+SocketServer* MessageQueue::socketserver() {
+  return ss_;
+}
+
+void MessageQueue::WakeUpSocketServer() {
+  ss_->WakeUp();
+}
+
+void MessageQueue::Quit() {
+  AtomicOps::ReleaseStore(&stop_, 1);
+  WakeUpSocketServer();
+}
+
+bool MessageQueue::IsQuitting() {
+  return AtomicOps::AcquireLoad(&stop_) != 0;
+}
+
+bool MessageQueue::IsProcessingMessages() {
+  return !IsQuitting();
+}
+
+void MessageQueue::Restart() {
+  AtomicOps::ReleaseStore(&stop_, 0);
+}
+
+bool MessageQueue::Peek(Message *pmsg, int cmsWait) {
+  if (fPeekKeep_) {
+    *pmsg = msgPeek_;
+    return true;
+  }
+  if (!Get(pmsg, cmsWait))
+    return false;
+  msgPeek_ = *pmsg;
+  fPeekKeep_ = true;
+  return true;
+}
+
+bool MessageQueue::Get(Message *pmsg, int cmsWait, bool process_io) {
+  // Return and clear peek if present
+  // Always return the peek if it exists so there is Peek/Get symmetry
+
+  if (fPeekKeep_) {
+    *pmsg = msgPeek_;
+    fPeekKeep_ = false;
+    return true;
+  }
+
+  // Get w/wait + timer scan / dispatch + socket / event multiplexer dispatch
+
+  int64_t cmsTotal = cmsWait;
+  int64_t cmsElapsed = 0;
+  int64_t msStart = TimeMillis();
+  int64_t msCurrent = msStart;
+  while (true) {
+    // Check for sent messages
+    ReceiveSends();
+
+    // Check for posted events
+    int64_t cmsDelayNext = kForever;
+    bool first_pass = true;
+    while (true) {
+      // All queue operations need to be locked, but nothing else in this loop
+      // (specifically handling disposed message) can happen inside the crit.
+      // Otherwise, disposed MessageHandlers will cause deadlocks.
+      {
+        CritScope cs(&crit_);
+        // On the first pass, check for delayed messages that have been
+        // triggered and calculate the next trigger time.
+        if (first_pass) {
+          first_pass = false;
+          while (!dmsgq_.empty()) {
+            if (msCurrent < dmsgq_.top().msTrigger_) {
+              cmsDelayNext = TimeDiff(dmsgq_.top().msTrigger_, msCurrent);
+              break;
+            }
+            msgq_.push_back(dmsgq_.top().msg_);
+            dmsgq_.pop();
+          }
+        }
+        // Pull a message off the message queue, if available.
+        if (msgq_.empty()) {
+          break;
+        } else {
+          *pmsg = msgq_.front();
+          msgq_.pop_front();
+        }
+      }  // crit_ is released here.
+
+      // Log a warning for time-sensitive messages that we're late to deliver.
+      if (pmsg->ts_sensitive) {
+        int64_t delay = TimeDiff(msCurrent, pmsg->ts_sensitive);
+        if (delay > 0) {
+          LOG_F(LS_WARNING) << "id: " << pmsg->message_id << "  delay: "
+                            << (delay + kMaxMsgLatency) << "ms";
+        }
+      }
+      // If this was a dispose message, delete it and skip it.
+      if (MQID_DISPOSE == pmsg->message_id) {
+        RTC_DCHECK(nullptr == pmsg->phandler);
+        delete pmsg->pdata;
+        *pmsg = Message();
+        continue;
+      }
+      return true;
+    }
+
+    if (IsQuitting())
+      break;
+
+    // Which is shorter, the delay wait or the asked wait?
+
+    int64_t cmsNext;
+    if (cmsWait == kForever) {
+      cmsNext = cmsDelayNext;
+    } else {
+      cmsNext = std::max<int64_t>(0, cmsTotal - cmsElapsed);
+      if ((cmsDelayNext != kForever) && (cmsDelayNext < cmsNext))
+        cmsNext = cmsDelayNext;
+    }
+
+    {
+      // Wait and multiplex in the meantime
+      if (!ss_->Wait(static_cast<int>(cmsNext), process_io))
+        return false;
+    }
+
+    // If the specified timeout expired, return
+
+    msCurrent = TimeMillis();
+    cmsElapsed = TimeDiff(msCurrent, msStart);
+    if (cmsWait != kForever) {
+      if (cmsElapsed >= cmsWait)
+        return false;
+    }
+  }
+  return false;
+}
+
+void MessageQueue::ReceiveSends() {
+}
+
+void MessageQueue::Post(const Location& posted_from,
+                        MessageHandler* phandler,
+                        uint32_t id,
+                        MessageData* pdata,
+                        bool time_sensitive) {
+  if (IsQuitting())
+    return;
+
+  // Keep thread safe
+  // Add the message to the end of the queue
+  // Signal for the multiplexer to return
+
+  {
+    CritScope cs(&crit_);
+    Message msg;
+    msg.posted_from = posted_from;
+    msg.phandler = phandler;
+    msg.message_id = id;
+    msg.pdata = pdata;
+    if (time_sensitive) {
+      msg.ts_sensitive = TimeMillis() + kMaxMsgLatency;
+    }
+    msgq_.push_back(msg);
+  }
+  WakeUpSocketServer();
+}
+
+void MessageQueue::PostDelayed(const Location& posted_from,
+                               int cmsDelay,
+                               MessageHandler* phandler,
+                               uint32_t id,
+                               MessageData* pdata) {
+  return DoDelayPost(posted_from, cmsDelay, TimeAfter(cmsDelay), phandler, id,
+                     pdata);
+}
+
+void MessageQueue::PostAt(const Location& posted_from,
+                          uint32_t tstamp,
+                          MessageHandler* phandler,
+                          uint32_t id,
+                          MessageData* pdata) {
+  // This should work even if it is used (unexpectedly).
+  int64_t delay = static_cast<uint32_t>(TimeMillis()) - tstamp;
+  return DoDelayPost(posted_from, delay, tstamp, phandler, id, pdata);
+}
+
+void MessageQueue::PostAt(const Location& posted_from,
+                          int64_t tstamp,
+                          MessageHandler* phandler,
+                          uint32_t id,
+                          MessageData* pdata) {
+  return DoDelayPost(posted_from, TimeUntil(tstamp), tstamp, phandler, id,
+                     pdata);
+}
+
+void MessageQueue::DoDelayPost(const Location& posted_from,
+                               int64_t cmsDelay,
+                               int64_t tstamp,
+                               MessageHandler* phandler,
+                               uint32_t id,
+                               MessageData* pdata) {
+  if (IsQuitting()) {
+    return;
+  }
+
+  // Keep thread safe
+  // Add to the priority queue. Gets sorted soonest first.
+  // Signal for the multiplexer to return.
+
+  {
+    CritScope cs(&crit_);
+    Message msg;
+    msg.posted_from = posted_from;
+    msg.phandler = phandler;
+    msg.message_id = id;
+    msg.pdata = pdata;
+    DelayedMessage dmsg(cmsDelay, tstamp, dmsgq_next_num_, msg);
+    dmsgq_.push(dmsg);
+    // If this message queue processes 1 message every millisecond for 50 days,
+    // we will wrap this number.  Even then, only messages with identical times
+    // will be misordered, and then only briefly.  This is probably ok.
+    ++dmsgq_next_num_;
+    RTC_DCHECK_NE(0, dmsgq_next_num_);
+  }
+  WakeUpSocketServer();
+}
+
+int MessageQueue::GetDelay() {
+  CritScope cs(&crit_);
+
+  if (!msgq_.empty())
+    return 0;
+
+  if (!dmsgq_.empty()) {
+    int delay = TimeUntil(dmsgq_.top().msTrigger_);
+    if (delay < 0)
+      delay = 0;
+    return delay;
+  }
+
+  return kForever;
+}
+
+void MessageQueue::Clear(MessageHandler* phandler,
+                         uint32_t id,
+                         MessageList* removed) {
+  CritScope cs(&crit_);
+
+  // Remove messages with phandler
+
+  if (fPeekKeep_ && msgPeek_.Match(phandler, id)) {
+    if (removed) {
+      removed->push_back(msgPeek_);
+    } else {
+      delete msgPeek_.pdata;
+    }
+    fPeekKeep_ = false;
+  }
+
+  // Remove from ordered message queue
+
+  for (MessageList::iterator it = msgq_.begin(); it != msgq_.end();) {
+    if (it->Match(phandler, id)) {
+      if (removed) {
+        removed->push_back(*it);
+      } else {
+        delete it->pdata;
+      }
+      it = msgq_.erase(it);
+    } else {
+      ++it;
+    }
+  }
+
+  // Remove from priority queue. Not directly iterable, so use this approach
+
+  PriorityQueue::container_type::iterator new_end = dmsgq_.container().begin();
+  for (PriorityQueue::container_type::iterator it = new_end;
+       it != dmsgq_.container().end(); ++it) {
+    if (it->msg_.Match(phandler, id)) {
+      if (removed) {
+        removed->push_back(it->msg_);
+      } else {
+        delete it->msg_.pdata;
+      }
+    } else {
+      *new_end++ = *it;
+    }
+  }
+  dmsgq_.container().erase(new_end, dmsgq_.container().end());
+  dmsgq_.reheap();
+}
+
+void MessageQueue::Dispatch(Message *pmsg) {
+  TRACE_EVENT2("webrtc", "MessageQueue::Dispatch", "src_file_and_line",
+               pmsg->posted_from.file_and_line(), "src_func",
+               pmsg->posted_from.function_name());
+  int64_t start_time = TimeMillis();
+  pmsg->phandler->OnMessage(pmsg);
+  int64_t end_time = TimeMillis();
+  int64_t diff = TimeDiff(end_time, start_time);
+  if (diff >= kSlowDispatchLoggingThreshold) {
+    LOG(LS_INFO) << "Message took " << diff << "ms to dispatch. Posted from: "
+                 << pmsg->posted_from.ToString();
+  }
+}
+
+}  // namespace rtc
diff --git a/base/messagequeue.h b/base/messagequeue.h
index 353a4b7..e39c9f9 100644
--- a/base/messagequeue.h
+++ b/base/messagequeue.h
@@ -11,9 +11,317 @@
 #ifndef WEBRTC_BASE_MESSAGEQUEUE_H_
 #define WEBRTC_BASE_MESSAGEQUEUE_H_
 
+#include <string.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/messagequeue.h"
+#include <algorithm>
+#include <list>
+#include <memory>
+#include <queue>
+#include <utility>
+#include <vector>
+
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/location.h"
+#include "webrtc/base/messagehandler.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/socketserver.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/base/thread_annotations.h"
+
+namespace rtc {
+
+struct Message;
+class MessageQueue;
+
+// MessageQueueManager does cleanup of of message queues
+
+class MessageQueueManager {
+ public:
+  static void Add(MessageQueue *message_queue);
+  static void Remove(MessageQueue *message_queue);
+  static void Clear(MessageHandler *handler);
+
+  // For testing purposes, we expose whether or not the MessageQueueManager
+  // instance has been initialized. It has no other use relative to the rest of
+  // the functions of this class, which auto-initialize the underlying
+  // MessageQueueManager instance when necessary.
+  static bool IsInitialized();
+
+  // Mainly for testing purposes, for use with a simulated clock.
+  // Ensures that all message queues have processed delayed messages
+  // up until the current point in time.
+  static void ProcessAllMessageQueues();
+
+ private:
+  static MessageQueueManager* Instance();
+
+  MessageQueueManager();
+  ~MessageQueueManager();
+
+  void AddInternal(MessageQueue *message_queue);
+  void RemoveInternal(MessageQueue *message_queue);
+  void ClearInternal(MessageHandler *handler);
+  void ProcessAllMessageQueuesInternal();
+
+  static MessageQueueManager* instance_;
+  // This list contains all live MessageQueues.
+  std::vector<MessageQueue*> message_queues_ GUARDED_BY(crit_);
+
+  // Acquire this with DebugNonReentrantCritScope.
+  CriticalSection crit_;
+  bool locked_ GUARDED_BY(crit_);
+};
+
+// Derive from this for specialized data
+// App manages lifetime, except when messages are purged
+
+class MessageData {
+ public:
+  MessageData() {}
+  virtual ~MessageData() {}
+};
+
+template <class T>
+class TypedMessageData : public MessageData {
+ public:
+  explicit TypedMessageData(const T& data) : data_(data) { }
+  const T& data() const { return data_; }
+  T& data() { return data_; }
+ private:
+  T data_;
+};
+
+// Like TypedMessageData, but for pointers that require a delete.
+template <class T>
+class ScopedMessageData : public MessageData {
+ public:
+  explicit ScopedMessageData(std::unique_ptr<T> data)
+      : data_(std::move(data)) {}
+  // Deprecated.
+  // TODO(deadbeef): Remove this once downstream applications stop using it.
+  explicit ScopedMessageData(T* data) : data_(data) {}
+  // Deprecated.
+  // TODO(deadbeef): Returning a reference to a unique ptr? Why. Get rid of
+  // this once downstream applications stop using it, then rename inner_data to
+  // just data.
+  const std::unique_ptr<T>& data() const { return data_; }
+  std::unique_ptr<T>& data() { return data_; }
+
+  const T& inner_data() const { return *data_; }
+  T& inner_data() { return *data_; }
+
+ private:
+  std::unique_ptr<T> data_;
+};
+
+// Like ScopedMessageData, but for reference counted pointers.
+template <class T>
+class ScopedRefMessageData : public MessageData {
+ public:
+  explicit ScopedRefMessageData(T* data) : data_(data) { }
+  const scoped_refptr<T>& data() const { return data_; }
+  scoped_refptr<T>& data() { return data_; }
+ private:
+  scoped_refptr<T> data_;
+};
+
+template<class T>
+inline MessageData* WrapMessageData(const T& data) {
+  return new TypedMessageData<T>(data);
+}
+
+template<class T>
+inline const T& UseMessageData(MessageData* data) {
+  return static_cast< TypedMessageData<T>* >(data)->data();
+}
+
+template<class T>
+class DisposeData : public MessageData {
+ public:
+  explicit DisposeData(T* data) : data_(data) { }
+  virtual ~DisposeData() { delete data_; }
+ private:
+  T* data_;
+};
+
+const uint32_t MQID_ANY = static_cast<uint32_t>(-1);
+const uint32_t MQID_DISPOSE = static_cast<uint32_t>(-2);
+
+// No destructor
+
+struct Message {
+  Message()
+      : phandler(nullptr), message_id(0), pdata(nullptr), ts_sensitive(0) {}
+  inline bool Match(MessageHandler* handler, uint32_t id) const {
+    return (handler == nullptr || handler == phandler) &&
+           (id == MQID_ANY || id == message_id);
+  }
+  Location posted_from;
+  MessageHandler *phandler;
+  uint32_t message_id;
+  MessageData *pdata;
+  int64_t ts_sensitive;
+};
+
+typedef std::list<Message> MessageList;
+
+// DelayedMessage goes into a priority queue, sorted by trigger time.  Messages
+// with the same trigger time are processed in num_ (FIFO) order.
+
+class DelayedMessage {
+ public:
+  DelayedMessage(int64_t delay,
+                 int64_t trigger,
+                 uint32_t num,
+                 const Message& msg)
+      : cmsDelay_(delay), msTrigger_(trigger), num_(num), msg_(msg) {}
+
+  bool operator< (const DelayedMessage& dmsg) const {
+    return (dmsg.msTrigger_ < msTrigger_)
+           || ((dmsg.msTrigger_ == msTrigger_) && (dmsg.num_ < num_));
+  }
+
+  int64_t cmsDelay_;  // for debugging
+  int64_t msTrigger_;
+  uint32_t num_;
+  Message msg_;
+};
+
+class MessageQueue {
+ public:
+  static const int kForever = -1;
+
+  // Create a new MessageQueue and optionally assign it to the passed
+  // SocketServer. Subclasses that override Clear should pass false for
+  // init_queue and call DoInit() from their constructor to prevent races
+  // with the MessageQueueManager using the object while the vtable is still
+  // being created.
+  MessageQueue(SocketServer* ss, bool init_queue);
+  MessageQueue(std::unique_ptr<SocketServer> ss, bool init_queue);
+
+  // NOTE: SUBCLASSES OF MessageQueue THAT OVERRIDE Clear MUST CALL
+  // DoDestroy() IN THEIR DESTRUCTORS! This is required to avoid a data race
+  // between the destructor modifying the vtable, and the MessageQueueManager
+  // calling Clear on the object from a different thread.
+  virtual ~MessageQueue();
+
+  SocketServer* socketserver();
+
+  // Note: The behavior of MessageQueue has changed.  When a MQ is stopped,
+  // futher Posts and Sends will fail.  However, any pending Sends and *ready*
+  // Posts (as opposed to unexpired delayed Posts) will be delivered before
+  // Get (or Peek) returns false.  By guaranteeing delivery of those messages,
+  // we eliminate the race condition when an MessageHandler and MessageQueue
+  // may be destroyed independently of each other.
+  virtual void Quit();
+  virtual bool IsQuitting();
+  virtual void Restart();
+  // Not all message queues actually process messages (such as SignalThread).
+  // In those cases, it's important to know, before posting, that it won't be
+  // Processed.  Normally, this would be true until IsQuitting() is true.
+  virtual bool IsProcessingMessages();
+
+  // Get() will process I/O until:
+  //  1) A message is available (returns true)
+  //  2) cmsWait seconds have elapsed (returns false)
+  //  3) Stop() is called (returns false)
+  virtual bool Get(Message *pmsg, int cmsWait = kForever,
+                   bool process_io = true);
+  virtual bool Peek(Message *pmsg, int cmsWait = 0);
+  virtual void Post(const Location& posted_from,
+                    MessageHandler* phandler,
+                    uint32_t id = 0,
+                    MessageData* pdata = nullptr,
+                    bool time_sensitive = false);
+  virtual void PostDelayed(const Location& posted_from,
+                           int cmsDelay,
+                           MessageHandler* phandler,
+                           uint32_t id = 0,
+                           MessageData* pdata = nullptr);
+  virtual void PostAt(const Location& posted_from,
+                      int64_t tstamp,
+                      MessageHandler* phandler,
+                      uint32_t id = 0,
+                      MessageData* pdata = nullptr);
+  // TODO(honghaiz): Remove this when all the dependencies are removed.
+  virtual void PostAt(const Location& posted_from,
+                      uint32_t tstamp,
+                      MessageHandler* phandler,
+                      uint32_t id = 0,
+                      MessageData* pdata = nullptr);
+  virtual void Clear(MessageHandler* phandler,
+                     uint32_t id = MQID_ANY,
+                     MessageList* removed = nullptr);
+  virtual void Dispatch(Message *pmsg);
+  virtual void ReceiveSends();
+
+  // Amount of time until the next message can be retrieved
+  virtual int GetDelay();
+
+  bool empty() const { return size() == 0u; }
+  size_t size() const {
+    CritScope cs(&crit_);  // msgq_.size() is not thread safe.
+    return msgq_.size() + dmsgq_.size() + (fPeekKeep_ ? 1u : 0u);
+  }
+
+  // Internally posts a message which causes the doomed object to be deleted
+  template<class T> void Dispose(T* doomed) {
+    if (doomed) {
+      Post(RTC_FROM_HERE, nullptr, MQID_DISPOSE, new DisposeData<T>(doomed));
+    }
+  }
+
+  // When this signal is sent out, any references to this queue should
+  // no longer be used.
+  sigslot::signal0<> SignalQueueDestroyed;
+
+ protected:
+  class PriorityQueue : public std::priority_queue<DelayedMessage> {
+   public:
+    container_type& container() { return c; }
+    void reheap() { make_heap(c.begin(), c.end(), comp); }
+  };
+
+  void DoDelayPost(const Location& posted_from,
+                   int64_t cmsDelay,
+                   int64_t tstamp,
+                   MessageHandler* phandler,
+                   uint32_t id,
+                   MessageData* pdata);
+
+  // Perform initialization, subclasses must call this from their constructor
+  // if false was passed as init_queue to the MessageQueue constructor.
+  void DoInit();
+
+  // Perform cleanup, subclasses that override Clear must call this from the
+  // destructor.
+  void DoDestroy();
+
+  void WakeUpSocketServer();
+
+  bool fPeekKeep_;
+  Message msgPeek_;
+  MessageList msgq_ GUARDED_BY(crit_);
+  PriorityQueue dmsgq_ GUARDED_BY(crit_);
+  uint32_t dmsgq_next_num_ GUARDED_BY(crit_);
+  CriticalSection crit_;
+  bool fInitialized_;
+  bool fDestroyed_;
+
+ private:
+  volatile int stop_;
+
+  // The SocketServer might not be owned by MessageQueue.
+  SocketServer* const ss_;
+  // Used if SocketServer ownership lies with |this|.
+  std::unique_ptr<SocketServer> own_ss_;
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(MessageQueue);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_MESSAGEQUEUE_H_
diff --git a/base/messagequeue_unittest.cc b/base/messagequeue_unittest.cc
new file mode 100644
index 0000000..9652bb3
--- /dev/null
+++ b/base/messagequeue_unittest.cc
@@ -0,0 +1,217 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/messagequeue.h"
+
+#include <functional>
+
+#include "webrtc/base/atomicops.h"
+#include "webrtc/base/bind.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/base/nullsocketserver.h"
+
+using namespace rtc;
+
+class MessageQueueTest: public testing::Test, public MessageQueue {
+ public:
+  MessageQueueTest() : MessageQueue(SocketServer::CreateDefault(), true) {}
+  bool IsLocked_Worker() {
+    if (!crit_.TryEnter()) {
+      return true;
+    }
+    crit_.Leave();
+    return false;
+  }
+  bool IsLocked() {
+    // We have to do this on a worker thread, or else the TryEnter will
+    // succeed, since our critical sections are reentrant.
+    Thread worker;
+    worker.Start();
+    return worker.Invoke<bool>(
+        RTC_FROM_HERE, rtc::Bind(&MessageQueueTest::IsLocked_Worker, this));
+  }
+};
+
+struct DeletedLockChecker {
+  DeletedLockChecker(MessageQueueTest* test, bool* was_locked, bool* deleted)
+      : test(test), was_locked(was_locked), deleted(deleted) { }
+  ~DeletedLockChecker() {
+    *deleted = true;
+    *was_locked = test->IsLocked();
+  }
+  MessageQueueTest* test;
+  bool* was_locked;
+  bool* deleted;
+};
+
+static void DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(
+    MessageQueue* q) {
+  EXPECT_TRUE(q != nullptr);
+  int64_t now = TimeMillis();
+  q->PostAt(RTC_FROM_HERE, now, nullptr, 3);
+  q->PostAt(RTC_FROM_HERE, now - 2, nullptr, 0);
+  q->PostAt(RTC_FROM_HERE, now - 1, nullptr, 1);
+  q->PostAt(RTC_FROM_HERE, now, nullptr, 4);
+  q->PostAt(RTC_FROM_HERE, now - 1, nullptr, 2);
+
+  Message msg;
+  for (size_t i=0; i<5; ++i) {
+    memset(&msg, 0, sizeof(msg));
+    EXPECT_TRUE(q->Get(&msg, 0));
+    EXPECT_EQ(i, msg.message_id);
+  }
+
+  EXPECT_FALSE(q->Get(&msg, 0));  // No more messages
+}
+
+TEST_F(MessageQueueTest,
+       DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder) {
+  MessageQueue q(SocketServer::CreateDefault(), true);
+  DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(&q);
+
+  NullSocketServer nullss;
+  MessageQueue q_nullss(&nullss, true);
+  DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(&q_nullss);
+}
+
+TEST_F(MessageQueueTest, DisposeNotLocked) {
+  bool was_locked = true;
+  bool deleted = false;
+  DeletedLockChecker* d = new DeletedLockChecker(this, &was_locked, &deleted);
+  Dispose(d);
+  Message msg;
+  EXPECT_FALSE(Get(&msg, 0));
+  EXPECT_TRUE(deleted);
+  EXPECT_FALSE(was_locked);
+}
+
+class DeletedMessageHandler : public MessageHandler {
+ public:
+  explicit DeletedMessageHandler(bool* deleted) : deleted_(deleted) { }
+  ~DeletedMessageHandler() {
+    *deleted_ = true;
+  }
+  void OnMessage(Message* msg) { }
+ private:
+  bool* deleted_;
+};
+
+TEST_F(MessageQueueTest, DiposeHandlerWithPostedMessagePending) {
+  bool deleted = false;
+  DeletedMessageHandler *handler = new DeletedMessageHandler(&deleted);
+  // First, post a dispose.
+  Dispose(handler);
+  // Now, post a message, which should *not* be returned by Get().
+  Post(RTC_FROM_HERE, handler, 1);
+  Message msg;
+  EXPECT_FALSE(Get(&msg, 0));
+  EXPECT_TRUE(deleted);
+}
+
+struct UnwrapMainThreadScope {
+  UnwrapMainThreadScope() : rewrap_(Thread::Current() != nullptr) {
+    if (rewrap_) ThreadManager::Instance()->UnwrapCurrentThread();
+  }
+  ~UnwrapMainThreadScope() {
+    if (rewrap_) ThreadManager::Instance()->WrapCurrentThread();
+  }
+ private:
+  bool rewrap_;
+};
+
+TEST(MessageQueueManager, Clear) {
+  UnwrapMainThreadScope s;
+  if (MessageQueueManager::IsInitialized()) {
+    LOG(LS_INFO) << "Unable to run MessageQueueManager::Clear test, since the "
+                 << "MessageQueueManager was already initialized by some "
+                 << "other test in this run.";
+    return;
+  }
+  bool deleted = false;
+  DeletedMessageHandler* handler = new DeletedMessageHandler(&deleted);
+  delete handler;
+  EXPECT_TRUE(deleted);
+  EXPECT_FALSE(MessageQueueManager::IsInitialized());
+}
+
+// Ensure that ProcessAllMessageQueues does its essential function; process
+// all messages (both delayed and non delayed) up until the current time, on
+// all registered message queues.
+TEST(MessageQueueManager, ProcessAllMessageQueues) {
+  Event entered_process_all_message_queues(true, false);
+  Thread a;
+  Thread b;
+  a.Start();
+  b.Start();
+
+  volatile int messages_processed = 0;
+  FunctorMessageHandler<void, std::function<void()>> incrementer(
+      [&messages_processed, &entered_process_all_message_queues] {
+        // Wait for event as a means to ensure Increment doesn't occur outside
+        // of ProcessAllMessageQueues. The event is set by a message posted to
+        // the main thread, which is guaranteed to be handled inside
+        // ProcessAllMessageQueues.
+        entered_process_all_message_queues.Wait(Event::kForever);
+        AtomicOps::Increment(&messages_processed);
+      });
+  FunctorMessageHandler<void, std::function<void()>> event_signaler(
+      [&entered_process_all_message_queues] {
+        entered_process_all_message_queues.Set();
+      });
+
+  // Post messages (both delayed and non delayed) to both threads.
+  a.Post(RTC_FROM_HERE, &incrementer);
+  b.Post(RTC_FROM_HERE, &incrementer);
+  a.PostDelayed(RTC_FROM_HERE, 0, &incrementer);
+  b.PostDelayed(RTC_FROM_HERE, 0, &incrementer);
+  rtc::Thread::Current()->Post(RTC_FROM_HERE, &event_signaler);
+
+  MessageQueueManager::ProcessAllMessageQueues();
+  EXPECT_EQ(4, AtomicOps::AcquireLoad(&messages_processed));
+}
+
+// Test that ProcessAllMessageQueues doesn't hang if a thread is quitting.
+TEST(MessageQueueManager, ProcessAllMessageQueuesWithQuittingThread) {
+  Thread t;
+  t.Start();
+  t.Quit();
+  MessageQueueManager::ProcessAllMessageQueues();
+}
+
+// Test that ProcessAllMessageQueues doesn't hang if a queue clears its
+// messages.
+TEST(MessageQueueManager, ProcessAllMessageQueuesWithClearedQueue) {
+  Event entered_process_all_message_queues(true, false);
+  Thread t;
+  t.Start();
+
+  FunctorMessageHandler<void, std::function<void()>> clearer(
+      [&entered_process_all_message_queues] {
+        // Wait for event as a means to ensure Clear doesn't occur outside of
+        // ProcessAllMessageQueues. The event is set by a message posted to the
+        // main thread, which is guaranteed to be handled inside
+        // ProcessAllMessageQueues.
+        entered_process_all_message_queues.Wait(Event::kForever);
+        rtc::Thread::Current()->Clear(nullptr);
+      });
+  FunctorMessageHandler<void, std::function<void()>> event_signaler(
+      [&entered_process_all_message_queues] {
+        entered_process_all_message_queues.Set();
+      });
+
+  // Post messages (both delayed and non delayed) to both threads.
+  t.Post(RTC_FROM_HERE, &clearer);
+  rtc::Thread::Current()->Post(RTC_FROM_HERE, &event_signaler);
+  MessageQueueManager::ProcessAllMessageQueues();
+}
diff --git a/base/mod_ops.h b/base/mod_ops.h
index d61bd05..7ce100e 100644
--- a/base/mod_ops.h
+++ b/base/mod_ops.h
@@ -11,9 +11,125 @@
 #ifndef WEBRTC_BASE_MOD_OPS_H_
 #define WEBRTC_BASE_MOD_OPS_H_
 
+#include <limits>
+#include <type_traits>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/mod_ops.h"
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+template <unsigned long M>                                    // NOLINT
+inline unsigned long Add(unsigned long a, unsigned long b) {  // NOLINT
+  RTC_DCHECK_LT(a, M);
+  unsigned long t = M - b % M;  // NOLINT
+  unsigned long res = a - t;    // NOLINT
+  if (t > a)
+    return res + M;
+  return res;
+}
+
+template <unsigned long M>                                         // NOLINT
+inline unsigned long Subtract(unsigned long a, unsigned long b) {  // NOLINT
+  RTC_DCHECK_LT(a, M);
+  unsigned long sub = b % M;  // NOLINT
+  if (a < sub)
+    return M - (sub - a);
+  return a - sub;
+}
+
+// Calculates the forward difference between two wrapping numbers.
+//
+// Example:
+// uint8_t x = 253;
+// uint8_t y = 2;
+//
+// ForwardDiff(x, y) == 5
+//
+//   252   253   254   255    0     1     2     3
+// #################################################
+// |     |  x  |     |     |     |     |  y  |     |
+// #################################################
+//          |----->----->----->----->----->
+//
+// ForwardDiff(y, x) == 251
+//
+//   252   253   254   255    0     1     2     3
+// #################################################
+// |     |  x  |     |     |     |     |  y  |     |
+// #################################################
+// -->----->                              |----->---
+//
+template <typename T, T M>
+inline T ForwardDiff(T a, T b) {
+  static_assert(std::is_unsigned<T>::value,
+                "Type must be an unsigned integer.");
+  RTC_DCHECK_LT(a, M);
+  RTC_DCHECK_LT(b, M);
+  return a <= b ? b - a : M - (a - b);
+}
+
+template <typename T>
+inline T ForwardDiff(T a, T b) {
+  static_assert(std::is_unsigned<T>::value,
+                "Type must be an unsigned integer.");
+  return b - a;
+}
+
+// Calculates the reverse difference between two wrapping numbers.
+//
+// Example:
+// uint8_t x = 253;
+// uint8_t y = 2;
+//
+// ReverseDiff(y, x) == 5
+//
+//   252   253   254   255    0     1     2     3
+// #################################################
+// |     |  x  |     |     |     |     |  y  |     |
+// #################################################
+//          <-----<-----<-----<-----<-----|
+//
+// ReverseDiff(x, y) == 251
+//
+//   252   253   254   255    0     1     2     3
+// #################################################
+// |     |  x  |     |     |     |     |  y  |     |
+// #################################################
+// ---<-----|                             |<-----<--
+//
+template <typename T, T M>
+inline T ReverseDiff(T a, T b) {
+  static_assert(std::is_unsigned<T>::value,
+                "Type must be an unsigned integer.");
+  RTC_DCHECK_LT(a, M);
+  RTC_DCHECK_LT(b, M);
+  return b <= a ? a - b : M - (b - a);
+}
+
+template <typename T>
+inline T ReverseDiff(T a, T b) {
+  static_assert(std::is_unsigned<T>::value,
+                "Type must be an unsigned integer.");
+  return a - b;
+}
+
+// Calculates the minimum distance between to wrapping numbers.
+//
+// The minimum distance is defined as min(ForwardDiff(a, b), ReverseDiff(a, b))
+template <typename T, T M>
+inline T MinDiff(T a, T b) {
+  static_assert(std::is_unsigned<T>::value,
+                "Type must be an unsigned integer.");
+  return std::min(ForwardDiff<T, M>(a, b), ReverseDiff<T, M>(a, b));
+}
+
+template <typename T>
+inline T MinDiff(T a, T b) {
+  static_assert(std::is_unsigned<T>::value,
+                "Type must be an unsigned integer.");
+  return std::min(ForwardDiff(a, b), ReverseDiff(a, b));
+}
+
+}  // namespace webrtc
 
 #endif  // WEBRTC_BASE_MOD_OPS_H_
diff --git a/base/mod_ops_unittest.cc b/base/mod_ops_unittest.cc
new file mode 100644
index 0000000..58c6a8e
--- /dev/null
+++ b/base/mod_ops_unittest.cc
@@ -0,0 +1,140 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/mod_ops.h"
+#include "webrtc/test/gtest.h"
+
+namespace webrtc {
+class TestModOps : public ::testing::Test {
+ protected:
+  // Can't use std::numeric_limits<unsigned long>::max() since
+  // MSVC doesn't support constexpr.
+  static const unsigned long ulmax = ~0ul;  // NOLINT
+};
+
+TEST_F(TestModOps, Add) {
+  const int D = 100;
+  ASSERT_EQ(1u, Add<D>(0, 1));
+  ASSERT_EQ(0u, Add<D>(0, D));
+  for (int i = 0; i < D; ++i)
+    ASSERT_EQ(0u, Add<D>(i, D - i));
+
+  int t = 37;
+  uint8_t a = t;
+  for (int i = 0; i < 256; ++i) {
+    ASSERT_EQ(a, static_cast<uint8_t>(t));
+    t = Add<256>(t, 1);
+    ++a;
+  }
+}
+
+TEST_F(TestModOps, AddLarge) {
+  const unsigned long D = ulmax - 10ul;  // NOLINT
+  unsigned long l = D - 1ul;             // NOLINT
+  ASSERT_EQ(D - 2ul, Add<D>(l, l));
+  ASSERT_EQ(9ul, Add<D>(l, ulmax));
+  ASSERT_EQ(10ul, Add<D>(0ul, ulmax));
+}
+
+TEST_F(TestModOps, Subtract) {
+  const int D = 100;
+  ASSERT_EQ(99u, Subtract<D>(0, 1));
+  ASSERT_EQ(0u, Subtract<D>(0, D));
+  for (int i = 0; i < D; ++i)
+    ASSERT_EQ(0u, Subtract<D>(i, D + i));
+
+  int t = 37;
+  uint8_t a = t;
+  for (int i = 0; i < 256; ++i) {
+    ASSERT_EQ(a, static_cast<uint8_t>(t));
+    t = Subtract<256>(t, 1);
+    --a;
+  }
+}
+
+TEST_F(TestModOps, SubtractLarge) {
+  // NOLINTNEXTLINE
+  const unsigned long D = ulmax - 10ul;  // NOLINT
+  unsigned long l = D - 1ul;             // NOLINT
+  ASSERT_EQ(0ul, Subtract<D>(l, l));
+  ASSERT_EQ(D - 11ul, Subtract<D>(l, ulmax));
+  ASSERT_EQ(D - 10ul, Subtract<D>(0ul, ulmax));
+}
+
+TEST_F(TestModOps, ForwardDiff) {
+  ASSERT_EQ(0u, ForwardDiff(4711u, 4711u));
+
+  uint8_t x = 0;
+  uint8_t y = 255;
+  for (int i = 0; i < 256; ++i) {
+    ASSERT_EQ(255u, ForwardDiff(x, y));
+    ++x;
+    ++y;
+  }
+
+  int yi = 255;
+  for (int i = 0; i < 256; ++i) {
+    ASSERT_EQ(255u, ForwardDiff<uint8_t>(x, yi));
+    ++x;
+    ++yi;
+  }
+}
+
+TEST_F(TestModOps, ReverseDiff) {
+  ASSERT_EQ(0u, ReverseDiff(4711u, 4711u));
+
+  uint8_t x = 0;
+  uint8_t y = 255;
+  for (int i = 0; i < 256; ++i) {
+    ASSERT_EQ(1u, ReverseDiff(x, y));
+    ++x;
+    ++y;
+  }
+
+  int yi = 255;
+  for (int i = 0; i < 256; ++i) {
+    ASSERT_EQ(1u, ReverseDiff<uint8_t>(x, yi));
+    ++x;
+    ++yi;
+  }
+}
+
+TEST_F(TestModOps, MinDiff) {
+  for (uint16_t i = 0; i < 256; ++i) {
+    ASSERT_EQ(0, MinDiff<uint8_t>(i, i));
+    ASSERT_EQ(1, MinDiff<uint8_t>(i - 1, i));
+    ASSERT_EQ(1, MinDiff<uint8_t>(i + 1, i));
+  }
+
+  for (uint8_t i = 0; i < 128; ++i)
+    ASSERT_EQ(i, MinDiff<uint8_t>(0, i));
+
+  for (uint8_t i = 0; i < 128; ++i)
+    ASSERT_EQ(128 - i, MinDiff<uint8_t>(0, 128 + i));
+}
+
+TEST_F(TestModOps, MinDiffWitDivisor) {
+  ASSERT_EQ(5u, (MinDiff<uint8_t, 11>(0, 5)));
+  ASSERT_EQ(5u, (MinDiff<uint8_t, 11>(0, 6)));
+  ASSERT_EQ(5u, (MinDiff<uint8_t, 11>(5, 0)));
+  ASSERT_EQ(5u, (MinDiff<uint8_t, 11>(6, 0)));
+
+  const uint16_t D = 4711;
+
+  for (uint16_t i = 0; i < D / 2; ++i)
+    ASSERT_EQ(i, (MinDiff<uint16_t, D>(0, i)));
+
+  ASSERT_EQ(D / 2, (MinDiff<uint16_t, D>(0, D / 2)));
+
+  for (uint16_t i = 0; i < D / 2; ++i)
+    ASSERT_EQ(D / 2 - i, (MinDiff<uint16_t, D>(0, D / 2 - i)));
+}
+
+}  // namespace webrtc
diff --git a/base/nat_unittest.cc b/base/nat_unittest.cc
new file mode 100644
index 0000000..b7ec161
--- /dev/null
+++ b/base/nat_unittest.cc
@@ -0,0 +1,389 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "webrtc/base/asynctcpsocket.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/natserver.h"
+#include "webrtc/base/natsocketfactory.h"
+#include "webrtc/base/nethelpers.h"
+#include "webrtc/base/network.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/ptr_util.h"
+#include "webrtc/base/testclient.h"
+#include "webrtc/base/virtualsocketserver.h"
+
+using namespace rtc;
+
+bool CheckReceive(
+    TestClient* client, bool should_receive, const char* buf, size_t size) {
+  return (should_receive) ?
+      client->CheckNextPacket(buf, size, 0) :
+      client->CheckNoPacket();
+}
+
+TestClient* CreateTestClient(
+      SocketFactory* factory, const SocketAddress& local_addr) {
+  return new TestClient(
+      WrapUnique(AsyncUDPSocket::Create(factory, local_addr)));
+}
+
+TestClient* CreateTCPTestClient(AsyncSocket* socket) {
+  return new TestClient(MakeUnique<AsyncTCPSocket>(socket, false));
+}
+
+// Tests that when sending from internal_addr to external_addrs through the
+// NAT type specified by nat_type, all external addrs receive the sent packet
+// and, if exp_same is true, all use the same mapped-address on the NAT.
+void TestSend(
+      SocketServer* internal, const SocketAddress& internal_addr,
+      SocketServer* external, const SocketAddress external_addrs[4],
+      NATType nat_type, bool exp_same) {
+  Thread th_int(internal);
+  Thread th_ext(external);
+
+  SocketAddress server_addr = internal_addr;
+  server_addr.SetPort(0);  // Auto-select a port
+  NATServer* nat = new NATServer(nat_type, internal, server_addr, server_addr,
+                                 external, external_addrs[0]);
+  NATSocketFactory* natsf = new NATSocketFactory(internal,
+                                                 nat->internal_udp_address(),
+                                                 nat->internal_tcp_address());
+
+  TestClient* in = CreateTestClient(natsf, internal_addr);
+  TestClient* out[4];
+  for (int i = 0; i < 4; i++)
+    out[i] = CreateTestClient(external, external_addrs[i]);
+
+  th_int.Start();
+  th_ext.Start();
+
+  const char* buf = "filter_test";
+  size_t len = strlen(buf);
+
+  in->SendTo(buf, len, out[0]->address());
+  SocketAddress trans_addr;
+  EXPECT_TRUE(out[0]->CheckNextPacket(buf, len, &trans_addr));
+
+  for (int i = 1; i < 4; i++) {
+    in->SendTo(buf, len, out[i]->address());
+    SocketAddress trans_addr2;
+    EXPECT_TRUE(out[i]->CheckNextPacket(buf, len, &trans_addr2));
+    bool are_same = (trans_addr == trans_addr2);
+    ASSERT_EQ(are_same, exp_same) << "same translated address";
+    ASSERT_NE(AF_UNSPEC, trans_addr.family());
+    ASSERT_NE(AF_UNSPEC, trans_addr2.family());
+  }
+
+  th_int.Stop();
+  th_ext.Stop();
+
+  delete nat;
+  delete natsf;
+  delete in;
+  for (int i = 0; i < 4; i++)
+    delete out[i];
+}
+
+// Tests that when sending from external_addrs to internal_addr, the packet
+// is delivered according to the specified filter_ip and filter_port rules.
+void TestRecv(
+      SocketServer* internal, const SocketAddress& internal_addr,
+      SocketServer* external, const SocketAddress external_addrs[4],
+      NATType nat_type, bool filter_ip, bool filter_port) {
+  Thread th_int(internal);
+  Thread th_ext(external);
+
+  SocketAddress server_addr = internal_addr;
+  server_addr.SetPort(0);  // Auto-select a port
+  NATServer* nat = new NATServer(nat_type, internal, server_addr, server_addr,
+                                 external, external_addrs[0]);
+  NATSocketFactory* natsf = new NATSocketFactory(internal,
+                                                 nat->internal_udp_address(),
+                                                 nat->internal_tcp_address());
+
+  TestClient* in = CreateTestClient(natsf, internal_addr);
+  TestClient* out[4];
+  for (int i = 0; i < 4; i++)
+    out[i] = CreateTestClient(external, external_addrs[i]);
+
+  th_int.Start();
+  th_ext.Start();
+
+  const char* buf = "filter_test";
+  size_t len = strlen(buf);
+
+  in->SendTo(buf, len, out[0]->address());
+  SocketAddress trans_addr;
+  EXPECT_TRUE(out[0]->CheckNextPacket(buf, len, &trans_addr));
+
+  out[1]->SendTo(buf, len, trans_addr);
+  EXPECT_TRUE(CheckReceive(in, !filter_ip, buf, len));
+
+  out[2]->SendTo(buf, len, trans_addr);
+  EXPECT_TRUE(CheckReceive(in, !filter_port, buf, len));
+
+  out[3]->SendTo(buf, len, trans_addr);
+  EXPECT_TRUE(CheckReceive(in, !filter_ip && !filter_port, buf, len));
+
+  th_int.Stop();
+  th_ext.Stop();
+
+  delete nat;
+  delete natsf;
+  delete in;
+  for (int i = 0; i < 4; i++)
+    delete out[i];
+}
+
+// Tests that NATServer allocates bindings properly.
+void TestBindings(
+    SocketServer* internal, const SocketAddress& internal_addr,
+    SocketServer* external, const SocketAddress external_addrs[4]) {
+  TestSend(internal, internal_addr, external, external_addrs,
+           NAT_OPEN_CONE, true);
+  TestSend(internal, internal_addr, external, external_addrs,
+           NAT_ADDR_RESTRICTED, true);
+  TestSend(internal, internal_addr, external, external_addrs,
+           NAT_PORT_RESTRICTED, true);
+  TestSend(internal, internal_addr, external, external_addrs,
+           NAT_SYMMETRIC, false);
+}
+
+// Tests that NATServer filters packets properly.
+void TestFilters(
+    SocketServer* internal, const SocketAddress& internal_addr,
+    SocketServer* external, const SocketAddress external_addrs[4]) {
+  TestRecv(internal, internal_addr, external, external_addrs,
+           NAT_OPEN_CONE, false, false);
+  TestRecv(internal, internal_addr, external, external_addrs,
+           NAT_ADDR_RESTRICTED, true, false);
+  TestRecv(internal, internal_addr, external, external_addrs,
+           NAT_PORT_RESTRICTED, true, true);
+  TestRecv(internal, internal_addr, external, external_addrs,
+           NAT_SYMMETRIC, true, true);
+}
+
+bool TestConnectivity(const SocketAddress& src, const IPAddress& dst) {
+  // The physical NAT tests require connectivity to the selected ip from the
+  // internal address used for the NAT. Things like firewalls can break that, so
+  // check to see if it's worth even trying with this ip.
+  std::unique_ptr<PhysicalSocketServer> pss(new PhysicalSocketServer());
+  std::unique_ptr<AsyncSocket> client(
+      pss->CreateAsyncSocket(src.family(), SOCK_DGRAM));
+  std::unique_ptr<AsyncSocket> server(
+      pss->CreateAsyncSocket(src.family(), SOCK_DGRAM));
+  if (client->Bind(SocketAddress(src.ipaddr(), 0)) != 0 ||
+      server->Bind(SocketAddress(dst, 0)) != 0) {
+    return false;
+  }
+  const char* buf = "hello other socket";
+  size_t len = strlen(buf);
+  int sent = client->SendTo(buf, len, server->GetLocalAddress());
+  SocketAddress addr;
+  const size_t kRecvBufSize = 64;
+  char recvbuf[kRecvBufSize];
+  Thread::Current()->SleepMs(100);
+  int received = server->RecvFrom(recvbuf, kRecvBufSize, &addr, nullptr);
+  return received == sent && ::memcmp(buf, recvbuf, len) == 0;
+}
+
+void TestPhysicalInternal(const SocketAddress& int_addr) {
+  BasicNetworkManager network_manager;
+  network_manager.set_ipv6_enabled(true);
+  network_manager.StartUpdating();
+  // Process pending messages so the network list is updated.
+  Thread::Current()->ProcessMessages(0);
+
+  std::vector<Network*> networks;
+  network_manager.GetNetworks(&networks);
+  networks.erase(std::remove_if(networks.begin(), networks.end(),
+                                [](rtc::Network* network) {
+                                  return rtc::kDefaultNetworkIgnoreMask &
+                                         network->type();
+                                }),
+                 networks.end());
+  if (networks.empty()) {
+    LOG(LS_WARNING) << "Not enough network adapters for test.";
+    return;
+  }
+
+  SocketAddress ext_addr1(int_addr);
+  SocketAddress ext_addr2;
+  // Find an available IP with matching family. The test breaks if int_addr
+  // can't talk to ip, so check for connectivity as well.
+  for (std::vector<Network*>::iterator it = networks.begin();
+      it != networks.end(); ++it) {
+    const IPAddress& ip = (*it)->GetBestIP();
+    if (ip.family() == int_addr.family() && TestConnectivity(int_addr, ip)) {
+      ext_addr2.SetIP(ip);
+      break;
+    }
+  }
+  if (ext_addr2.IsNil()) {
+    LOG(LS_WARNING) << "No available IP of same family as " << int_addr;
+    return;
+  }
+
+  LOG(LS_INFO) << "selected ip " << ext_addr2.ipaddr();
+
+  SocketAddress ext_addrs[4] = {
+      SocketAddress(ext_addr1),
+      SocketAddress(ext_addr2),
+      SocketAddress(ext_addr1),
+      SocketAddress(ext_addr2)
+  };
+
+  std::unique_ptr<PhysicalSocketServer> int_pss(new PhysicalSocketServer());
+  std::unique_ptr<PhysicalSocketServer> ext_pss(new PhysicalSocketServer());
+
+  TestBindings(int_pss.get(), int_addr, ext_pss.get(), ext_addrs);
+  TestFilters(int_pss.get(), int_addr, ext_pss.get(), ext_addrs);
+}
+
+TEST(NatTest, TestPhysicalIPv4) {
+  TestPhysicalInternal(SocketAddress("127.0.0.1", 0));
+}
+
+TEST(NatTest, TestPhysicalIPv6) {
+  if (HasIPv6Enabled()) {
+    TestPhysicalInternal(SocketAddress("::1", 0));
+  } else {
+    LOG(LS_WARNING) << "No IPv6, skipping";
+  }
+}
+
+namespace {
+
+class TestVirtualSocketServer : public VirtualSocketServer {
+ public:
+  // Expose this publicly
+  IPAddress GetNextIP(int af) { return VirtualSocketServer::GetNextIP(af); }
+};
+
+}  // namespace
+
+void TestVirtualInternal(int family) {
+  std::unique_ptr<TestVirtualSocketServer> int_vss(
+      new TestVirtualSocketServer());
+  std::unique_ptr<TestVirtualSocketServer> ext_vss(
+      new TestVirtualSocketServer());
+
+  SocketAddress int_addr;
+  SocketAddress ext_addrs[4];
+  int_addr.SetIP(int_vss->GetNextIP(family));
+  ext_addrs[0].SetIP(ext_vss->GetNextIP(int_addr.family()));
+  ext_addrs[1].SetIP(ext_vss->GetNextIP(int_addr.family()));
+  ext_addrs[2].SetIP(ext_addrs[0].ipaddr());
+  ext_addrs[3].SetIP(ext_addrs[1].ipaddr());
+
+  TestBindings(int_vss.get(), int_addr, ext_vss.get(), ext_addrs);
+  TestFilters(int_vss.get(), int_addr, ext_vss.get(), ext_addrs);
+}
+
+TEST(NatTest, TestVirtualIPv4) {
+  TestVirtualInternal(AF_INET);
+}
+
+TEST(NatTest, TestVirtualIPv6) {
+  if (HasIPv6Enabled()) {
+    TestVirtualInternal(AF_INET6);
+  } else {
+    LOG(LS_WARNING) << "No IPv6, skipping";
+  }
+}
+
+class NatTcpTest : public testing::Test, public sigslot::has_slots<> {
+ public:
+  NatTcpTest()
+      : int_addr_("192.168.0.1", 0),
+        ext_addr_("10.0.0.1", 0),
+        connected_(false),
+        int_vss_(new TestVirtualSocketServer()),
+        ext_vss_(new TestVirtualSocketServer()),
+        int_thread_(new Thread(int_vss_.get())),
+        ext_thread_(new Thread(ext_vss_.get())),
+        nat_(new NATServer(NAT_OPEN_CONE,
+                           int_vss_.get(),
+                           int_addr_,
+                           int_addr_,
+                           ext_vss_.get(),
+                           ext_addr_)),
+        natsf_(new NATSocketFactory(int_vss_.get(),
+                                    nat_->internal_udp_address(),
+                                    nat_->internal_tcp_address())) {
+    int_thread_->Start();
+    ext_thread_->Start();
+  }
+
+  void OnConnectEvent(AsyncSocket* socket) {
+    connected_ = true;
+  }
+
+  void OnAcceptEvent(AsyncSocket* socket) {
+    accepted_.reset(server_->Accept(nullptr));
+  }
+
+  void OnCloseEvent(AsyncSocket* socket, int error) {
+  }
+
+  void ConnectEvents() {
+    server_->SignalReadEvent.connect(this, &NatTcpTest::OnAcceptEvent);
+    client_->SignalConnectEvent.connect(this, &NatTcpTest::OnConnectEvent);
+  }
+
+  SocketAddress int_addr_;
+  SocketAddress ext_addr_;
+  bool connected_;
+  std::unique_ptr<TestVirtualSocketServer> int_vss_;
+  std::unique_ptr<TestVirtualSocketServer> ext_vss_;
+  std::unique_ptr<Thread> int_thread_;
+  std::unique_ptr<Thread> ext_thread_;
+  std::unique_ptr<NATServer> nat_;
+  std::unique_ptr<NATSocketFactory> natsf_;
+  std::unique_ptr<AsyncSocket> client_;
+  std::unique_ptr<AsyncSocket> server_;
+  std::unique_ptr<AsyncSocket> accepted_;
+};
+
+TEST_F(NatTcpTest, DISABLED_TestConnectOut) {
+  server_.reset(ext_vss_->CreateAsyncSocket(SOCK_STREAM));
+  server_->Bind(ext_addr_);
+  server_->Listen(5);
+
+  client_.reset(natsf_->CreateAsyncSocket(SOCK_STREAM));
+  EXPECT_GE(0, client_->Bind(int_addr_));
+  EXPECT_GE(0, client_->Connect(server_->GetLocalAddress()));
+
+  ConnectEvents();
+
+  EXPECT_TRUE_WAIT(connected_, 1000);
+  EXPECT_EQ(client_->GetRemoteAddress(), server_->GetLocalAddress());
+  EXPECT_EQ(accepted_->GetRemoteAddress().ipaddr(), ext_addr_.ipaddr());
+
+  std::unique_ptr<rtc::TestClient> in(CreateTCPTestClient(client_.release()));
+  std::unique_ptr<rtc::TestClient> out(
+      CreateTCPTestClient(accepted_.release()));
+
+  const char* buf = "test_packet";
+  size_t len = strlen(buf);
+
+  in->Send(buf, len);
+  SocketAddress trans_addr;
+  EXPECT_TRUE(out->CheckNextPacket(buf, len, &trans_addr));
+
+  out->Send(buf, len);
+  EXPECT_TRUE(in->CheckNextPacket(buf, len, &trans_addr));
+}
+// #endif
diff --git a/base/natserver.cc b/base/natserver.cc
new file mode 100644
index 0000000..e00bf9f
--- /dev/null
+++ b/base/natserver.cc
@@ -0,0 +1,252 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/natsocketfactory.h"
+#include "webrtc/base/natserver.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/socketadapters.h"
+
+namespace rtc {
+
+RouteCmp::RouteCmp(NAT* nat) : symmetric(nat->IsSymmetric()) {
+}
+
+size_t RouteCmp::operator()(const SocketAddressPair& r) const {
+  size_t h = r.source().Hash();
+  if (symmetric)
+    h ^= r.destination().Hash();
+  return h;
+}
+
+bool RouteCmp::operator()(
+      const SocketAddressPair& r1, const SocketAddressPair& r2) const {
+  if (r1.source() < r2.source())
+    return true;
+  if (r2.source() < r1.source())
+    return false;
+  if (symmetric && (r1.destination() < r2.destination()))
+    return true;
+  if (symmetric && (r2.destination() < r1.destination()))
+    return false;
+  return false;
+}
+
+AddrCmp::AddrCmp(NAT* nat)
+    : use_ip(nat->FiltersIP()), use_port(nat->FiltersPort()) {
+}
+
+size_t AddrCmp::operator()(const SocketAddress& a) const {
+  size_t h = 0;
+  if (use_ip)
+    h ^= HashIP(a.ipaddr());
+  if (use_port)
+    h ^= a.port() | (a.port() << 16);
+  return h;
+}
+
+bool AddrCmp::operator()(
+      const SocketAddress& a1, const SocketAddress& a2) const {
+  if (use_ip && (a1.ipaddr() < a2.ipaddr()))
+    return true;
+  if (use_ip && (a2.ipaddr() < a1.ipaddr()))
+    return false;
+  if (use_port && (a1.port() < a2.port()))
+    return true;
+  if (use_port && (a2.port() < a1.port()))
+    return false;
+  return false;
+}
+
+// Proxy socket that will capture the external destination address intended for
+// a TCP connection to the NAT server.
+class NATProxyServerSocket : public AsyncProxyServerSocket {
+ public:
+  NATProxyServerSocket(AsyncSocket* socket)
+      : AsyncProxyServerSocket(socket, kNATEncodedIPv6AddressSize) {
+    BufferInput(true);
+  }
+
+  void SendConnectResult(int err, const SocketAddress& addr) override {
+    char code = err ? 1 : 0;
+    BufferedReadAdapter::DirectSend(&code, sizeof(char));
+  }
+
+ protected:
+  void ProcessInput(char* data, size_t* len) override {
+    if (*len < 2) {
+      return;
+    }
+
+    int family = data[1];
+    RTC_DCHECK(family == AF_INET || family == AF_INET6);
+    if ((family == AF_INET && *len < kNATEncodedIPv4AddressSize) ||
+        (family == AF_INET6 && *len < kNATEncodedIPv6AddressSize)) {
+      return;
+    }
+
+    SocketAddress dest_addr;
+    size_t address_length = UnpackAddressFromNAT(data, *len, &dest_addr);
+
+    *len -= address_length;
+    if (*len > 0) {
+      memmove(data, data + address_length, *len);
+    }
+
+    bool remainder = (*len > 0);
+    BufferInput(false);
+    SignalConnectRequest(this, dest_addr);
+    if (remainder) {
+      SignalReadEvent(this);
+    }
+  }
+
+};
+
+class NATProxyServer : public ProxyServer {
+ public:
+  NATProxyServer(SocketFactory* int_factory, const SocketAddress& int_addr,
+                 SocketFactory* ext_factory, const SocketAddress& ext_ip)
+      : ProxyServer(int_factory, int_addr, ext_factory, ext_ip) {
+  }
+
+ protected:
+  AsyncProxyServerSocket* WrapSocket(AsyncSocket* socket) override {
+    return new NATProxyServerSocket(socket);
+  }
+};
+
+NATServer::NATServer(
+    NATType type, SocketFactory* internal,
+    const SocketAddress& internal_udp_addr,
+    const SocketAddress& internal_tcp_addr,
+    SocketFactory* external, const SocketAddress& external_ip)
+    : external_(external), external_ip_(external_ip.ipaddr(), 0) {
+  nat_ = NAT::Create(type);
+
+  udp_server_socket_ = AsyncUDPSocket::Create(internal, internal_udp_addr);
+  udp_server_socket_->SignalReadPacket.connect(this,
+                                               &NATServer::OnInternalUDPPacket);
+  tcp_proxy_server_ = new NATProxyServer(internal, internal_tcp_addr, external,
+                                         external_ip);
+
+  int_map_ = new InternalMap(RouteCmp(nat_));
+  ext_map_ = new ExternalMap();
+}
+
+NATServer::~NATServer() {
+  for (InternalMap::iterator iter = int_map_->begin();
+       iter != int_map_->end();
+       iter++)
+    delete iter->second;
+
+  delete nat_;
+  delete udp_server_socket_;
+  delete tcp_proxy_server_;
+  delete int_map_;
+  delete ext_map_;
+}
+
+void NATServer::OnInternalUDPPacket(
+    AsyncPacketSocket* socket, const char* buf, size_t size,
+    const SocketAddress& addr, const PacketTime& packet_time) {
+  // Read the intended destination from the wire.
+  SocketAddress dest_addr;
+  size_t length = UnpackAddressFromNAT(buf, size, &dest_addr);
+
+  // Find the translation for these addresses (allocating one if necessary).
+  SocketAddressPair route(addr, dest_addr);
+  InternalMap::iterator iter = int_map_->find(route);
+  if (iter == int_map_->end()) {
+    Translate(route);
+    iter = int_map_->find(route);
+  }
+  RTC_DCHECK(iter != int_map_->end());
+
+  // Allow the destination to send packets back to the source.
+  iter->second->WhitelistInsert(dest_addr);
+
+  // Send the packet to its intended destination.
+  rtc::PacketOptions options;
+  iter->second->socket->SendTo(buf + length, size - length, dest_addr, options);
+}
+
+void NATServer::OnExternalUDPPacket(
+    AsyncPacketSocket* socket, const char* buf, size_t size,
+    const SocketAddress& remote_addr, const PacketTime& packet_time) {
+  SocketAddress local_addr = socket->GetLocalAddress();
+
+  // Find the translation for this addresses.
+  ExternalMap::iterator iter = ext_map_->find(local_addr);
+  RTC_DCHECK(iter != ext_map_->end());
+
+  // Allow the NAT to reject this packet.
+  if (ShouldFilterOut(iter->second, remote_addr)) {
+    LOG(LS_INFO) << "Packet from " << remote_addr.ToSensitiveString()
+                 << " was filtered out by the NAT.";
+    return;
+  }
+
+  // Forward this packet to the internal address.
+  // First prepend the address in a quasi-STUN format.
+  std::unique_ptr<char[]> real_buf(new char[size + kNATEncodedIPv6AddressSize]);
+  size_t addrlength = PackAddressForNAT(real_buf.get(),
+                                        size + kNATEncodedIPv6AddressSize,
+                                        remote_addr);
+  // Copy the data part after the address.
+  rtc::PacketOptions options;
+  memcpy(real_buf.get() + addrlength, buf, size);
+  udp_server_socket_->SendTo(real_buf.get(), size + addrlength,
+                             iter->second->route.source(), options);
+}
+
+void NATServer::Translate(const SocketAddressPair& route) {
+  AsyncUDPSocket* socket = AsyncUDPSocket::Create(external_, external_ip_);
+
+  if (!socket) {
+    LOG(LS_ERROR) << "Couldn't find a free port!";
+    return;
+  }
+
+  TransEntry* entry = new TransEntry(route, socket, nat_);
+  (*int_map_)[route] = entry;
+  (*ext_map_)[socket->GetLocalAddress()] = entry;
+  socket->SignalReadPacket.connect(this, &NATServer::OnExternalUDPPacket);
+}
+
+bool NATServer::ShouldFilterOut(TransEntry* entry,
+                                const SocketAddress& ext_addr) {
+  return entry->WhitelistContains(ext_addr);
+}
+
+NATServer::TransEntry::TransEntry(
+    const SocketAddressPair& r, AsyncUDPSocket* s, NAT* nat)
+    : route(r), socket(s) {
+  whitelist = new AddressSet(AddrCmp(nat));
+}
+
+NATServer::TransEntry::~TransEntry() {
+  delete whitelist;
+  delete socket;
+}
+
+void NATServer::TransEntry::WhitelistInsert(const SocketAddress& addr) {
+  CritScope cs(&crit_);
+  whitelist->insert(addr);
+}
+
+bool NATServer::TransEntry::WhitelistContains(const SocketAddress& ext_addr) {
+  CritScope cs(&crit_);
+  return whitelist->find(ext_addr) == whitelist->end();
+}
+
+}  // namespace rtc
diff --git a/base/natserver.h b/base/natserver.h
index b803ad8..460518b 100644
--- a/base/natserver.h
+++ b/base/natserver.h
@@ -11,9 +11,114 @@
 #ifndef WEBRTC_BASE_NATSERVER_H_
 #define WEBRTC_BASE_NATSERVER_H_
 
+#include <map>
+#include <set>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/natserver.h"
+#include "webrtc/base/asyncudpsocket.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/socketaddresspair.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/socketfactory.h"
+#include "webrtc/base/nattypes.h"
+#include "webrtc/base/proxyserver.h"
+
+namespace rtc {
+
+// Change how routes (socketaddress pairs) are compared based on the type of
+// NAT.  The NAT server maintains a hashtable of the routes that it knows
+// about.  So these affect which routes are treated the same.
+struct RouteCmp {
+  explicit RouteCmp(NAT* nat);
+  size_t operator()(const SocketAddressPair& r) const;
+  bool operator()(
+      const SocketAddressPair& r1, const SocketAddressPair& r2) const;
+
+  bool symmetric;
+};
+
+// Changes how addresses are compared based on the filtering rules of the NAT.
+struct AddrCmp {
+  explicit AddrCmp(NAT* nat);
+  size_t operator()(const SocketAddress& r) const;
+  bool operator()(const SocketAddress& r1, const SocketAddress& r2) const;
+
+  bool use_ip;
+  bool use_port;
+};
+
+// Implements the NAT device.  It listens for packets on the internal network,
+// translates them, and sends them out over the external network.
+//
+// TCP connections initiated from the internal side of the NAT server are
+// also supported, by making a connection to the NAT server's TCP address and
+// then sending the remote address in quasi-STUN format. The connection status
+// will be indicated back to the client as a 1 byte status code, where '0'
+// indicates success.
+
+const int NAT_SERVER_UDP_PORT = 4237;
+const int NAT_SERVER_TCP_PORT = 4238;
+
+class NATServer : public sigslot::has_slots<> {
+ public:
+  NATServer(
+      NATType type, SocketFactory* internal,
+      const SocketAddress& internal_udp_addr,
+      const SocketAddress& internal_tcp_addr,
+      SocketFactory* external, const SocketAddress& external_ip);
+  ~NATServer() override;
+
+  SocketAddress internal_udp_address() const {
+    return udp_server_socket_->GetLocalAddress();
+  }
+
+  SocketAddress internal_tcp_address() const {
+    return tcp_proxy_server_->GetServerAddress();
+  }
+
+  // Packets received on one of the networks.
+  void OnInternalUDPPacket(AsyncPacketSocket* socket, const char* buf,
+                           size_t size, const SocketAddress& addr,
+                           const PacketTime& packet_time);
+  void OnExternalUDPPacket(AsyncPacketSocket* socket, const char* buf,
+                           size_t size, const SocketAddress& remote_addr,
+                           const PacketTime& packet_time);
+
+ private:
+  typedef std::set<SocketAddress, AddrCmp> AddressSet;
+
+  /* Records a translation and the associated external socket. */
+  struct TransEntry {
+    TransEntry(const SocketAddressPair& r, AsyncUDPSocket* s, NAT* nat);
+    ~TransEntry();
+
+    void WhitelistInsert(const SocketAddress& addr);
+    bool WhitelistContains(const SocketAddress& ext_addr);
+
+    SocketAddressPair route;
+    AsyncUDPSocket* socket;
+    AddressSet* whitelist;
+    CriticalSection crit_;
+  };
+
+  typedef std::map<SocketAddressPair, TransEntry*, RouteCmp> InternalMap;
+  typedef std::map<SocketAddress, TransEntry*> ExternalMap;
+
+  /* Creates a new entry that translates the given route. */
+  void Translate(const SocketAddressPair& route);
+
+  /* Determines whether the NAT would filter out a packet from this address. */
+  bool ShouldFilterOut(TransEntry* entry, const SocketAddress& ext_addr);
+
+  NAT* nat_;
+  SocketFactory* external_;
+  SocketAddress external_ip_;
+  AsyncUDPSocket* udp_server_socket_;
+  ProxyServer* tcp_proxy_server_;
+  InternalMap* int_map_;
+  ExternalMap* ext_map_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(NATServer);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_NATSERVER_H_
diff --git a/base/natsocketfactory.cc b/base/natsocketfactory.cc
new file mode 100644
index 0000000..95e6487
--- /dev/null
+++ b/base/natsocketfactory.cc
@@ -0,0 +1,530 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/natsocketfactory.h"
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/natserver.h"
+#include "webrtc/base/virtualsocketserver.h"
+
+namespace rtc {
+
+// Packs the given socketaddress into the buffer in buf, in the quasi-STUN
+// format that the natserver uses.
+// Returns 0 if an invalid address is passed.
+size_t PackAddressForNAT(char* buf, size_t buf_size,
+                         const SocketAddress& remote_addr) {
+  const IPAddress& ip = remote_addr.ipaddr();
+  int family = ip.family();
+  buf[0] = 0;
+  buf[1] = family;
+  // Writes the port.
+  *(reinterpret_cast<uint16_t*>(&buf[2])) = HostToNetwork16(remote_addr.port());
+  if (family == AF_INET) {
+    RTC_DCHECK(buf_size >= kNATEncodedIPv4AddressSize);
+    in_addr v4addr = ip.ipv4_address();
+    memcpy(&buf[4], &v4addr, kNATEncodedIPv4AddressSize - 4);
+    return kNATEncodedIPv4AddressSize;
+  } else if (family == AF_INET6) {
+    RTC_DCHECK(buf_size >= kNATEncodedIPv6AddressSize);
+    in6_addr v6addr = ip.ipv6_address();
+    memcpy(&buf[4], &v6addr, kNATEncodedIPv6AddressSize - 4);
+    return kNATEncodedIPv6AddressSize;
+  }
+  return 0U;
+}
+
+// Decodes the remote address from a packet that has been encoded with the nat's
+// quasi-STUN format. Returns the length of the address (i.e., the offset into
+// data where the original packet starts).
+size_t UnpackAddressFromNAT(const char* buf, size_t buf_size,
+                            SocketAddress* remote_addr) {
+  RTC_DCHECK(buf_size >= 8);
+  RTC_DCHECK(buf[0] == 0);
+  int family = buf[1];
+  uint16_t port =
+      NetworkToHost16(*(reinterpret_cast<const uint16_t*>(&buf[2])));
+  if (family == AF_INET) {
+    const in_addr* v4addr = reinterpret_cast<const in_addr*>(&buf[4]);
+    *remote_addr = SocketAddress(IPAddress(*v4addr), port);
+    return kNATEncodedIPv4AddressSize;
+  } else if (family == AF_INET6) {
+    RTC_DCHECK(buf_size >= 20);
+    const in6_addr* v6addr = reinterpret_cast<const in6_addr*>(&buf[4]);
+    *remote_addr = SocketAddress(IPAddress(*v6addr), port);
+    return kNATEncodedIPv6AddressSize;
+  }
+  return 0U;
+}
+
+
+// NATSocket
+class NATSocket : public AsyncSocket, public sigslot::has_slots<> {
+ public:
+  explicit NATSocket(NATInternalSocketFactory* sf, int family, int type)
+      : sf_(sf),
+        family_(family),
+        type_(type),
+        connected_(false),
+        socket_(nullptr),
+        buf_(nullptr),
+        size_(0) {}
+
+  ~NATSocket() override {
+    delete socket_;
+    delete[] buf_;
+  }
+
+  SocketAddress GetLocalAddress() const override {
+    return (socket_) ? socket_->GetLocalAddress() : SocketAddress();
+  }
+
+  SocketAddress GetRemoteAddress() const override {
+    return remote_addr_;  // will be NIL if not connected
+  }
+
+  int Bind(const SocketAddress& addr) override {
+    if (socket_) {  // already bound, bubble up error
+      return -1;
+    }
+
+    return BindInternal(addr);
+  }
+
+  int Connect(const SocketAddress& addr) override {
+    int result = 0;
+    // If we're not already bound (meaning |socket_| is null), bind to ANY
+    // address.
+    if (!socket_) {
+      result = BindInternal(SocketAddress(GetAnyIP(family_), 0));
+      if (result < 0) {
+        return result;
+      }
+    }
+
+    if (type_ == SOCK_STREAM) {
+      result = socket_->Connect(server_addr_.IsNil() ? addr : server_addr_);
+    } else {
+      connected_ = true;
+    }
+
+    if (result >= 0) {
+      remote_addr_ = addr;
+    }
+
+    return result;
+  }
+
+  int Send(const void* data, size_t size) override {
+    RTC_DCHECK(connected_);
+    return SendTo(data, size, remote_addr_);
+  }
+
+  int SendTo(const void* data,
+             size_t size,
+             const SocketAddress& addr) override {
+    RTC_DCHECK(!connected_ || addr == remote_addr_);
+    if (server_addr_.IsNil() || type_ == SOCK_STREAM) {
+      return socket_->SendTo(data, size, addr);
+    }
+    // This array will be too large for IPv4 packets, but only by 12 bytes.
+    std::unique_ptr<char[]> buf(new char[size + kNATEncodedIPv6AddressSize]);
+    size_t addrlength = PackAddressForNAT(buf.get(),
+                                          size + kNATEncodedIPv6AddressSize,
+                                          addr);
+    size_t encoded_size = size + addrlength;
+    memcpy(buf.get() + addrlength, data, size);
+    int result = socket_->SendTo(buf.get(), encoded_size, server_addr_);
+    if (result >= 0) {
+      RTC_DCHECK(result == static_cast<int>(encoded_size));
+      result = result - static_cast<int>(addrlength);
+    }
+    return result;
+  }
+
+  int Recv(void* data, size_t size, int64_t* timestamp) override {
+    SocketAddress addr;
+    return RecvFrom(data, size, &addr, timestamp);
+  }
+
+  int RecvFrom(void* data,
+               size_t size,
+               SocketAddress* out_addr,
+               int64_t* timestamp) override {
+    if (server_addr_.IsNil() || type_ == SOCK_STREAM) {
+      return socket_->RecvFrom(data, size, out_addr, timestamp);
+    }
+    // Make sure we have enough room to read the requested amount plus the
+    // largest possible header address.
+    SocketAddress remote_addr;
+    Grow(size + kNATEncodedIPv6AddressSize);
+
+    // Read the packet from the socket.
+    int result = socket_->RecvFrom(buf_, size_, &remote_addr, timestamp);
+    if (result >= 0) {
+      RTC_DCHECK(remote_addr == server_addr_);
+
+      // TODO: we need better framing so we know how many bytes we can
+      // return before we need to read the next address. For UDP, this will be
+      // fine as long as the reader always reads everything in the packet.
+      RTC_DCHECK((size_t)result < size_);
+
+      // Decode the wire packet into the actual results.
+      SocketAddress real_remote_addr;
+      size_t addrlength = UnpackAddressFromNAT(buf_, result, &real_remote_addr);
+      memcpy(data, buf_ + addrlength, result - addrlength);
+
+      // Make sure this packet should be delivered before returning it.
+      if (!connected_ || (real_remote_addr == remote_addr_)) {
+        if (out_addr)
+          *out_addr = real_remote_addr;
+        result = result - static_cast<int>(addrlength);
+      } else {
+        LOG(LS_ERROR) << "Dropping packet from unknown remote address: "
+                      << real_remote_addr.ToString();
+        result = 0;  // Tell the caller we didn't read anything
+      }
+    }
+
+    return result;
+  }
+
+  int Close() override {
+    int result = 0;
+    if (socket_) {
+      result = socket_->Close();
+      if (result >= 0) {
+        connected_ = false;
+        remote_addr_ = SocketAddress();
+        delete socket_;
+        socket_ = nullptr;
+      }
+    }
+    return result;
+  }
+
+  int Listen(int backlog) override { return socket_->Listen(backlog); }
+  AsyncSocket* Accept(SocketAddress* paddr) override {
+    return socket_->Accept(paddr);
+  }
+  int GetError() const override {
+    return socket_ ? socket_->GetError() : error_;
+  }
+  void SetError(int error) override {
+    if (socket_) {
+      socket_->SetError(error);
+    } else {
+      error_ = error;
+    }
+  }
+  ConnState GetState() const override {
+    return connected_ ? CS_CONNECTED : CS_CLOSED;
+  }
+  int GetOption(Option opt, int* value) override {
+    return socket_->GetOption(opt, value);
+  }
+  int SetOption(Option opt, int value) override {
+    return socket_->SetOption(opt, value);
+  }
+
+  void OnConnectEvent(AsyncSocket* socket) {
+    // If we're NATed, we need to send a message with the real addr to use.
+    RTC_DCHECK(socket == socket_);
+    if (server_addr_.IsNil()) {
+      connected_ = true;
+      SignalConnectEvent(this);
+    } else {
+      SendConnectRequest();
+    }
+  }
+  void OnReadEvent(AsyncSocket* socket) {
+    // If we're NATed, we need to process the connect reply.
+    RTC_DCHECK(socket == socket_);
+    if (type_ == SOCK_STREAM && !server_addr_.IsNil() && !connected_) {
+      HandleConnectReply();
+    } else {
+      SignalReadEvent(this);
+    }
+  }
+  void OnWriteEvent(AsyncSocket* socket) {
+    RTC_DCHECK(socket == socket_);
+    SignalWriteEvent(this);
+  }
+  void OnCloseEvent(AsyncSocket* socket, int error) {
+    RTC_DCHECK(socket == socket_);
+    SignalCloseEvent(this, error);
+  }
+
+ private:
+  int BindInternal(const SocketAddress& addr) {
+    RTC_DCHECK(!socket_);
+
+    int result;
+    socket_ = sf_->CreateInternalSocket(family_, type_, addr, &server_addr_);
+    result = (socket_) ? socket_->Bind(addr) : -1;
+    if (result >= 0) {
+      socket_->SignalConnectEvent.connect(this, &NATSocket::OnConnectEvent);
+      socket_->SignalReadEvent.connect(this, &NATSocket::OnReadEvent);
+      socket_->SignalWriteEvent.connect(this, &NATSocket::OnWriteEvent);
+      socket_->SignalCloseEvent.connect(this, &NATSocket::OnCloseEvent);
+    } else {
+      server_addr_.Clear();
+      delete socket_;
+      socket_ = nullptr;
+    }
+
+    return result;
+  }
+
+  // Makes sure the buffer is at least the given size.
+  void Grow(size_t new_size) {
+    if (size_ < new_size) {
+      delete[] buf_;
+      size_ = new_size;
+      buf_ = new char[size_];
+    }
+  }
+
+  // Sends the destination address to the server to tell it to connect.
+  void SendConnectRequest() {
+    char buf[kNATEncodedIPv6AddressSize];
+    size_t length = PackAddressForNAT(buf, arraysize(buf), remote_addr_);
+    socket_->Send(buf, length);
+  }
+
+  // Handles the byte sent back from the server and fires the appropriate event.
+  void HandleConnectReply() {
+    char code;
+    socket_->Recv(&code, sizeof(code), nullptr);
+    if (code == 0) {
+      connected_ = true;
+      SignalConnectEvent(this);
+    } else {
+      Close();
+      SignalCloseEvent(this, code);
+    }
+  }
+
+  NATInternalSocketFactory* sf_;
+  int family_;
+  int type_;
+  bool connected_;
+  SocketAddress remote_addr_;
+  SocketAddress server_addr_;  // address of the NAT server
+  AsyncSocket* socket_;
+  // Need to hold error in case it occurs before the socket is created.
+  int error_ = 0;
+  char* buf_;
+  size_t size_;
+};
+
+// NATSocketFactory
+NATSocketFactory::NATSocketFactory(SocketFactory* factory,
+                                   const SocketAddress& nat_udp_addr,
+                                   const SocketAddress& nat_tcp_addr)
+    : factory_(factory), nat_udp_addr_(nat_udp_addr),
+      nat_tcp_addr_(nat_tcp_addr) {
+}
+
+Socket* NATSocketFactory::CreateSocket(int type) {
+  return CreateSocket(AF_INET, type);
+}
+
+Socket* NATSocketFactory::CreateSocket(int family, int type) {
+  return new NATSocket(this, family, type);
+}
+
+AsyncSocket* NATSocketFactory::CreateAsyncSocket(int type) {
+  return CreateAsyncSocket(AF_INET, type);
+}
+
+AsyncSocket* NATSocketFactory::CreateAsyncSocket(int family, int type) {
+  return new NATSocket(this, family, type);
+}
+
+AsyncSocket* NATSocketFactory::CreateInternalSocket(int family, int type,
+    const SocketAddress& local_addr, SocketAddress* nat_addr) {
+  if (type == SOCK_STREAM) {
+    *nat_addr = nat_tcp_addr_;
+  } else {
+    *nat_addr = nat_udp_addr_;
+  }
+  return factory_->CreateAsyncSocket(family, type);
+}
+
+// NATSocketServer
+NATSocketServer::NATSocketServer(SocketServer* server)
+    : server_(server), msg_queue_(nullptr) {}
+
+NATSocketServer::Translator* NATSocketServer::GetTranslator(
+    const SocketAddress& ext_ip) {
+  return nats_.Get(ext_ip);
+}
+
+NATSocketServer::Translator* NATSocketServer::AddTranslator(
+    const SocketAddress& ext_ip, const SocketAddress& int_ip, NATType type) {
+  // Fail if a translator already exists with this extternal address.
+  if (nats_.Get(ext_ip))
+    return nullptr;
+
+  return nats_.Add(ext_ip, new Translator(this, type, int_ip, server_, ext_ip));
+}
+
+void NATSocketServer::RemoveTranslator(
+    const SocketAddress& ext_ip) {
+  nats_.Remove(ext_ip);
+}
+
+Socket* NATSocketServer::CreateSocket(int type) {
+  return CreateSocket(AF_INET, type);
+}
+
+Socket* NATSocketServer::CreateSocket(int family, int type) {
+  return new NATSocket(this, family, type);
+}
+
+AsyncSocket* NATSocketServer::CreateAsyncSocket(int type) {
+  return CreateAsyncSocket(AF_INET, type);
+}
+
+AsyncSocket* NATSocketServer::CreateAsyncSocket(int family, int type) {
+  return new NATSocket(this, family, type);
+}
+
+void NATSocketServer::SetMessageQueue(MessageQueue* queue) {
+  msg_queue_ = queue;
+  server_->SetMessageQueue(queue);
+}
+
+bool NATSocketServer::Wait(int cms, bool process_io) {
+  return server_->Wait(cms, process_io);
+}
+
+void NATSocketServer::WakeUp() {
+  server_->WakeUp();
+}
+
+AsyncSocket* NATSocketServer::CreateInternalSocket(int family, int type,
+    const SocketAddress& local_addr, SocketAddress* nat_addr) {
+  AsyncSocket* socket = nullptr;
+  Translator* nat = nats_.FindClient(local_addr);
+  if (nat) {
+    socket = nat->internal_factory()->CreateAsyncSocket(family, type);
+    *nat_addr = (type == SOCK_STREAM) ?
+        nat->internal_tcp_address() : nat->internal_udp_address();
+  } else {
+    socket = server_->CreateAsyncSocket(family, type);
+  }
+  return socket;
+}
+
+// NATSocketServer::Translator
+NATSocketServer::Translator::Translator(
+    NATSocketServer* server, NATType type, const SocketAddress& int_ip,
+    SocketFactory* ext_factory, const SocketAddress& ext_ip)
+    : server_(server) {
+  // Create a new private network, and a NATServer running on the private
+  // network that bridges to the external network. Also tell the private
+  // network to use the same message queue as us.
+  VirtualSocketServer* internal_server = new VirtualSocketServer();
+  internal_server->SetMessageQueue(server_->queue());
+  internal_factory_.reset(internal_server);
+  nat_server_.reset(new NATServer(type, internal_server, int_ip, int_ip,
+                                  ext_factory, ext_ip));
+}
+
+NATSocketServer::Translator::~Translator() = default;
+
+NATSocketServer::Translator* NATSocketServer::Translator::GetTranslator(
+    const SocketAddress& ext_ip) {
+  return nats_.Get(ext_ip);
+}
+
+NATSocketServer::Translator* NATSocketServer::Translator::AddTranslator(
+    const SocketAddress& ext_ip, const SocketAddress& int_ip, NATType type) {
+  // Fail if a translator already exists with this extternal address.
+  if (nats_.Get(ext_ip))
+    return nullptr;
+
+  AddClient(ext_ip);
+  return nats_.Add(ext_ip,
+                   new Translator(server_, type, int_ip, server_, ext_ip));
+}
+void NATSocketServer::Translator::RemoveTranslator(
+    const SocketAddress& ext_ip) {
+  nats_.Remove(ext_ip);
+  RemoveClient(ext_ip);
+}
+
+bool NATSocketServer::Translator::AddClient(
+    const SocketAddress& int_ip) {
+  // Fail if a client already exists with this internal address.
+  if (clients_.find(int_ip) != clients_.end())
+    return false;
+
+  clients_.insert(int_ip);
+  return true;
+}
+
+void NATSocketServer::Translator::RemoveClient(
+    const SocketAddress& int_ip) {
+  std::set<SocketAddress>::iterator it = clients_.find(int_ip);
+  if (it != clients_.end()) {
+    clients_.erase(it);
+  }
+}
+
+NATSocketServer::Translator* NATSocketServer::Translator::FindClient(
+    const SocketAddress& int_ip) {
+  // See if we have the requested IP, or any of our children do.
+  return (clients_.find(int_ip) != clients_.end()) ?
+      this : nats_.FindClient(int_ip);
+}
+
+// NATSocketServer::TranslatorMap
+NATSocketServer::TranslatorMap::~TranslatorMap() {
+  for (TranslatorMap::iterator it = begin(); it != end(); ++it) {
+    delete it->second;
+  }
+}
+
+NATSocketServer::Translator* NATSocketServer::TranslatorMap::Get(
+    const SocketAddress& ext_ip) {
+  TranslatorMap::iterator it = find(ext_ip);
+  return (it != end()) ? it->second : nullptr;
+}
+
+NATSocketServer::Translator* NATSocketServer::TranslatorMap::Add(
+    const SocketAddress& ext_ip, Translator* nat) {
+  (*this)[ext_ip] = nat;
+  return nat;
+}
+
+void NATSocketServer::TranslatorMap::Remove(
+    const SocketAddress& ext_ip) {
+  TranslatorMap::iterator it = find(ext_ip);
+  if (it != end()) {
+    delete it->second;
+    erase(it);
+  }
+}
+
+NATSocketServer::Translator* NATSocketServer::TranslatorMap::FindClient(
+    const SocketAddress& int_ip) {
+  Translator* nat = nullptr;
+  for (TranslatorMap::iterator it = begin(); it != end() && !nat; ++it) {
+    nat = it->second->FindClient(int_ip);
+  }
+  return nat;
+}
+
+}  // namespace rtc
diff --git a/base/natsocketfactory.h b/base/natsocketfactory.h
index 31c29ab..6fad30c 100644
--- a/base/natsocketfactory.h
+++ b/base/natsocketfactory.h
@@ -11,9 +11,158 @@
 #ifndef WEBRTC_BASE_NATSOCKETFACTORY_H_
 #define WEBRTC_BASE_NATSOCKETFACTORY_H_
 
+#include <string>
+#include <map>
+#include <memory>
+#include <set>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/natsocketfactory.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/natserver.h"
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/socketserver.h"
+
+namespace rtc {
+
+const size_t kNATEncodedIPv4AddressSize = 8U;
+const size_t kNATEncodedIPv6AddressSize = 20U;
+
+// Used by the NAT socket implementation.
+class NATInternalSocketFactory {
+ public:
+  virtual ~NATInternalSocketFactory() {}
+  virtual AsyncSocket* CreateInternalSocket(int family, int type,
+      const SocketAddress& local_addr, SocketAddress* nat_addr) = 0;
+};
+
+// Creates sockets that will send all traffic through a NAT, using an existing
+// NATServer instance running at nat_addr. The actual data is sent using sockets
+// from a socket factory, given to the constructor.
+class NATSocketFactory : public SocketFactory, public NATInternalSocketFactory {
+ public:
+  NATSocketFactory(SocketFactory* factory, const SocketAddress& nat_udp_addr,
+                   const SocketAddress& nat_tcp_addr);
+
+  // SocketFactory implementation
+  Socket* CreateSocket(int type) override;
+  Socket* CreateSocket(int family, int type) override;
+  AsyncSocket* CreateAsyncSocket(int type) override;
+  AsyncSocket* CreateAsyncSocket(int family, int type) override;
+
+  // NATInternalSocketFactory implementation
+  AsyncSocket* CreateInternalSocket(int family,
+                                    int type,
+                                    const SocketAddress& local_addr,
+                                    SocketAddress* nat_addr) override;
+
+ private:
+  SocketFactory* factory_;
+  SocketAddress nat_udp_addr_;
+  SocketAddress nat_tcp_addr_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(NATSocketFactory);
+};
+
+// Creates sockets that will send traffic through a NAT depending on what
+// address they bind to. This can be used to simulate a client on a NAT sending
+// to a client that is not behind a NAT.
+// Note that the internal addresses of clients must be unique. This is because
+// there is only one socketserver per thread, and the Bind() address is used to
+// figure out which NAT (if any) the socket should talk to.
+//
+// Example with 3 NATs (2 cascaded), and 3 clients.
+// ss->AddTranslator("1.2.3.4", "192.168.0.1", NAT_ADDR_RESTRICTED);
+// ss->AddTranslator("99.99.99.99", "10.0.0.1", NAT_SYMMETRIC)->
+//     AddTranslator("10.0.0.2", "192.168.1.1", NAT_OPEN_CONE);
+// ss->GetTranslator("1.2.3.4")->AddClient("1.2.3.4", "192.168.0.2");
+// ss->GetTranslator("99.99.99.99")->AddClient("10.0.0.3");
+// ss->GetTranslator("99.99.99.99")->GetTranslator("10.0.0.2")->
+//     AddClient("192.168.1.2");
+class NATSocketServer : public SocketServer, public NATInternalSocketFactory {
+ public:
+  class Translator;
+  // holds a list of NATs
+  class TranslatorMap : private std::map<SocketAddress, Translator*> {
+   public:
+    ~TranslatorMap();
+    Translator* Get(const SocketAddress& ext_ip);
+    Translator* Add(const SocketAddress& ext_ip, Translator*);
+    void Remove(const SocketAddress& ext_ip);
+    Translator* FindClient(const SocketAddress& int_ip);
+  };
+
+  // a specific NAT
+  class Translator {
+   public:
+    Translator(NATSocketServer* server, NATType type,
+               const SocketAddress& int_addr, SocketFactory* ext_factory,
+               const SocketAddress& ext_addr);
+    ~Translator();
+
+    SocketFactory* internal_factory() { return internal_factory_.get(); }
+    SocketAddress internal_udp_address() const {
+      return nat_server_->internal_udp_address();
+    }
+    SocketAddress internal_tcp_address() const {
+      return SocketAddress();  // nat_server_->internal_tcp_address();
+    }
+
+    Translator* GetTranslator(const SocketAddress& ext_ip);
+    Translator* AddTranslator(const SocketAddress& ext_ip,
+                              const SocketAddress& int_ip, NATType type);
+    void RemoveTranslator(const SocketAddress& ext_ip);
+
+    bool AddClient(const SocketAddress& int_ip);
+    void RemoveClient(const SocketAddress& int_ip);
+
+    // Looks for the specified client in this or a child NAT.
+    Translator* FindClient(const SocketAddress& int_ip);
+
+   private:
+    NATSocketServer* server_;
+    std::unique_ptr<SocketFactory> internal_factory_;
+    std::unique_ptr<NATServer> nat_server_;
+    TranslatorMap nats_;
+    std::set<SocketAddress> clients_;
+  };
+
+  explicit NATSocketServer(SocketServer* ss);
+
+  SocketServer* socketserver() { return server_; }
+  MessageQueue* queue() { return msg_queue_; }
+
+  Translator* GetTranslator(const SocketAddress& ext_ip);
+  Translator* AddTranslator(const SocketAddress& ext_ip,
+                            const SocketAddress& int_ip, NATType type);
+  void RemoveTranslator(const SocketAddress& ext_ip);
+
+  // SocketServer implementation
+  Socket* CreateSocket(int type) override;
+  Socket* CreateSocket(int family, int type) override;
+
+  AsyncSocket* CreateAsyncSocket(int type) override;
+  AsyncSocket* CreateAsyncSocket(int family, int type) override;
+
+  void SetMessageQueue(MessageQueue* queue) override;
+  bool Wait(int cms, bool process_io) override;
+  void WakeUp() override;
+
+  // NATInternalSocketFactory implementation
+  AsyncSocket* CreateInternalSocket(int family,
+                                    int type,
+                                    const SocketAddress& local_addr,
+                                    SocketAddress* nat_addr) override;
+
+ private:
+  SocketServer* server_;
+  MessageQueue* msg_queue_;
+  TranslatorMap nats_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(NATSocketServer);
+};
+
+// Free-standing NAT helper functions.
+size_t PackAddressForNAT(char* buf, size_t buf_size,
+                         const SocketAddress& remote_addr);
+size_t UnpackAddressFromNAT(const char* buf, size_t buf_size,
+                            SocketAddress* remote_addr);
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_NATSOCKETFACTORY_H_
diff --git a/base/nattypes.cc b/base/nattypes.cc
new file mode 100644
index 0000000..1e37698
--- /dev/null
+++ b/base/nattypes.cc
@@ -0,0 +1,61 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/nattypes.h"
+
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+class SymmetricNAT : public NAT {
+public:
+ bool IsSymmetric() override { return true; }
+ bool FiltersIP() override { return true; }
+ bool FiltersPort() override { return true; }
+};
+
+class OpenConeNAT : public NAT {
+public:
+ bool IsSymmetric() override { return false; }
+ bool FiltersIP() override { return false; }
+ bool FiltersPort() override { return false; }
+};
+
+class AddressRestrictedNAT : public NAT {
+public:
+ bool IsSymmetric() override { return false; }
+ bool FiltersIP() override { return true; }
+ bool FiltersPort() override { return false; }
+};
+
+class PortRestrictedNAT : public NAT {
+public:
+ bool IsSymmetric() override { return false; }
+ bool FiltersIP() override { return true; }
+ bool FiltersPort() override { return true; }
+};
+
+NAT* NAT::Create(NATType type) {
+  switch (type) {
+    case NAT_OPEN_CONE:
+      return new OpenConeNAT();
+    case NAT_ADDR_RESTRICTED:
+      return new AddressRestrictedNAT();
+    case NAT_PORT_RESTRICTED:
+      return new PortRestrictedNAT();
+    case NAT_SYMMETRIC:
+      return new SymmetricNAT();
+    default:
+      RTC_NOTREACHED();
+      return 0;
+  }
+}
+
+} // namespace rtc
diff --git a/base/nattypes.h b/base/nattypes.h
index 001f57f..27e4b2f 100644
--- a/base/nattypes.h
+++ b/base/nattypes.h
@@ -8,12 +8,40 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_NATTYPES_H_
-#define WEBRTC_BASE_NATTYPES_H_
+#ifndef WEBRTC_BASE_NATTYPE_H__
+#define WEBRTC_BASE_NATTYPE_H__
 
+namespace rtc {
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/nattypes.h"
+/* Identifies each type of NAT that can be simulated. */
+enum NATType {
+  NAT_OPEN_CONE,
+  NAT_ADDR_RESTRICTED,
+  NAT_PORT_RESTRICTED,
+  NAT_SYMMETRIC
+};
 
-#endif // WEBRTC_BASE_NATTYPES_H_
+// Implements the rules for each specific type of NAT.
+class NAT {
+public:
+  virtual ~NAT() { }
+
+  // Determines whether this NAT uses both source and destination address when
+  // checking whether a mapping already exists.
+  virtual bool IsSymmetric() = 0;
+
+  // Determines whether this NAT drops packets received from a different IP
+  // the one last sent to.
+  virtual bool FiltersIP() = 0;
+
+  // Determines whether this NAT drops packets received from a different port
+  // the one last sent to.
+  virtual bool FiltersPort() = 0;
+
+  // Returns an implementation of the given type of NAT.
+  static NAT* Create(NATType type);
+};
+
+} // namespace rtc
+
+#endif // WEBRTC_BASE_NATTYPE_H__
diff --git a/base/nethelpers.cc b/base/nethelpers.cc
new file mode 100644
index 0000000..6c11ef8
--- /dev/null
+++ b/base/nethelpers.cc
@@ -0,0 +1,219 @@
+/*
+ *  Copyright 2008 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/nethelpers.h"
+
+#include <memory>
+
+#if defined(WEBRTC_WIN)
+#include <ws2spi.h>
+#include <ws2tcpip.h>
+#include "webrtc/base/win32.h"
+#endif
+#if defined(WEBRTC_POSIX) && !defined(__native_client__)
+#if defined(WEBRTC_ANDROID)
+#include "webrtc/base/ifaddrs-android.h"
+#else
+#include <ifaddrs.h>
+#endif
+#endif  // defined(WEBRTC_POSIX) && !defined(__native_client__)
+
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/signalthread.h"
+
+namespace rtc {
+
+int ResolveHostname(const std::string& hostname, int family,
+                    std::vector<IPAddress>* addresses) {
+#ifdef __native_client__
+  RTC_NOTREACHED();
+  LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl";
+  return -1;
+#else  // __native_client__
+  if (!addresses) {
+    return -1;
+  }
+  addresses->clear();
+  struct addrinfo* result = nullptr;
+  struct addrinfo hints = {0};
+  hints.ai_family = family;
+  // |family| here will almost always be AF_UNSPEC, because |family| comes from
+  // AsyncResolver::addr_.family(), which comes from a SocketAddress constructed
+  // with a hostname. When a SocketAddress is constructed with a hostname, its
+  // family is AF_UNSPEC. However, if someday in the future we construct
+  // a SocketAddress with both a hostname and a family other than AF_UNSPEC,
+  // then it would be possible to get a specific family value here.
+
+  // The behavior of AF_UNSPEC is roughly "get both ipv4 and ipv6", as
+  // documented by the various operating systems:
+  // Linux: http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
+  // Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/
+  // ms738520(v=vs.85).aspx
+  // Mac: https://developer.apple.com/legacy/library/documentation/Darwin/
+  // Reference/ManPages/man3/getaddrinfo.3.html
+  // Android (source code, not documentation):
+  // https://android.googlesource.com/platform/bionic/+/
+  // 7e0bfb511e85834d7c6cb9631206b62f82701d60/libc/netbsd/net/getaddrinfo.c#1657
+  hints.ai_flags = AI_ADDRCONFIG;
+  int ret = getaddrinfo(hostname.c_str(), nullptr, &hints, &result);
+  if (ret != 0) {
+    return ret;
+  }
+  struct addrinfo* cursor = result;
+  for (; cursor; cursor = cursor->ai_next) {
+    if (family == AF_UNSPEC || cursor->ai_family == family) {
+      IPAddress ip;
+      if (IPFromAddrInfo(cursor, &ip)) {
+        addresses->push_back(ip);
+      }
+    }
+  }
+  freeaddrinfo(result);
+  return 0;
+#endif  // !__native_client__
+}
+
+// AsyncResolver
+AsyncResolver::AsyncResolver()
+    : SignalThread(false /* use_socket_server */), error_(-1) {}
+
+AsyncResolver::~AsyncResolver() = default;
+
+void AsyncResolver::Start(const SocketAddress& addr) {
+  addr_ = addr;
+  // SignalThred Start will kickoff the resolve process.
+  SignalThread::Start();
+}
+
+bool AsyncResolver::GetResolvedAddress(int family, SocketAddress* addr) const {
+  if (error_ != 0 || addresses_.empty())
+    return false;
+
+  *addr = addr_;
+  for (size_t i = 0; i < addresses_.size(); ++i) {
+    if (family == addresses_[i].family()) {
+      addr->SetResolvedIP(addresses_[i]);
+      return true;
+    }
+  }
+  return false;
+}
+
+int AsyncResolver::GetError() const {
+  return error_;
+}
+
+void AsyncResolver::Destroy(bool wait) {
+  SignalThread::Destroy(wait);
+}
+
+void AsyncResolver::DoWork() {
+  error_ = ResolveHostname(addr_.hostname().c_str(), addr_.family(),
+                           &addresses_);
+}
+
+void AsyncResolver::OnWorkDone() {
+  SignalDone(this);
+}
+
+const char* inet_ntop(int af, const void *src, char* dst, socklen_t size) {
+#if defined(WEBRTC_WIN)
+  return win32_inet_ntop(af, src, dst, size);
+#else
+  return ::inet_ntop(af, src, dst, size);
+#endif
+}
+
+int inet_pton(int af, const char* src, void *dst) {
+#if defined(WEBRTC_WIN)
+  return win32_inet_pton(af, src, dst);
+#else
+  return ::inet_pton(af, src, dst);
+#endif
+}
+
+bool HasIPv4Enabled() {
+#if defined(WEBRTC_POSIX) && !defined(__native_client__)
+  bool has_ipv4 = false;
+  struct ifaddrs* ifa;
+  if (getifaddrs(&ifa) < 0) {
+    return false;
+  }
+  for (struct ifaddrs* cur = ifa; cur != nullptr; cur = cur->ifa_next) {
+    if (cur->ifa_addr->sa_family == AF_INET) {
+      has_ipv4 = true;
+      break;
+    }
+  }
+  freeifaddrs(ifa);
+  return has_ipv4;
+#else
+  return true;
+#endif
+}
+
+bool HasIPv6Enabled() {
+#if defined(WEBRTC_WIN)
+  if (IsWindowsVistaOrLater()) {
+    return true;
+  }
+  if (!IsWindowsXpOrLater()) {
+    return false;
+  }
+  DWORD protbuff_size = 4096;
+  std::unique_ptr<char[]> protocols;
+  LPWSAPROTOCOL_INFOW protocol_infos = nullptr;
+  int requested_protocols[2] = {AF_INET6, 0};
+
+  int err = 0;
+  int ret = 0;
+  // Check for protocols in a do-while loop until we provide a buffer large
+  // enough. (WSCEnumProtocols sets protbuff_size to its desired value).
+  // It is extremely unlikely that this will loop more than once.
+  do {
+    protocols.reset(new char[protbuff_size]);
+    protocol_infos = reinterpret_cast<LPWSAPROTOCOL_INFOW>(protocols.get());
+    ret = WSCEnumProtocols(requested_protocols, protocol_infos,
+                           &protbuff_size, &err);
+  } while (ret == SOCKET_ERROR && err == WSAENOBUFS);
+
+  if (ret == SOCKET_ERROR) {
+    return false;
+  }
+
+  // Even if ret is positive, check specifically for IPv6.
+  // Non-IPv6 enabled WinXP will still return a RAW protocol.
+  for (int i = 0; i < ret; ++i) {
+    if (protocol_infos[i].iAddressFamily == AF_INET6) {
+      return true;
+    }
+  }
+  return false;
+#elif defined(WEBRTC_POSIX) && !defined(__native_client__)
+  bool has_ipv6 = false;
+  struct ifaddrs* ifa;
+  if (getifaddrs(&ifa) < 0) {
+    return false;
+  }
+  for (struct ifaddrs* cur = ifa; cur != nullptr; cur = cur->ifa_next) {
+    if (cur->ifa_addr->sa_family == AF_INET6) {
+      has_ipv6 = true;
+      break;
+    }
+  }
+  freeifaddrs(ifa);
+  return has_ipv6;
+#else
+  return true;
+#endif
+}
+}  // namespace rtc
diff --git a/base/nethelpers.h b/base/nethelpers.h
index 9a8e607..b0727f8 100644
--- a/base/nethelpers.h
+++ b/base/nethelpers.h
@@ -11,9 +11,56 @@
 #ifndef WEBRTC_BASE_NETHELPERS_H_
 #define WEBRTC_BASE_NETHELPERS_H_
 
+#if defined(WEBRTC_POSIX)
+#include <netdb.h>
+#include <stddef.h>
+#elif WEBRTC_WIN
+#include <winsock2.h>  // NOLINT
+#endif
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/nethelpers.h"
+#include <list>
+
+#include "webrtc/base/asyncresolverinterface.h"
+#include "webrtc/base/signalthread.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/socketaddress.h"
+
+namespace rtc {
+
+class AsyncResolverTest;
+
+// AsyncResolver will perform async DNS resolution, signaling the result on
+// the SignalDone from AsyncResolverInterface when the operation completes.
+class AsyncResolver : public SignalThread, public AsyncResolverInterface {
+ public:
+  AsyncResolver();
+  ~AsyncResolver() override;
+
+  void Start(const SocketAddress& addr) override;
+  bool GetResolvedAddress(int family, SocketAddress* addr) const override;
+  int GetError() const override;
+  void Destroy(bool wait) override;
+
+  const std::vector<IPAddress>& addresses() const { return addresses_; }
+  void set_error(int error) { error_ = error; }
+
+ protected:
+  void DoWork() override;
+  void OnWorkDone() override;
+
+ private:
+  SocketAddress addr_;
+  std::vector<IPAddress> addresses_;
+  int error_;
+};
+
+// rtc namespaced wrappers for inet_ntop and inet_pton so we can avoid
+// the windows-native versions of these.
+const char* inet_ntop(int af, const void *src, char* dst, socklen_t size);
+int inet_pton(int af, const char* src, void *dst);
+
+bool HasIPv4Enabled();
+bool HasIPv6Enabled();
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_NETHELPERS_H_
diff --git a/base/network.cc b/base/network.cc
new file mode 100644
index 0000000..69dc28c
--- /dev/null
+++ b/base/network.cc
@@ -0,0 +1,992 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/network.h"
+
+#if defined(WEBRTC_POSIX)
+// linux/if.h can't be included at the same time as the posix sys/if.h, and
+// it's transitively required by linux/route.h, so include that version on
+// linux instead of the standard posix one.
+#if defined(WEBRTC_LINUX)
+#include <linux/if.h>
+#include <linux/route.h>
+#elif !defined(__native_client__)
+#include <net/if.h>
+#endif
+#endif  // WEBRTC_POSIX
+
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#include <Iphlpapi.h>
+#elif !defined(__native_client__)
+#include "webrtc/base/ifaddrs_converter.h"
+#endif
+
+#include <stdio.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/networkmonitor.h"
+#include "webrtc/base/socket.h"  // includes something that makes windows happy
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+namespace {
+
+// Turning on IPv6 could make many IPv6 interfaces available for connectivity
+// check and delay the call setup time. kMaxIPv6Networks is the default upper
+// limit of IPv6 networks but could be changed by set_max_ipv6_networks().
+const int kMaxIPv6Networks = 5;
+
+const uint32_t kUpdateNetworksMessage = 1;
+const uint32_t kSignalNetworksMessage = 2;
+
+// Fetch list of networks every two seconds.
+const int kNetworksUpdateIntervalMs = 2000;
+
+const int kHighestNetworkPreference = 127;
+
+typedef struct {
+  Network* net;
+  std::vector<InterfaceAddress> ips;
+} AddressList;
+
+bool CompareNetworks(const Network* a, const Network* b) {
+  if (a->prefix_length() == b->prefix_length()) {
+    if (a->name() == b->name()) {
+      return a->prefix() < b->prefix();
+    }
+  }
+  return a->name() < b->name();
+}
+
+bool SortNetworks(const Network* a, const Network* b) {
+  // Network types will be preferred above everything else while sorting
+  // Networks.
+
+  // Networks are sorted first by type.
+  if (a->type() != b->type()) {
+    return a->type() < b->type();
+  }
+
+  IPAddress ip_a = a->GetBestIP();
+  IPAddress ip_b = b->GetBestIP();
+
+  // After type, networks are sorted by IP address precedence values
+  // from RFC 3484-bis
+  if (IPAddressPrecedence(ip_a) != IPAddressPrecedence(ip_b)) {
+    return IPAddressPrecedence(ip_a) > IPAddressPrecedence(ip_b);
+  }
+
+  // TODO(mallinath) - Add VPN and Link speed conditions while sorting.
+
+  // Networks are sorted last by key.
+  return a->key() > b->key();
+}
+
+std::string AdapterTypeToString(AdapterType type) {
+  switch (type) {
+    case ADAPTER_TYPE_UNKNOWN:
+      return "Unknown";
+    case ADAPTER_TYPE_ETHERNET:
+      return "Ethernet";
+    case ADAPTER_TYPE_WIFI:
+      return "Wifi";
+    case ADAPTER_TYPE_CELLULAR:
+      return "Cellular";
+    case ADAPTER_TYPE_VPN:
+      return "VPN";
+    case ADAPTER_TYPE_LOOPBACK:
+      return "Loopback";
+    default:
+      RTC_NOTREACHED() << "Invalid type " << type;
+      return std::string();
+  }
+}
+
+#if !defined(__native_client__)
+bool IsIgnoredIPv6(const InterfaceAddress& ip) {
+  if (ip.family() != AF_INET6) {
+    return false;
+  }
+
+  // Link-local addresses require scope id to be bound successfully.
+  // However, our IPAddress structure doesn't carry that so the
+  // information is lost and causes binding failure.
+  if (IPIsLinkLocal(ip)) {
+    return true;
+  }
+
+  // Any MAC based IPv6 should be avoided to prevent the MAC tracking.
+  if (IPIsMacBased(ip)) {
+    return true;
+  }
+
+  // Ignore deprecated IPv6.
+  if (ip.ipv6_flags() & IPV6_ADDRESS_FLAG_DEPRECATED) {
+    return true;
+  }
+
+  return false;
+}
+#endif  // !defined(__native_client__)
+
+}  // namespace
+
+// These addresses are used as the targets to find out the default local address
+// on a multi-homed endpoint. They are actually DNS servers.
+const char kPublicIPv4Host[] = "8.8.8.8";
+const char kPublicIPv6Host[] = "2001:4860:4860::8888";
+const int kPublicPort = 53;  // DNS port.
+
+std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix,
+                           int prefix_length) {
+  std::ostringstream ost;
+  ost << name << "%" << prefix.ToString() << "/" << prefix_length;
+  return ost.str();
+}
+
+NetworkManager::NetworkManager() {
+}
+
+NetworkManager::~NetworkManager() {
+}
+
+NetworkManager::EnumerationPermission NetworkManager::enumeration_permission()
+    const {
+  return ENUMERATION_ALLOWED;
+}
+
+bool NetworkManager::GetDefaultLocalAddress(int family, IPAddress* addr) const {
+  return false;
+}
+
+NetworkManagerBase::NetworkManagerBase()
+    : enumeration_permission_(NetworkManager::ENUMERATION_ALLOWED),
+      max_ipv6_networks_(kMaxIPv6Networks),
+      ipv6_enabled_(true) {
+}
+
+NetworkManagerBase::~NetworkManagerBase() {
+  for (const auto& kv : networks_map_) {
+    delete kv.second;
+  }
+}
+
+NetworkManager::EnumerationPermission
+NetworkManagerBase::enumeration_permission() const {
+  return enumeration_permission_;
+}
+
+void NetworkManagerBase::GetAnyAddressNetworks(NetworkList* networks) {
+  if (!ipv4_any_address_network_) {
+    const rtc::IPAddress ipv4_any_address(INADDR_ANY);
+    ipv4_any_address_network_.reset(
+        new rtc::Network("any", "any", ipv4_any_address, 0));
+    ipv4_any_address_network_->set_default_local_address_provider(this);
+    ipv4_any_address_network_->AddIP(ipv4_any_address);
+  }
+  networks->push_back(ipv4_any_address_network_.get());
+
+  if (ipv6_enabled()) {
+    if (!ipv6_any_address_network_) {
+      const rtc::IPAddress ipv6_any_address(in6addr_any);
+      ipv6_any_address_network_.reset(
+          new rtc::Network("any", "any", ipv6_any_address, 0));
+      ipv6_any_address_network_->set_default_local_address_provider(this);
+      ipv6_any_address_network_->AddIP(ipv6_any_address);
+    }
+    networks->push_back(ipv6_any_address_network_.get());
+  }
+}
+
+void NetworkManagerBase::GetNetworks(NetworkList* result) const {
+  int ipv6_networks = 0;
+  result->clear();
+  for (Network* network : networks_) {
+    // Keep the number of IPv6 networks under |max_ipv6_networks_|.
+    if (network->prefix().family() == AF_INET6) {
+      if (ipv6_networks >= max_ipv6_networks_) {
+        continue;
+      }
+      ++ipv6_networks;
+    }
+    result->push_back(network);
+  }
+}
+
+void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks,
+                                          bool* changed) {
+  NetworkManager::Stats stats;
+  MergeNetworkList(new_networks, changed, &stats);
+}
+
+void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks,
+                                          bool* changed,
+                                          NetworkManager::Stats* stats) {
+  *changed = false;
+  // AddressList in this map will track IP addresses for all Networks
+  // with the same key.
+  std::map<std::string, AddressList> consolidated_address_list;
+  NetworkList list(new_networks);
+  std::sort(list.begin(), list.end(), CompareNetworks);
+  // First, build a set of network-keys to the ipaddresses.
+  for (Network* network : list) {
+    bool might_add_to_merged_list = false;
+    std::string key = MakeNetworkKey(network->name(),
+                                     network->prefix(),
+                                     network->prefix_length());
+    if (consolidated_address_list.find(key) ==
+        consolidated_address_list.end()) {
+      AddressList addrlist;
+      addrlist.net = network;
+      consolidated_address_list[key] = addrlist;
+      might_add_to_merged_list = true;
+    }
+    const std::vector<InterfaceAddress>& addresses = network->GetIPs();
+    AddressList& current_list = consolidated_address_list[key];
+    for (const InterfaceAddress& address : addresses) {
+      current_list.ips.push_back(address);
+    }
+    if (!might_add_to_merged_list) {
+      delete network;
+    } else {
+      if (current_list.ips[0].family() == AF_INET) {
+        stats->ipv4_network_count++;
+      } else {
+        RTC_DCHECK(current_list.ips[0].family() == AF_INET6);
+        stats->ipv6_network_count++;
+      }
+    }
+  }
+
+  // Next, look for existing network objects to re-use.
+  // Result of Network merge. Element in this list should have unique key.
+  NetworkList merged_list;
+  for (const auto& kv : consolidated_address_list) {
+    const std::string& key = kv.first;
+    Network* net = kv.second.net;
+    auto existing = networks_map_.find(key);
+    if (existing == networks_map_.end()) {
+      // This network is new. Place it in the network map.
+      merged_list.push_back(net);
+      networks_map_[key] = net;
+      net->set_id(next_available_network_id_++);
+      // Also, we might have accumulated IPAddresses from the first
+      // step, set it here.
+      net->SetIPs(kv.second.ips, true);
+      *changed = true;
+    } else {
+      // This network exists in the map already. Reset its IP addresses.
+      Network* existing_net = existing->second;
+      *changed = existing_net->SetIPs(kv.second.ips, *changed);
+      merged_list.push_back(existing_net);
+      if (net->type() != ADAPTER_TYPE_UNKNOWN &&
+          net->type() != existing_net->type()) {
+        existing_net->set_type(net->type());
+        *changed = true;
+      }
+      // If the existing network was not active, networks have changed.
+      if (!existing_net->active()) {
+        *changed = true;
+      }
+      RTC_DCHECK(net->active());
+      if (existing_net != net) {
+        delete net;
+      }
+    }
+  }
+  // It may still happen that the merged list is a subset of |networks_|.
+  // To detect this change, we compare their sizes.
+  if (merged_list.size() != networks_.size()) {
+    *changed = true;
+  }
+
+  // If the network list changes, we re-assign |networks_| to the merged list
+  // and re-sort it.
+  if (*changed) {
+    networks_ = merged_list;
+    // Reset the active states of all networks.
+    for (const auto& kv : networks_map_) {
+      Network* network = kv.second;
+      // If |network| is in the newly generated |networks_|, it is active.
+      bool found = std::find(networks_.begin(), networks_.end(), network) !=
+                   networks_.end();
+      network->set_active(found);
+    }
+    std::sort(networks_.begin(), networks_.end(), SortNetworks);
+    // Now network interfaces are sorted, we should set the preference value
+    // for each of the interfaces we are planning to use.
+    // Preference order of network interfaces might have changed from previous
+    // sorting due to addition of higher preference network interface.
+    // Since we have already sorted the network interfaces based on our
+    // requirements, we will just assign a preference value starting with 127,
+    // in decreasing order.
+    int pref = kHighestNetworkPreference;
+    for (Network* network : networks_) {
+      network->set_preference(pref);
+      if (pref > 0) {
+        --pref;
+      } else {
+        LOG(LS_ERROR) << "Too many network interfaces to handle!";
+        break;
+      }
+    }
+  }
+}
+
+void NetworkManagerBase::set_default_local_addresses(const IPAddress& ipv4,
+                                                     const IPAddress& ipv6) {
+  if (ipv4.family() == AF_INET) {
+    default_local_ipv4_address_ = ipv4;
+  }
+  if (ipv6.family() == AF_INET6) {
+    default_local_ipv6_address_ = ipv6;
+  }
+}
+
+bool NetworkManagerBase::GetDefaultLocalAddress(int family,
+                                                IPAddress* ipaddr) const {
+  if (family == AF_INET && !default_local_ipv4_address_.IsNil()) {
+    *ipaddr = default_local_ipv4_address_;
+    return true;
+  } else if (family == AF_INET6 && !default_local_ipv6_address_.IsNil()) {
+    Network* ipv6_network = GetNetworkFromAddress(default_local_ipv6_address_);
+    if (ipv6_network) {
+      // If the default ipv6 network's BestIP is different than
+      // default_local_ipv6_address_, use it instead.
+      // This is to prevent potential IP address leakage. See WebRTC bug 5376.
+      *ipaddr = ipv6_network->GetBestIP();
+    } else {
+      *ipaddr = default_local_ipv6_address_;
+    }
+    return true;
+  }
+  return false;
+}
+
+Network* NetworkManagerBase::GetNetworkFromAddress(
+    const rtc::IPAddress& ip) const {
+  for (Network* network : networks_) {
+    const auto& ips = network->GetIPs();
+    if (std::find_if(ips.begin(), ips.end(),
+                     [ip](const InterfaceAddress& existing_ip) {
+                       return ip == static_cast<rtc::IPAddress>(existing_ip);
+                     }) != ips.end()) {
+      return network;
+    }
+  }
+  return nullptr;
+}
+
+BasicNetworkManager::BasicNetworkManager()
+    : thread_(nullptr),
+      sent_first_update_(false),
+      start_count_(0),
+      ignore_non_default_routes_(false) {}
+
+BasicNetworkManager::~BasicNetworkManager() {
+}
+
+void BasicNetworkManager::OnNetworksChanged() {
+  LOG(LS_INFO) << "Network change was observed";
+  UpdateNetworksOnce();
+}
+
+#if defined(__native_client__)
+
+bool BasicNetworkManager::CreateNetworks(bool include_ignored,
+                                         NetworkList* networks) const {
+  RTC_NOTREACHED();
+  LOG(LS_WARNING) << "BasicNetworkManager doesn't work on NaCl yet";
+  return false;
+}
+
+#elif defined(WEBRTC_POSIX)
+void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces,
+                                         IfAddrsConverter* ifaddrs_converter,
+                                         bool include_ignored,
+                                         NetworkList* networks) const {
+  NetworkMap current_networks;
+
+  for (struct ifaddrs* cursor = interfaces; cursor != nullptr;
+       cursor = cursor->ifa_next) {
+    IPAddress prefix;
+    IPAddress mask;
+    InterfaceAddress ip;
+    int scope_id = 0;
+
+    // Some interfaces may not have address assigned.
+    if (!cursor->ifa_addr || !cursor->ifa_netmask) {
+      continue;
+    }
+    // Skip ones which are down.
+    if (!(cursor->ifa_flags & IFF_RUNNING)) {
+      continue;
+    }
+    // Skip unknown family.
+    if (cursor->ifa_addr->sa_family != AF_INET &&
+        cursor->ifa_addr->sa_family != AF_INET6) {
+      continue;
+    }
+    // Skip IPv6 if not enabled.
+    if (cursor->ifa_addr->sa_family == AF_INET6 && !ipv6_enabled()) {
+      continue;
+    }
+    // Convert to InterfaceAddress.
+    if (!ifaddrs_converter->ConvertIfAddrsToIPAddress(cursor, &ip, &mask)) {
+      continue;
+    }
+
+    // Special case for IPv6 address.
+    if (cursor->ifa_addr->sa_family == AF_INET6) {
+      if (IsIgnoredIPv6(ip)) {
+        continue;
+      }
+      scope_id =
+          reinterpret_cast<sockaddr_in6*>(cursor->ifa_addr)->sin6_scope_id;
+    }
+
+    AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN;
+    if (cursor->ifa_flags & IFF_LOOPBACK) {
+      adapter_type = ADAPTER_TYPE_LOOPBACK;
+    } else {
+      adapter_type = GetAdapterTypeFromName(cursor->ifa_name);
+    }
+    int prefix_length = CountIPMaskBits(mask);
+    prefix = TruncateIP(ip, prefix_length);
+    std::string key = MakeNetworkKey(std::string(cursor->ifa_name),
+                                     prefix, prefix_length);
+    auto iter = current_networks.find(key);
+    if (iter == current_networks.end()) {
+      // TODO(phoglund): Need to recognize other types as well.
+      std::unique_ptr<Network> network(
+          new Network(cursor->ifa_name, cursor->ifa_name, prefix, prefix_length,
+                      adapter_type));
+      network->set_default_local_address_provider(this);
+      network->set_scope_id(scope_id);
+      network->AddIP(ip);
+      network->set_ignored(IsIgnoredNetwork(*network));
+      if (include_ignored || !network->ignored()) {
+        current_networks[key] = network.get();
+        networks->push_back(network.release());
+      }
+    } else {
+      Network* existing_network = iter->second;
+      existing_network->AddIP(ip);
+      if (adapter_type != ADAPTER_TYPE_UNKNOWN) {
+        existing_network->set_type(adapter_type);
+      }
+    }
+  }
+}
+
+bool BasicNetworkManager::CreateNetworks(bool include_ignored,
+                                         NetworkList* networks) const {
+  struct ifaddrs* interfaces;
+  int error = getifaddrs(&interfaces);
+  if (error != 0) {
+    LOG_ERR(LERROR) << "getifaddrs failed to gather interface data: " << error;
+    return false;
+  }
+
+  std::unique_ptr<IfAddrsConverter> ifaddrs_converter(CreateIfAddrsConverter());
+  ConvertIfAddrs(interfaces, ifaddrs_converter.get(), include_ignored,
+                 networks);
+
+  freeifaddrs(interfaces);
+  return true;
+}
+
+#elif defined(WEBRTC_WIN)
+
+unsigned int GetPrefix(PIP_ADAPTER_PREFIX prefixlist,
+              const IPAddress& ip, IPAddress* prefix) {
+  IPAddress current_prefix;
+  IPAddress best_prefix;
+  unsigned int best_length = 0;
+  while (prefixlist) {
+    // Look for the longest matching prefix in the prefixlist.
+    if (prefixlist->Address.lpSockaddr == nullptr ||
+        prefixlist->Address.lpSockaddr->sa_family != ip.family()) {
+      prefixlist = prefixlist->Next;
+      continue;
+    }
+    switch (prefixlist->Address.lpSockaddr->sa_family) {
+      case AF_INET: {
+        sockaddr_in* v4_addr =
+            reinterpret_cast<sockaddr_in*>(prefixlist->Address.lpSockaddr);
+        current_prefix = IPAddress(v4_addr->sin_addr);
+        break;
+      }
+      case AF_INET6: {
+          sockaddr_in6* v6_addr =
+              reinterpret_cast<sockaddr_in6*>(prefixlist->Address.lpSockaddr);
+          current_prefix = IPAddress(v6_addr->sin6_addr);
+          break;
+      }
+      default: {
+        prefixlist = prefixlist->Next;
+        continue;
+      }
+    }
+    if (TruncateIP(ip, prefixlist->PrefixLength) == current_prefix &&
+        prefixlist->PrefixLength > best_length) {
+      best_prefix = current_prefix;
+      best_length = prefixlist->PrefixLength;
+    }
+    prefixlist = prefixlist->Next;
+  }
+  *prefix = best_prefix;
+  return best_length;
+}
+
+bool BasicNetworkManager::CreateNetworks(bool include_ignored,
+                                         NetworkList* networks) const {
+  NetworkMap current_networks;
+  // MSDN recommends a 15KB buffer for the first try at GetAdaptersAddresses.
+  size_t buffer_size = 16384;
+  std::unique_ptr<char[]> adapter_info(new char[buffer_size]);
+  PIP_ADAPTER_ADDRESSES adapter_addrs =
+      reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_info.get());
+  int adapter_flags = (GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_ANYCAST |
+                       GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_INCLUDE_PREFIX);
+  int ret = 0;
+  do {
+    adapter_info.reset(new char[buffer_size]);
+    adapter_addrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(adapter_info.get());
+    ret = GetAdaptersAddresses(AF_UNSPEC, adapter_flags,
+                               0, adapter_addrs,
+                               reinterpret_cast<PULONG>(&buffer_size));
+  } while (ret == ERROR_BUFFER_OVERFLOW);
+  if (ret != ERROR_SUCCESS) {
+    return false;
+  }
+  int count = 0;
+  while (adapter_addrs) {
+    if (adapter_addrs->OperStatus == IfOperStatusUp) {
+      PIP_ADAPTER_UNICAST_ADDRESS address = adapter_addrs->FirstUnicastAddress;
+      PIP_ADAPTER_PREFIX prefixlist = adapter_addrs->FirstPrefix;
+      std::string name;
+      std::string description;
+#if !defined(NDEBUG)
+      name = ToUtf8(adapter_addrs->FriendlyName,
+                    wcslen(adapter_addrs->FriendlyName));
+#endif
+      description = ToUtf8(adapter_addrs->Description,
+                           wcslen(adapter_addrs->Description));
+      for (; address; address = address->Next) {
+#if defined(NDEBUG)
+        name = rtc::ToString(count);
+#endif
+
+        IPAddress ip;
+        int scope_id = 0;
+        std::unique_ptr<Network> network;
+        switch (address->Address.lpSockaddr->sa_family) {
+          case AF_INET: {
+            sockaddr_in* v4_addr =
+                reinterpret_cast<sockaddr_in*>(address->Address.lpSockaddr);
+            ip = IPAddress(v4_addr->sin_addr);
+            break;
+          }
+          case AF_INET6: {
+            if (ipv6_enabled()) {
+              sockaddr_in6* v6_addr =
+                  reinterpret_cast<sockaddr_in6*>(address->Address.lpSockaddr);
+              scope_id = v6_addr->sin6_scope_id;
+              ip = IPAddress(v6_addr->sin6_addr);
+
+              if (IsIgnoredIPv6(ip)) {
+                continue;
+              }
+
+              break;
+            } else {
+              continue;
+            }
+          }
+          default: {
+            continue;
+          }
+        }
+
+        IPAddress prefix;
+        int prefix_length = GetPrefix(prefixlist, ip, &prefix);
+        std::string key = MakeNetworkKey(name, prefix, prefix_length);
+        auto existing_network = current_networks.find(key);
+        if (existing_network == current_networks.end()) {
+          AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN;
+          if (adapter_addrs->IfType == IF_TYPE_SOFTWARE_LOOPBACK) {
+            // TODO(phoglund): Need to recognize other types as well.
+            adapter_type = ADAPTER_TYPE_LOOPBACK;
+          }
+          std::unique_ptr<Network> network(new Network(
+              name, description, prefix, prefix_length, adapter_type));
+          network->set_default_local_address_provider(this);
+          network->set_scope_id(scope_id);
+          network->AddIP(ip);
+          bool ignored = IsIgnoredNetwork(*network);
+          network->set_ignored(ignored);
+          if (include_ignored || !network->ignored()) {
+            current_networks[key] = network.get();
+            networks->push_back(network.release());
+          }
+        } else {
+          (*existing_network).second->AddIP(ip);
+        }
+      }
+      // Count is per-adapter - all 'Networks' created from the same
+      // adapter need to have the same name.
+      ++count;
+    }
+    adapter_addrs = adapter_addrs->Next;
+  }
+  return true;
+}
+#endif  // WEBRTC_WIN
+
+#if defined(WEBRTC_LINUX)
+bool IsDefaultRoute(const std::string& network_name) {
+  FileStream fs;
+  if (!fs.Open("/proc/net/route", "r", nullptr)) {
+    LOG(LS_WARNING) << "Couldn't read /proc/net/route, skipping default "
+                    << "route check (assuming everything is a default route).";
+    return true;
+  } else {
+    std::string line;
+    while (fs.ReadLine(&line) == SR_SUCCESS) {
+      char iface_name[256];
+      unsigned int iface_ip, iface_gw, iface_mask, iface_flags;
+      if (sscanf(line.c_str(),
+                 "%255s %8X %8X %4X %*d %*u %*d %8X",
+                 iface_name, &iface_ip, &iface_gw,
+                 &iface_flags, &iface_mask) == 5 &&
+          network_name == iface_name &&
+          iface_mask == 0 &&
+          (iface_flags & (RTF_UP | RTF_HOST)) == RTF_UP) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+#endif
+
+bool BasicNetworkManager::IsIgnoredNetwork(const Network& network) const {
+  // Ignore networks on the explicit ignore list.
+  for (const std::string& ignored_name : network_ignore_list_) {
+    if (network.name() == ignored_name) {
+      return true;
+    }
+  }
+
+#if defined(WEBRTC_POSIX)
+  // Filter out VMware/VirtualBox interfaces, typically named vmnet1,
+  // vmnet8, or vboxnet0.
+  if (strncmp(network.name().c_str(), "vmnet", 5) == 0 ||
+      strncmp(network.name().c_str(), "vnic", 4) == 0 ||
+      strncmp(network.name().c_str(), "vboxnet", 7) == 0) {
+    return true;
+  }
+#if defined(WEBRTC_LINUX)
+  // Make sure this is a default route, if we're ignoring non-defaults.
+  if (ignore_non_default_routes_ && !IsDefaultRoute(network.name())) {
+    return true;
+  }
+#endif
+#elif defined(WEBRTC_WIN)
+  // Ignore any HOST side vmware adapters with a description like:
+  // VMware Virtual Ethernet Adapter for VMnet1
+  // but don't ignore any GUEST side adapters with a description like:
+  // VMware Accelerated AMD PCNet Adapter #2
+  if (strstr(network.description().c_str(), "VMnet") != nullptr) {
+    return true;
+  }
+#endif
+
+  // Ignore any networks with a 0.x.y.z IP
+  if (network.prefix().family() == AF_INET) {
+    return (network.prefix().v4AddressAsHostOrderInteger() < 0x01000000);
+  }
+
+  return false;
+}
+
+void BasicNetworkManager::StartUpdating() {
+  thread_ = Thread::Current();
+  if (start_count_) {
+    // If network interfaces are already discovered and signal is sent,
+    // we should trigger network signal immediately for the new clients
+    // to start allocating ports.
+    if (sent_first_update_)
+      thread_->Post(RTC_FROM_HERE, this, kSignalNetworksMessage);
+  } else {
+    thread_->Post(RTC_FROM_HERE, this, kUpdateNetworksMessage);
+    StartNetworkMonitor();
+  }
+  ++start_count_;
+}
+
+void BasicNetworkManager::StopUpdating() {
+  RTC_DCHECK(Thread::Current() == thread_);
+  if (!start_count_)
+    return;
+
+  --start_count_;
+  if (!start_count_) {
+    thread_->Clear(this);
+    sent_first_update_ = false;
+    StopNetworkMonitor();
+  }
+}
+
+void BasicNetworkManager::StartNetworkMonitor() {
+  NetworkMonitorFactory* factory = NetworkMonitorFactory::GetFactory();
+  if (factory == nullptr) {
+    return;
+  }
+  if (!network_monitor_) {
+    network_monitor_.reset(factory->CreateNetworkMonitor());
+    if (!network_monitor_) {
+      return;
+    }
+    network_monitor_->SignalNetworksChanged.connect(
+        this, &BasicNetworkManager::OnNetworksChanged);
+  }
+  network_monitor_->Start();
+}
+
+void BasicNetworkManager::StopNetworkMonitor() {
+  if (!network_monitor_) {
+    return;
+  }
+  network_monitor_->Stop();
+}
+
+void BasicNetworkManager::OnMessage(Message* msg) {
+  switch (msg->message_id) {
+    case kUpdateNetworksMessage: {
+      UpdateNetworksContinually();
+      break;
+    }
+    case kSignalNetworksMessage:  {
+      SignalNetworksChanged();
+      break;
+    }
+    default:
+      RTC_NOTREACHED();
+  }
+}
+
+AdapterType BasicNetworkManager::GetAdapterTypeFromName(
+    const char* network_name) const {
+  // If there is a network_monitor, use it to get the adapter type.
+  // Otherwise, get the adapter type based on a few name matching rules.
+  if (network_monitor_) {
+    AdapterType type = network_monitor_->GetAdapterType(network_name);
+    if (type != ADAPTER_TYPE_UNKNOWN) {
+      return type;
+    }
+  }
+#if defined(WEBRTC_IOS)
+  // Cell networks are pdp_ipN on iOS.
+  if (strncmp(network_name, "pdp_ip", 6) == 0) {
+    return ADAPTER_TYPE_CELLULAR;
+  }
+  if (strncmp(network_name, "en", 2) == 0) {
+    // This may not be most accurate because sometimes Ethernet interface
+    // name also starts with "en" but it is better than showing it as
+    // "unknown" type.
+    // TODO(honghaiz): Write a proper IOS network manager.
+    return ADAPTER_TYPE_WIFI;
+  }
+#elif defined(WEBRTC_ANDROID)
+  if (strncmp(network_name, "rmnet", 5) == 0 ||
+      strncmp(network_name, "v4-rmnet", 8) == 0) {
+    return ADAPTER_TYPE_CELLULAR;
+  }
+  if (strncmp(network_name, "wlan", 4) == 0) {
+    return ADAPTER_TYPE_WIFI;
+  }
+#endif
+
+  return ADAPTER_TYPE_UNKNOWN;
+}
+
+IPAddress BasicNetworkManager::QueryDefaultLocalAddress(int family) const {
+  RTC_DCHECK(thread_ == Thread::Current());
+  RTC_DCHECK(thread_->socketserver() != nullptr);
+  RTC_DCHECK(family == AF_INET || family == AF_INET6);
+
+  std::unique_ptr<AsyncSocket> socket(
+      thread_->socketserver()->CreateAsyncSocket(family, SOCK_DGRAM));
+  if (!socket) {
+    LOG_ERR(LERROR) << "Socket creation failed";
+    return IPAddress();
+  }
+
+  if (socket->Connect(SocketAddress(
+          family == AF_INET ? kPublicIPv4Host : kPublicIPv6Host, kPublicPort)) <
+      0) {
+    if (socket->GetError() != ENETUNREACH
+        && socket->GetError() != EHOSTUNREACH) {
+      // Ignore the expected case of "host/net unreachable" - which happens if
+      // the network is V4- or V6-only.
+      LOG(LS_INFO) << "Connect failed with " << socket->GetError();
+    }
+    return IPAddress();
+  }
+  return socket->GetLocalAddress().ipaddr();
+}
+
+void BasicNetworkManager::UpdateNetworksOnce() {
+  if (!start_count_)
+    return;
+
+  RTC_DCHECK(Thread::Current() == thread_);
+
+  NetworkList list;
+  if (!CreateNetworks(false, &list)) {
+    SignalError();
+  } else {
+    bool changed;
+    NetworkManager::Stats stats;
+    MergeNetworkList(list, &changed, &stats);
+    set_default_local_addresses(QueryDefaultLocalAddress(AF_INET),
+                                QueryDefaultLocalAddress(AF_INET6));
+    if (changed || !sent_first_update_) {
+      SignalNetworksChanged();
+      sent_first_update_ = true;
+    }
+  }
+}
+
+void BasicNetworkManager::UpdateNetworksContinually() {
+  UpdateNetworksOnce();
+  thread_->PostDelayed(RTC_FROM_HERE, kNetworksUpdateIntervalMs, this,
+                       kUpdateNetworksMessage);
+}
+
+void BasicNetworkManager::DumpNetworks() {
+  NetworkList list;
+  GetNetworks(&list);
+  LOG(LS_INFO) << "NetworkManager detected " << list.size() << " networks:";
+  for (const Network* network : list) {
+    LOG(LS_INFO) << network->ToString() << ": " << network->description()
+                 << ", active ? " << network->active()
+                 << ((network->ignored()) ? ", Ignored" : "");
+  }
+}
+
+Network::Network(const std::string& name,
+                 const std::string& desc,
+                 const IPAddress& prefix,
+                 int prefix_length)
+    : name_(name),
+      description_(desc),
+      prefix_(prefix),
+      prefix_length_(prefix_length),
+      key_(MakeNetworkKey(name, prefix, prefix_length)),
+      scope_id_(0),
+      ignored_(false),
+      type_(ADAPTER_TYPE_UNKNOWN),
+      preference_(0) {}
+
+Network::Network(const std::string& name,
+                 const std::string& desc,
+                 const IPAddress& prefix,
+                 int prefix_length,
+                 AdapterType type)
+    : name_(name),
+      description_(desc),
+      prefix_(prefix),
+      prefix_length_(prefix_length),
+      key_(MakeNetworkKey(name, prefix, prefix_length)),
+      scope_id_(0),
+      ignored_(false),
+      type_(type),
+      preference_(0) {}
+
+Network::~Network() = default;
+
+// Sets the addresses of this network. Returns true if the address set changed.
+// Change detection is short circuited if the changed argument is true.
+bool Network::SetIPs(const std::vector<InterfaceAddress>& ips, bool changed) {
+  // Detect changes with a nested loop; n-squared but we expect on the order
+  // of 2-3 addresses per network.
+  changed = changed || ips.size() != ips_.size();
+  if (!changed) {
+    for (const InterfaceAddress& ip : ips) {
+      if (std::find(ips_.begin(), ips_.end(), ip) == ips_.end()) {
+        changed = true;
+        break;
+      }
+    }
+  }
+
+  ips_ = ips;
+  return changed;
+}
+
+// Select the best IP address to use from this Network.
+IPAddress Network::GetBestIP() const {
+  if (ips_.size() == 0) {
+    return IPAddress();
+  }
+
+  if (prefix_.family() == AF_INET) {
+    return static_cast<IPAddress>(ips_.at(0));
+  }
+
+  InterfaceAddress selected_ip, ula_ip;
+
+  for (const InterfaceAddress& ip : ips_) {
+    // Ignore any address which has been deprecated already.
+    if (ip.ipv6_flags() & IPV6_ADDRESS_FLAG_DEPRECATED)
+      continue;
+
+    // ULA address should only be returned when we have no other
+    // global IP.
+    if (IPIsULA(static_cast<const IPAddress&>(ip))) {
+      ula_ip = ip;
+      continue;
+    }
+    selected_ip = ip;
+
+    // Search could stop once a temporary non-deprecated one is found.
+    if (ip.ipv6_flags() & IPV6_ADDRESS_FLAG_TEMPORARY)
+      break;
+  }
+
+  // No proper global IPv6 address found, use ULA instead.
+  if (IPIsUnspec(selected_ip) && !IPIsUnspec(ula_ip)) {
+    selected_ip = ula_ip;
+  }
+
+  return static_cast<IPAddress>(selected_ip);
+}
+
+std::string Network::ToString() const {
+  std::stringstream ss;
+  // Print out the first space-terminated token of the network desc, plus
+  // the IP address.
+  ss << "Net[" << description_.substr(0, description_.find(' '))
+     << ":" << prefix_.ToSensitiveString() << "/" << prefix_length_
+     << ":" << AdapterTypeToString(type_) << "]";
+  return ss.str();
+}
+
+}  // namespace rtc
diff --git a/base/network.h b/base/network.h
index 2953098..34d6538 100644
--- a/base/network.h
+++ b/base/network.h
@@ -11,9 +11,424 @@
 #ifndef WEBRTC_BASE_NETWORK_H_
 #define WEBRTC_BASE_NETWORK_H_
 
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/network.h"
+#include <deque>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "webrtc/base/ipaddress.h"
+#include "webrtc/base/networkmonitor.h"
+#include "webrtc/base/messagehandler.h"
+#include "webrtc/base/sigslot.h"
+
+#if defined(WEBRTC_POSIX)
+struct ifaddrs;
+#endif  // defined(WEBRTC_POSIX)
+
+namespace rtc {
+
+extern const char kPublicIPv4Host[];
+extern const char kPublicIPv6Host[];
+
+class IfAddrsConverter;
+class Network;
+class NetworkMonitorInterface;
+class Thread;
+
+static const uint16_t kNetworkCostMax = 999;
+static const uint16_t kNetworkCostHigh = 900;
+static const uint16_t kNetworkCostUnknown = 50;
+static const uint16_t kNetworkCostLow = 10;
+static const uint16_t kNetworkCostMin = 0;
+
+// By default, ignore loopback interfaces on the host.
+const int kDefaultNetworkIgnoreMask = ADAPTER_TYPE_LOOPBACK;
+
+// Makes a string key for this network. Used in the network manager's maps.
+// Network objects are keyed on interface name, network prefix and the
+// length of that prefix.
+std::string MakeNetworkKey(const std::string& name, const IPAddress& prefix,
+                           int prefix_length);
+
+class DefaultLocalAddressProvider {
+ public:
+  virtual ~DefaultLocalAddressProvider() = default;
+  // The default local address is the local address used in multi-homed endpoint
+  // when the any address (0.0.0.0 or ::) is used as the local address. It's
+  // important to check the return value as a IP family may not be enabled.
+  virtual bool GetDefaultLocalAddress(int family, IPAddress* ipaddr) const = 0;
+};
+
+// Generic network manager interface. It provides list of local
+// networks.
+//
+// Every method of NetworkManager (including the destructor) must be called on
+// the same thread, except for the constructor which may be called on any
+// thread.
+//
+// This allows constructing a NetworkManager subclass on one thread and
+// passing it into an object that uses it on a different thread.
+class NetworkManager : public DefaultLocalAddressProvider {
+ public:
+  typedef std::vector<Network*> NetworkList;
+
+  // This enum indicates whether adapter enumeration is allowed.
+  enum EnumerationPermission {
+    ENUMERATION_ALLOWED,  // Adapter enumeration is allowed. Getting 0 network
+                          // from GetNetworks means that there is no network
+                          // available.
+    ENUMERATION_BLOCKED,  // Adapter enumeration is disabled.
+                          // GetAnyAddressNetworks() should be used instead.
+  };
+
+  NetworkManager();
+  ~NetworkManager() override;
+
+  // Called when network list is updated.
+  sigslot::signal0<> SignalNetworksChanged;
+
+  // Indicates a failure when getting list of network interfaces.
+  sigslot::signal0<> SignalError;
+
+  // This should be called on the NetworkManager's thread before the
+  // NetworkManager is used. Subclasses may override this if necessary.
+  virtual void Initialize() {}
+
+  // Start/Stop monitoring of network interfaces
+  // list. SignalNetworksChanged or SignalError is emitted immediately
+  // after StartUpdating() is called. After that SignalNetworksChanged
+  // is emitted whenever list of networks changes.
+  virtual void StartUpdating() = 0;
+  virtual void StopUpdating() = 0;
+
+  // Returns the current list of networks available on this machine.
+  // StartUpdating() must be called before this method is called.
+  // It makes sure that repeated calls return the same object for a
+  // given network, so that quality is tracked appropriately. Does not
+  // include ignored networks.
+  virtual void GetNetworks(NetworkList* networks) const = 0;
+
+  // return the current permission state of GetNetworks()
+  virtual EnumerationPermission enumeration_permission() const;
+
+  // "AnyAddressNetwork" is a network which only contains single "any address"
+  // IP address.  (i.e. INADDR_ANY for IPv4 or in6addr_any for IPv6). This is
+  // useful as binding to such interfaces allow default routing behavior like
+  // http traffic.
+  //
+  // This method appends the "any address" networks to the list, such that this
+  // can optionally be called after GetNetworks.
+  //
+  // TODO(guoweis): remove this body when chromium implements this.
+  virtual void GetAnyAddressNetworks(NetworkList* networks) {}
+
+  // Dumps the current list of networks in the network manager.
+  virtual void DumpNetworks() {}
+  bool GetDefaultLocalAddress(int family, IPAddress* ipaddr) const override;
+
+  struct Stats {
+    int ipv4_network_count;
+    int ipv6_network_count;
+    Stats() {
+      ipv4_network_count = 0;
+      ipv6_network_count = 0;
+    }
+  };
+};
+
+// Base class for NetworkManager implementations.
+class NetworkManagerBase : public NetworkManager {
+ public:
+  NetworkManagerBase();
+  ~NetworkManagerBase() override;
+
+  void GetNetworks(NetworkList* networks) const override;
+  void GetAnyAddressNetworks(NetworkList* networks) override;
+  // Defaults to true.
+  bool ipv6_enabled() const { return ipv6_enabled_; }
+  void set_ipv6_enabled(bool enabled) { ipv6_enabled_ = enabled; }
+
+  void set_max_ipv6_networks(int networks) { max_ipv6_networks_ = networks; }
+  int max_ipv6_networks() { return max_ipv6_networks_; }
+
+  EnumerationPermission enumeration_permission() const override;
+
+  bool GetDefaultLocalAddress(int family, IPAddress* ipaddr) const override;
+
+ protected:
+  typedef std::map<std::string, Network*> NetworkMap;
+  // Updates |networks_| with the networks listed in |list|. If
+  // |network_map_| already has a Network object for a network listed
+  // in the |list| then it is reused. Accept ownership of the Network
+  // objects in the |list|. |changed| will be set to true if there is
+  // any change in the network list.
+  void MergeNetworkList(const NetworkList& list, bool* changed);
+
+  // |stats| will be populated even if |*changed| is false.
+  void MergeNetworkList(const NetworkList& list,
+                        bool* changed,
+                        NetworkManager::Stats* stats);
+
+  void set_enumeration_permission(EnumerationPermission state) {
+    enumeration_permission_ = state;
+  }
+
+  void set_default_local_addresses(const IPAddress& ipv4,
+                                   const IPAddress& ipv6);
+
+ private:
+  friend class NetworkTest;
+
+  Network* GetNetworkFromAddress(const rtc::IPAddress& ip) const;
+
+  EnumerationPermission enumeration_permission_;
+
+  NetworkList networks_;
+  int max_ipv6_networks_;
+
+  NetworkMap networks_map_;
+  bool ipv6_enabled_;
+
+  std::unique_ptr<rtc::Network> ipv4_any_address_network_;
+  std::unique_ptr<rtc::Network> ipv6_any_address_network_;
+
+  IPAddress default_local_ipv4_address_;
+  IPAddress default_local_ipv6_address_;
+  // We use 16 bits to save the bandwidth consumption when sending the network
+  // id over the Internet. It is OK that the 16-bit integer overflows to get a
+  // network id 0 because we only compare the network ids in the old and the new
+  // best connections in the transport channel.
+  uint16_t next_available_network_id_ = 1;
+};
+
+// Basic implementation of the NetworkManager interface that gets list
+// of networks using OS APIs.
+class BasicNetworkManager : public NetworkManagerBase,
+                            public MessageHandler,
+                            public sigslot::has_slots<> {
+ public:
+  BasicNetworkManager();
+  ~BasicNetworkManager() override;
+
+  void StartUpdating() override;
+  void StopUpdating() override;
+
+  void DumpNetworks() override;
+
+  // MessageHandler interface.
+  void OnMessage(Message* msg) override;
+  bool started() { return start_count_ > 0; }
+
+  // Sets the network ignore list, which is empty by default. Any network on the
+  // ignore list will be filtered from network enumeration results.
+  void set_network_ignore_list(const std::vector<std::string>& list) {
+    network_ignore_list_ = list;
+  }
+
+#if defined(WEBRTC_LINUX)
+  // Sets the flag for ignoring non-default routes.
+  void set_ignore_non_default_routes(bool value) {
+    ignore_non_default_routes_ = true;
+  }
+#endif
+
+ protected:
+#if defined(WEBRTC_POSIX)
+  // Separated from CreateNetworks for tests.
+  void ConvertIfAddrs(ifaddrs* interfaces,
+                      IfAddrsConverter* converter,
+                      bool include_ignored,
+                      NetworkList* networks) const;
+#endif  // defined(WEBRTC_POSIX)
+
+  // Creates a network object for each network available on the machine.
+  bool CreateNetworks(bool include_ignored, NetworkList* networks) const;
+
+  // Determines if a network should be ignored. This should only be determined
+  // based on the network's property instead of any individual IP.
+  bool IsIgnoredNetwork(const Network& network) const;
+
+  // This function connects a UDP socket to a public address and returns the
+  // local address associated it. Since it binds to the "any" address
+  // internally, it returns the default local address on a multi-homed endpoint.
+  IPAddress QueryDefaultLocalAddress(int family) const;
+
+ private:
+  friend class NetworkTest;
+
+  // Creates a network monitor and listens for network updates.
+  void StartNetworkMonitor();
+  // Stops and removes the network monitor.
+  void StopNetworkMonitor();
+  // Called when it receives updates from the network monitor.
+  void OnNetworksChanged();
+
+  // Updates the networks and reschedules the next update.
+  void UpdateNetworksContinually();
+  // Only updates the networks; does not reschedule the next update.
+  void UpdateNetworksOnce();
+
+  AdapterType GetAdapterTypeFromName(const char* network_name) const;
+
+  Thread* thread_;
+  bool sent_first_update_;
+  int start_count_;
+  std::vector<std::string> network_ignore_list_;
+  bool ignore_non_default_routes_;
+  std::unique_ptr<NetworkMonitorInterface> network_monitor_;
+};
+
+// Represents a Unix-type network interface, with a name and single address.
+class Network {
+ public:
+  Network(const std::string& name,
+          const std::string& description,
+          const IPAddress& prefix,
+          int prefix_length);
+
+  Network(const std::string& name,
+          const std::string& description,
+          const IPAddress& prefix,
+          int prefix_length,
+          AdapterType type);
+  ~Network();
+
+  sigslot::signal1<const Network*> SignalTypeChanged;
+
+  const DefaultLocalAddressProvider* default_local_address_provider() {
+    return default_local_address_provider_;
+  }
+  void set_default_local_address_provider(
+      const DefaultLocalAddressProvider* provider) {
+    default_local_address_provider_ = provider;
+  }
+
+  // Returns the name of the interface this network is associated wtih.
+  const std::string& name() const { return name_; }
+
+  // Returns the OS-assigned name for this network. This is useful for
+  // debugging but should not be sent over the wire (for privacy reasons).
+  const std::string& description() const { return description_; }
+
+  // Returns the prefix for this network.
+  const IPAddress& prefix() const { return prefix_; }
+  // Returns the length, in bits, of this network's prefix.
+  int prefix_length() const { return prefix_length_; }
+
+  // |key_| has unique value per network interface. Used in sorting network
+  // interfaces. Key is derived from interface name and it's prefix.
+  std::string key() const { return key_; }
+
+  // Returns the Network's current idea of the 'best' IP it has.
+  // Or return an unset IP if this network has no active addresses.
+  // Here is the rule on how we mark the IPv6 address as ignorable for WebRTC.
+  // 1) return all global temporary dynamic and non-deprecrated ones.
+  // 2) if #1 not available, return global ones.
+  // 3) if #2 not available, use ULA ipv6 as last resort. (ULA stands
+  // for unique local address, which is not route-able in open
+  // internet but might be useful for a close WebRTC deployment.
+
+  // TODO(guoweis): rule #3 actually won't happen at current
+  // implementation. The reason being that ULA address starting with
+  // 0xfc 0r 0xfd will be grouped into its own Network. The result of
+  // that is WebRTC will have one extra Network to generate candidates
+  // but the lack of rule #3 shouldn't prevent turning on IPv6 since
+  // ULA should only be tried in a close deployment anyway.
+
+  // Note that when not specifying any flag, it's treated as case global
+  // IPv6 address
+  IPAddress GetBestIP() const;
+
+  // Keep the original function here for now.
+  // TODO(guoweis): Remove this when all callers are migrated to GetBestIP().
+  IPAddress ip() const { return GetBestIP(); }
+
+  // Adds an active IP address to this network. Does not check for duplicates.
+  void AddIP(const InterfaceAddress& ip) { ips_.push_back(ip); }
+
+  // Sets the network's IP address list. Returns true if new IP addresses were
+  // detected. Passing true to already_changed skips this check.
+  bool SetIPs(const std::vector<InterfaceAddress>& ips, bool already_changed);
+  // Get the list of IP Addresses associated with this network.
+  const std::vector<InterfaceAddress>& GetIPs() const { return ips_;}
+  // Clear the network's list of addresses.
+  void ClearIPs() { ips_.clear(); }
+
+  // Returns the scope-id of the network's address.
+  // Should only be relevant for link-local IPv6 addresses.
+  int scope_id() const { return scope_id_; }
+  void set_scope_id(int id) { scope_id_ = id; }
+
+  // Indicates whether this network should be ignored, perhaps because
+  // the IP is 0, or the interface is one we know is invalid.
+  bool ignored() const { return ignored_; }
+  void set_ignored(bool ignored) { ignored_ = ignored; }
+
+  AdapterType type() const { return type_; }
+  void set_type(AdapterType type) {
+    if (type_ == type) {
+      return;
+    }
+    type_ = type;
+    SignalTypeChanged(this);
+  }
+
+  uint16_t GetCost() const {
+    switch (type_) {
+      case rtc::ADAPTER_TYPE_ETHERNET:
+      case rtc::ADAPTER_TYPE_LOOPBACK:
+        return kNetworkCostMin;
+      case rtc::ADAPTER_TYPE_WIFI:
+      case rtc::ADAPTER_TYPE_VPN:
+        return kNetworkCostLow;
+      case rtc::ADAPTER_TYPE_CELLULAR:
+        return kNetworkCostHigh;
+      default:
+        return kNetworkCostUnknown;
+    }
+  }
+  // A unique id assigned by the network manager, which may be signaled
+  // to the remote side in the candidate.
+  uint16_t id() const { return id_; }
+  void set_id(uint16_t id) { id_ = id; }
+
+  int preference() const { return preference_; }
+  void set_preference(int preference) { preference_ = preference; }
+
+  // When we enumerate networks and find a previously-seen network is missing,
+  // we do not remove it (because it may be used elsewhere). Instead, we mark
+  // it inactive, so that we can detect network changes properly.
+  bool active() const { return active_; }
+  void set_active(bool active) {
+    if (active_ != active) {
+      active_ = active;
+    }
+  }
+
+  // Debugging description of this network
+  std::string ToString() const;
+
+ private:
+  const DefaultLocalAddressProvider* default_local_address_provider_ = nullptr;
+  std::string name_;
+  std::string description_;
+  IPAddress prefix_;
+  int prefix_length_;
+  std::string key_;
+  std::vector<InterfaceAddress> ips_;
+  int scope_id_;
+  bool ignored_;
+  AdapterType type_;
+  int preference_;
+  bool active_ = true;
+  uint16_t id_ = 0;
+
+  friend class NetworkManager;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_NETWORK_H_
diff --git a/base/network_unittest.cc b/base/network_unittest.cc
new file mode 100644
index 0000000..27dab48
--- /dev/null
+++ b/base/network_unittest.cc
@@ -0,0 +1,1154 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/network.h"
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/nethelpers.h"
+#include "webrtc/base/networkmonitor.h"
+#include <memory>
+#include <vector>
+#if defined(WEBRTC_POSIX)
+#include <sys/types.h>
+#include <net/if.h>
+#include "webrtc/base/ifaddrs_converter.h"
+#endif  // defined(WEBRTC_POSIX)
+#include "webrtc/base/gunit.h"
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/logging.h"  // For LOG_GLE
+#endif
+
+namespace rtc {
+
+namespace {
+
+class FakeNetworkMonitor : public NetworkMonitorBase {
+ public:
+  void Start() override { started_ = true; }
+  void Stop() override { started_ = false; }
+  bool started() { return started_; }
+  AdapterType GetAdapterType(const std::string& if_name) override {
+    // Note that the name matching rules are different from the
+    // GetAdapterTypeFromName in NetworkManager.
+    if (if_name.find("wifi") == 0) {
+      return ADAPTER_TYPE_WIFI;
+    }
+    if (if_name.find("cellular") == 0) {
+      return ADAPTER_TYPE_CELLULAR;
+    }
+    return ADAPTER_TYPE_UNKNOWN;
+  }
+
+ private:
+  bool started_ = false;
+};
+
+class FakeNetworkMonitorFactory : public NetworkMonitorFactory {
+ public:
+  FakeNetworkMonitorFactory() {}
+  NetworkMonitorInterface* CreateNetworkMonitor() override {
+    return new FakeNetworkMonitor();
+  }
+};
+
+}  // namespace
+
+class NetworkTest : public testing::Test, public sigslot::has_slots<>  {
+ public:
+  NetworkTest() : callback_called_(false) {}
+
+  void OnNetworksChanged() {
+    callback_called_ = true;
+  }
+
+  NetworkManager::Stats MergeNetworkList(
+      BasicNetworkManager& network_manager,
+      const NetworkManager::NetworkList& list,
+      bool* changed) {
+    NetworkManager::Stats stats;
+    network_manager.MergeNetworkList(list, changed, &stats);
+    return stats;
+  }
+
+  bool IsIgnoredNetwork(BasicNetworkManager& network_manager,
+                        const Network& network) {
+    return network_manager.IsIgnoredNetwork(network);
+  }
+
+  NetworkManager::NetworkList GetNetworks(
+      const BasicNetworkManager& network_manager, bool include_ignored) {
+    NetworkManager::NetworkList list;
+    network_manager.CreateNetworks(include_ignored, &list);
+    return list;
+  }
+
+  FakeNetworkMonitor* GetNetworkMonitor(BasicNetworkManager& network_manager) {
+    return static_cast<FakeNetworkMonitor*>(
+        network_manager.network_monitor_.get());
+  }
+  void ClearNetworks(BasicNetworkManager& network_manager) {
+    for (const auto& kv : network_manager.networks_map_) {
+      delete kv.second;
+    }
+    network_manager.networks_.clear();
+    network_manager.networks_map_.clear();
+  }
+
+  AdapterType GetAdapterType(BasicNetworkManager& network_manager) {
+    BasicNetworkManager::NetworkList list;
+    network_manager.GetNetworks(&list);
+    RTC_CHECK_EQ(1, list.size());
+    return list[0]->type();
+  }
+
+#if defined(WEBRTC_POSIX)
+  // Separated from CreateNetworks for tests.
+  static void CallConvertIfAddrs(const BasicNetworkManager& network_manager,
+                                 struct ifaddrs* interfaces,
+                                 bool include_ignored,
+                                 NetworkManager::NetworkList* networks) {
+    // Use the base IfAddrsConverter for test cases.
+    std::unique_ptr<IfAddrsConverter> ifaddrs_converter(new IfAddrsConverter());
+    network_manager.ConvertIfAddrs(interfaces, ifaddrs_converter.get(),
+                                   include_ignored, networks);
+  }
+
+  struct sockaddr_in6* CreateIpv6Addr(const std::string& ip_string,
+                                      uint32_t scope_id) {
+    struct sockaddr_in6* ipv6_addr = new struct sockaddr_in6;
+    memset(ipv6_addr, 0, sizeof(struct sockaddr_in6));
+    ipv6_addr->sin6_family = AF_INET6;
+    ipv6_addr->sin6_scope_id = scope_id;
+    IPAddress ip;
+    IPFromString(ip_string, &ip);
+    ipv6_addr->sin6_addr = ip.ipv6_address();
+    return ipv6_addr;
+  }
+
+  // Pointers created here need to be released via ReleaseIfAddrs.
+  struct ifaddrs* AddIpv6Address(struct ifaddrs* list,
+                                 char* if_name,
+                                 const std::string& ipv6_address,
+                                 const std::string& ipv6_netmask,
+                                 uint32_t scope_id) {
+    struct ifaddrs* if_addr = new struct ifaddrs;
+    memset(if_addr, 0, sizeof(struct ifaddrs));
+    if_addr->ifa_name = if_name;
+    if_addr->ifa_addr = reinterpret_cast<struct sockaddr*>(
+        CreateIpv6Addr(ipv6_address, scope_id));
+    if_addr->ifa_netmask =
+        reinterpret_cast<struct sockaddr*>(CreateIpv6Addr(ipv6_netmask, 0));
+    if_addr->ifa_next = list;
+    if_addr->ifa_flags = IFF_RUNNING;
+    return if_addr;
+  }
+
+  struct ifaddrs* InstallIpv6Network(char* if_name,
+                                     const std::string& ipv6_address,
+                                     const std::string& ipv6_mask,
+                                     BasicNetworkManager& network_manager) {
+    ifaddrs* addr_list = nullptr;
+    addr_list = AddIpv6Address(addr_list, if_name, ipv6_address, ipv6_mask, 0);
+    NetworkManager::NetworkList result;
+    bool changed;
+    NetworkManager::Stats stats;
+    CallConvertIfAddrs(network_manager, addr_list, true, &result);
+    network_manager.MergeNetworkList(result, &changed, &stats);
+    return addr_list;
+  }
+
+  void ReleaseIfAddrs(struct ifaddrs* list) {
+    struct ifaddrs* if_addr = list;
+    while (if_addr != nullptr) {
+      struct ifaddrs* next_addr = if_addr->ifa_next;
+      delete if_addr->ifa_addr;
+      delete if_addr->ifa_netmask;
+      delete if_addr;
+      if_addr = next_addr;
+    }
+  }
+#endif  // defined(WEBRTC_POSIX)
+
+ protected:
+  bool callback_called_;
+};
+
+class TestBasicNetworkManager : public BasicNetworkManager {
+ public:
+  using BasicNetworkManager::QueryDefaultLocalAddress;
+  using BasicNetworkManager::set_default_local_addresses;
+};
+
+// Test that the Network ctor works properly.
+TEST_F(NetworkTest, TestNetworkConstruct) {
+  Network ipv4_network1("test_eth0", "Test Network Adapter 1",
+                        IPAddress(0x12345600U), 24);
+  EXPECT_EQ("test_eth0", ipv4_network1.name());
+  EXPECT_EQ("Test Network Adapter 1", ipv4_network1.description());
+  EXPECT_EQ(IPAddress(0x12345600U), ipv4_network1.prefix());
+  EXPECT_EQ(24, ipv4_network1.prefix_length());
+  EXPECT_FALSE(ipv4_network1.ignored());
+}
+
+TEST_F(NetworkTest, TestIsIgnoredNetworkIgnoresIPsStartingWith0) {
+  Network ipv4_network1("test_eth0", "Test Network Adapter 1",
+                        IPAddress(0x12345600U), 24, ADAPTER_TYPE_ETHERNET);
+  Network ipv4_network2("test_eth1", "Test Network Adapter 2",
+                        IPAddress(0x010000U), 24, ADAPTER_TYPE_ETHERNET);
+  BasicNetworkManager network_manager;
+  EXPECT_FALSE(IsIgnoredNetwork(network_manager, ipv4_network1));
+  EXPECT_TRUE(IsIgnoredNetwork(network_manager, ipv4_network2));
+}
+
+// TODO(phoglund): Remove when ignore list goes away.
+TEST_F(NetworkTest, TestIgnoreList) {
+  Network ignore_me("ignore_me", "Ignore me please!",
+                    IPAddress(0x12345600U), 24);
+  Network include_me("include_me", "Include me please!",
+                     IPAddress(0x12345600U), 24);
+  BasicNetworkManager network_manager;
+  EXPECT_FALSE(IsIgnoredNetwork(network_manager, ignore_me));
+  EXPECT_FALSE(IsIgnoredNetwork(network_manager, include_me));
+  std::vector<std::string> ignore_list;
+  ignore_list.push_back("ignore_me");
+  network_manager.set_network_ignore_list(ignore_list);
+  EXPECT_TRUE(IsIgnoredNetwork(network_manager, ignore_me));
+  EXPECT_FALSE(IsIgnoredNetwork(network_manager, include_me));
+}
+
+// Test is failing on Windows opt: b/11288214
+TEST_F(NetworkTest, DISABLED_TestCreateNetworks) {
+  BasicNetworkManager manager;
+  NetworkManager::NetworkList result = GetNetworks(manager, true);
+  // We should be able to bind to any addresses we find.
+  NetworkManager::NetworkList::iterator it;
+  for (it = result.begin();
+       it != result.end();
+       ++it) {
+    sockaddr_storage storage;
+    memset(&storage, 0, sizeof(storage));
+    IPAddress ip = (*it)->GetBestIP();
+    SocketAddress bindaddress(ip, 0);
+    bindaddress.SetScopeID((*it)->scope_id());
+    // TODO(thaloun): Use rtc::AsyncSocket once it supports IPv6.
+    int fd = static_cast<int>(socket(ip.family(), SOCK_STREAM, IPPROTO_TCP));
+    if (fd > 0) {
+      size_t ipsize = bindaddress.ToSockAddrStorage(&storage);
+      EXPECT_GE(ipsize, 0U);
+      int success = ::bind(fd,
+                           reinterpret_cast<sockaddr*>(&storage),
+                           static_cast<int>(ipsize));
+#if defined(WEBRTC_WIN)
+      if (success) LOG_GLE(LS_ERROR) << "Socket bind failed.";
+#endif
+      EXPECT_EQ(0, success);
+#if defined(WEBRTC_WIN)
+      closesocket(fd);
+#else
+      close(fd);
+#endif
+    }
+    delete (*it);
+  }
+}
+
+// Test StartUpdating() and StopUpdating(). network_permission_state starts with
+// ALLOWED.
+TEST_F(NetworkTest, TestUpdateNetworks) {
+  BasicNetworkManager manager;
+  manager.SignalNetworksChanged.connect(
+      static_cast<NetworkTest*>(this), &NetworkTest::OnNetworksChanged);
+  EXPECT_EQ(NetworkManager::ENUMERATION_ALLOWED,
+            manager.enumeration_permission());
+  manager.StartUpdating();
+  Thread::Current()->ProcessMessages(0);
+  EXPECT_TRUE(callback_called_);
+  callback_called_ = false;
+  // Callback should be triggered immediately when StartUpdating
+  // is called, after network update signal is already sent.
+  manager.StartUpdating();
+  EXPECT_TRUE(manager.started());
+  Thread::Current()->ProcessMessages(0);
+  EXPECT_TRUE(callback_called_);
+  manager.StopUpdating();
+  EXPECT_TRUE(manager.started());
+  manager.StopUpdating();
+  EXPECT_EQ(NetworkManager::ENUMERATION_ALLOWED,
+            manager.enumeration_permission());
+  EXPECT_FALSE(manager.started());
+  manager.StopUpdating();
+  EXPECT_FALSE(manager.started());
+  callback_called_ = false;
+  // Callback should be triggered immediately after StartUpdating is called
+  // when start_count_ is reset to 0.
+  manager.StartUpdating();
+  Thread::Current()->ProcessMessages(0);
+  EXPECT_TRUE(callback_called_);
+}
+
+// Verify that MergeNetworkList() merges network lists properly.
+TEST_F(NetworkTest, TestBasicMergeNetworkList) {
+  Network ipv4_network1("test_eth0", "Test Network Adapter 1",
+                        IPAddress(0x12345600U), 24);
+  Network ipv4_network2("test_eth1", "Test Network Adapter 2",
+                        IPAddress(0x00010000U), 16);
+  ipv4_network1.AddIP(IPAddress(0x12345678));
+  ipv4_network2.AddIP(IPAddress(0x00010004));
+  BasicNetworkManager manager;
+
+  // Add ipv4_network1 to the list of networks.
+  NetworkManager::NetworkList list;
+  list.push_back(new Network(ipv4_network1));
+  bool changed;
+  NetworkManager::Stats stats = MergeNetworkList(manager, list, &changed);
+  EXPECT_TRUE(changed);
+  EXPECT_EQ(stats.ipv6_network_count, 0);
+  EXPECT_EQ(stats.ipv4_network_count, 1);
+  list.clear();
+
+  manager.GetNetworks(&list);
+  EXPECT_EQ(1U, list.size());
+  EXPECT_EQ(ipv4_network1.ToString(), list[0]->ToString());
+  Network* net1 = list[0];
+  uint16_t net_id1 = net1->id();
+  EXPECT_EQ(1, net_id1);
+  list.clear();
+
+  // Replace ipv4_network1 with ipv4_network2.
+  list.push_back(new Network(ipv4_network2));
+  stats = MergeNetworkList(manager, list, &changed);
+  EXPECT_TRUE(changed);
+  EXPECT_EQ(stats.ipv6_network_count, 0);
+  EXPECT_EQ(stats.ipv4_network_count, 1);
+  list.clear();
+
+  manager.GetNetworks(&list);
+  EXPECT_EQ(1U, list.size());
+  EXPECT_EQ(ipv4_network2.ToString(), list[0]->ToString());
+  Network* net2 = list[0];
+  uint16_t net_id2 = net2->id();
+  // Network id will increase.
+  EXPECT_LT(net_id1, net_id2);
+  list.clear();
+
+  // Add Network2 back.
+  list.push_back(new Network(ipv4_network1));
+  list.push_back(new Network(ipv4_network2));
+  stats = MergeNetworkList(manager, list, &changed);
+  EXPECT_TRUE(changed);
+  EXPECT_EQ(stats.ipv6_network_count, 0);
+  EXPECT_EQ(stats.ipv4_network_count, 2);
+  list.clear();
+
+  // Verify that we get previous instances of Network objects.
+  manager.GetNetworks(&list);
+  EXPECT_EQ(2U, list.size());
+  EXPECT_TRUE((net1 == list[0] && net2 == list[1]) ||
+              (net1 == list[1] && net2 == list[0]));
+  EXPECT_TRUE((net_id1 == list[0]->id() && net_id2 == list[1]->id()) ||
+              (net_id1 == list[1]->id() && net_id2 == list[0]->id()));
+  list.clear();
+
+  // Call MergeNetworkList() again and verify that we don't get update
+  // notification.
+  list.push_back(new Network(ipv4_network2));
+  list.push_back(new Network(ipv4_network1));
+  stats = MergeNetworkList(manager, list, &changed);
+  EXPECT_FALSE(changed);
+  EXPECT_EQ(stats.ipv6_network_count, 0);
+  EXPECT_EQ(stats.ipv4_network_count, 2);
+  list.clear();
+
+  // Verify that we get previous instances of Network objects.
+  manager.GetNetworks(&list);
+  EXPECT_EQ(2U, list.size());
+  EXPECT_TRUE((net1 == list[0] && net2 == list[1]) ||
+              (net1 == list[1] && net2 == list[0]));
+  EXPECT_TRUE((net_id1 == list[0]->id() && net_id2 == list[1]->id()) ||
+              (net_id1 == list[1]->id() && net_id2 == list[0]->id()));
+  list.clear();
+}
+
+// Sets up some test IPv6 networks and appends them to list.
+// Four networks are added - public and link local, for two interfaces.
+void SetupNetworks(NetworkManager::NetworkList* list) {
+  IPAddress ip;
+  IPAddress prefix;
+  EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:ef12", &ip));
+  EXPECT_TRUE(IPFromString("abcd::", &prefix));
+  // First, fake link-locals.
+  Network ipv6_eth0_linklocalnetwork("test_eth0", "Test NetworkAdapter 1",
+                                     prefix, 64);
+  ipv6_eth0_linklocalnetwork.AddIP(ip);
+  EXPECT_TRUE(IPFromString("abcd::5678:abcd:ef12:3456", &ip));
+  Network ipv6_eth1_linklocalnetwork("test_eth1", "Test NetworkAdapter 2",
+                                     prefix, 64);
+  ipv6_eth1_linklocalnetwork.AddIP(ip);
+  // Public networks:
+  EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c3", &ip));
+  prefix = TruncateIP(ip, 64);
+  Network ipv6_eth0_publicnetwork1_ip1("test_eth0", "Test NetworkAdapter 1",
+                                       prefix, 64);
+  ipv6_eth0_publicnetwork1_ip1.AddIP(ip);
+  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:abcd:efab:cdef", &ip));
+  prefix = TruncateIP(ip, 64);
+  Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 1",
+                                       prefix, 64);
+  ipv6_eth1_publicnetwork1_ip1.AddIP(ip);
+  list->push_back(new Network(ipv6_eth0_linklocalnetwork));
+  list->push_back(new Network(ipv6_eth1_linklocalnetwork));
+  list->push_back(new Network(ipv6_eth0_publicnetwork1_ip1));
+  list->push_back(new Network(ipv6_eth1_publicnetwork1_ip1));
+}
+
+// Test that the basic network merging case works.
+TEST_F(NetworkTest, TestIPv6MergeNetworkList) {
+  BasicNetworkManager manager;
+  manager.SignalNetworksChanged.connect(
+      static_cast<NetworkTest*>(this), &NetworkTest::OnNetworksChanged);
+  NetworkManager::NetworkList original_list;
+  SetupNetworks(&original_list);
+  bool changed = false;
+  NetworkManager::Stats stats =
+      MergeNetworkList(manager, original_list, &changed);
+  EXPECT_TRUE(changed);
+  EXPECT_EQ(stats.ipv6_network_count, 4);
+  EXPECT_EQ(stats.ipv4_network_count, 0);
+  NetworkManager::NetworkList list;
+  manager.GetNetworks(&list);
+  EXPECT_EQ(original_list.size(), list.size());
+  // Verify that the original members are in the merged list.
+  for (NetworkManager::NetworkList::iterator it = original_list.begin();
+       it != original_list.end(); ++it) {
+    EXPECT_NE(list.end(), std::find(list.begin(), list.end(), *it));
+  }
+}
+
+// Test that no more than manager.max_ipv6_networks() IPv6 networks get
+// returned.
+TEST_F(NetworkTest, TestIPv6MergeNetworkListTrimExcessive) {
+  BasicNetworkManager manager;
+  manager.SignalNetworksChanged.connect(static_cast<NetworkTest*>(this),
+                                        &NetworkTest::OnNetworksChanged);
+  NetworkManager::NetworkList original_list;
+
+  // Add twice the allowed number of IPv6 networks.
+  for (int i = 0; i < 2 * manager.max_ipv6_networks(); i++) {
+    // Make a network with different prefix length.
+    IPAddress ip;
+    EXPECT_TRUE(IPFromString("2401:fa01:4:1000:be30:faa:fee:faa", &ip));
+    IPAddress prefix = TruncateIP(ip, 64 - i);
+    Network* ipv6_network =
+        new Network("test_eth0", "Test Network Adapter 1", prefix, 64 - i);
+    ipv6_network->AddIP(ip);
+    original_list.push_back(ipv6_network);
+  }
+
+  // Add one IPv4 network.
+  Network* ipv4_network = new Network("test_eth0", "Test Network Adapter 1",
+                                      IPAddress(0x12345600U), 24);
+  ipv4_network->AddIP(IPAddress(0x12345600U));
+  original_list.push_back(ipv4_network);
+
+  bool changed = false;
+  MergeNetworkList(manager, original_list, &changed);
+  EXPECT_TRUE(changed);
+  NetworkManager::NetworkList list;
+  manager.GetNetworks(&list);
+
+  // List size should be the max allowed IPv6 networks plus one IPv4 network.
+  EXPECT_EQ(manager.max_ipv6_networks() + 1, (int)list.size());
+
+  // Verify that the IPv4 network is in the list.
+  EXPECT_NE(list.end(), std::find(list.begin(), list.end(), ipv4_network));
+}
+
+// Tests that when two network lists that describe the same set of networks are
+// merged, that the changed callback is not called, and that the original
+// objects remain in the result list.
+TEST_F(NetworkTest, TestNoChangeMerge) {
+  BasicNetworkManager manager;
+  manager.SignalNetworksChanged.connect(
+      static_cast<NetworkTest*>(this), &NetworkTest::OnNetworksChanged);
+  NetworkManager::NetworkList original_list;
+  SetupNetworks(&original_list);
+  bool changed = false;
+  MergeNetworkList(manager, original_list, &changed);
+  EXPECT_TRUE(changed);
+  // Second list that describes the same networks but with new objects.
+  NetworkManager::NetworkList second_list;
+  SetupNetworks(&second_list);
+  changed = false;
+  MergeNetworkList(manager, second_list, &changed);
+  EXPECT_FALSE(changed);
+  NetworkManager::NetworkList resulting_list;
+  manager.GetNetworks(&resulting_list);
+  EXPECT_EQ(original_list.size(), resulting_list.size());
+  // Verify that the original members are in the merged list.
+  for (NetworkManager::NetworkList::iterator it = original_list.begin();
+       it != original_list.end(); ++it) {
+    EXPECT_NE(resulting_list.end(),
+              std::find(resulting_list.begin(), resulting_list.end(), *it));
+  }
+  // Doublecheck that the new networks aren't in the list.
+  for (NetworkManager::NetworkList::iterator it = second_list.begin();
+       it != second_list.end(); ++it) {
+    EXPECT_EQ(resulting_list.end(),
+              std::find(resulting_list.begin(), resulting_list.end(), *it));
+  }
+}
+
+// Test that we can merge a network that is the same as another network but with
+// a different IP. The original network should remain in the list, but have its
+// IP changed.
+TEST_F(NetworkTest, MergeWithChangedIP) {
+  BasicNetworkManager manager;
+  manager.SignalNetworksChanged.connect(
+      static_cast<NetworkTest*>(this), &NetworkTest::OnNetworksChanged);
+  NetworkManager::NetworkList original_list;
+  SetupNetworks(&original_list);
+  // Make a network that we're going to change.
+  IPAddress ip;
+  EXPECT_TRUE(IPFromString("2401:fa01:4:1000:be30:faa:fee:faa", &ip));
+  IPAddress prefix = TruncateIP(ip, 64);
+  Network* network_to_change = new Network("test_eth0",
+                                          "Test Network Adapter 1",
+                                          prefix, 64);
+  Network* changed_network = new Network(*network_to_change);
+  network_to_change->AddIP(ip);
+  IPAddress changed_ip;
+  EXPECT_TRUE(IPFromString("2401:fa01:4:1000:be30:f00:f00:f00", &changed_ip));
+  changed_network->AddIP(changed_ip);
+  original_list.push_back(network_to_change);
+  bool changed = false;
+  MergeNetworkList(manager, original_list, &changed);
+  NetworkManager::NetworkList second_list;
+  SetupNetworks(&second_list);
+  second_list.push_back(changed_network);
+  changed = false;
+  MergeNetworkList(manager, second_list, &changed);
+  EXPECT_TRUE(changed);
+  NetworkManager::NetworkList list;
+  manager.GetNetworks(&list);
+  EXPECT_EQ(original_list.size(), list.size());
+  // Make sure the original network is still in the merged list.
+  EXPECT_NE(list.end(),
+            std::find(list.begin(), list.end(), network_to_change));
+  EXPECT_EQ(changed_ip, network_to_change->GetIPs().at(0));
+}
+
+// Testing a similar case to above, but checking that a network can be updated
+// with additional IPs (not just a replacement).
+TEST_F(NetworkTest, TestMultipleIPMergeNetworkList) {
+  BasicNetworkManager manager;
+  manager.SignalNetworksChanged.connect(
+      static_cast<NetworkTest*>(this), &NetworkTest::OnNetworksChanged);
+  NetworkManager::NetworkList original_list;
+  SetupNetworks(&original_list);
+  bool changed = false;
+  MergeNetworkList(manager, original_list, &changed);
+  EXPECT_TRUE(changed);
+  IPAddress ip;
+  IPAddress check_ip;
+  IPAddress prefix;
+  // Add a second IP to the public network on eth0 (2401:fa00:4:1000/64).
+  EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c6", &ip));
+  prefix = TruncateIP(ip, 64);
+  Network ipv6_eth0_publicnetwork1_ip2("test_eth0", "Test NetworkAdapter 1",
+                                       prefix, 64);
+  // This is the IP that already existed in the public network on eth0.
+  EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c3", &check_ip));
+  ipv6_eth0_publicnetwork1_ip2.AddIP(ip);
+  original_list.push_back(new Network(ipv6_eth0_publicnetwork1_ip2));
+  changed = false;
+  MergeNetworkList(manager, original_list, &changed);
+  EXPECT_TRUE(changed);
+  // There should still be four networks.
+  NetworkManager::NetworkList list;
+  manager.GetNetworks(&list);
+  EXPECT_EQ(4U, list.size());
+  // Check the gathered IPs.
+  int matchcount = 0;
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    if ((*it)->ToString() == original_list[2]->ToString()) {
+      ++matchcount;
+      EXPECT_EQ(1, matchcount);
+      // This should be the same network object as before.
+      EXPECT_EQ((*it), original_list[2]);
+      // But with two addresses now.
+      EXPECT_EQ(2U, (*it)->GetIPs().size());
+      EXPECT_NE((*it)->GetIPs().end(),
+                std::find((*it)->GetIPs().begin(),
+                          (*it)->GetIPs().end(),
+                          check_ip));
+      EXPECT_NE((*it)->GetIPs().end(),
+                std::find((*it)->GetIPs().begin(),
+                          (*it)->GetIPs().end(),
+                          ip));
+    } else {
+      // Check the IP didn't get added anywhere it wasn't supposed to.
+      EXPECT_EQ((*it)->GetIPs().end(),
+                std::find((*it)->GetIPs().begin(),
+                          (*it)->GetIPs().end(),
+                          ip));
+    }
+  }
+}
+
+// Test that merge correctly distinguishes multiple networks on an interface.
+TEST_F(NetworkTest, TestMultiplePublicNetworksOnOneInterfaceMerge) {
+  BasicNetworkManager manager;
+  manager.SignalNetworksChanged.connect(
+      static_cast<NetworkTest*>(this), &NetworkTest::OnNetworksChanged);
+  NetworkManager::NetworkList original_list;
+  SetupNetworks(&original_list);
+  bool changed = false;
+  MergeNetworkList(manager, original_list, &changed);
+  EXPECT_TRUE(changed);
+  IPAddress ip;
+  IPAddress prefix;
+  // A second network for eth0.
+  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:5bff:fee5:c3", &ip));
+  prefix = TruncateIP(ip, 64);
+  Network ipv6_eth0_publicnetwork2_ip1("test_eth0", "Test NetworkAdapter 1",
+                                       prefix, 64);
+  ipv6_eth0_publicnetwork2_ip1.AddIP(ip);
+  original_list.push_back(new Network(ipv6_eth0_publicnetwork2_ip1));
+  changed = false;
+  MergeNetworkList(manager, original_list, &changed);
+  EXPECT_TRUE(changed);
+  // There should be five networks now.
+  NetworkManager::NetworkList list;
+  manager.GetNetworks(&list);
+  EXPECT_EQ(5U, list.size());
+  // Check the resulting addresses.
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    if ((*it)->prefix() == ipv6_eth0_publicnetwork2_ip1.prefix() &&
+        (*it)->name() == ipv6_eth0_publicnetwork2_ip1.name()) {
+      // Check the new network has 1 IP and that it's the correct one.
+      EXPECT_EQ(1U, (*it)->GetIPs().size());
+      EXPECT_EQ(ip, (*it)->GetIPs().at(0));
+    } else {
+      // Check the IP didn't get added anywhere it wasn't supposed to.
+      EXPECT_EQ((*it)->GetIPs().end(),
+                std::find((*it)->GetIPs().begin(),
+                          (*it)->GetIPs().end(),
+                          ip));
+    }
+  }
+}
+
+// Test that DumpNetworks does not crash.
+TEST_F(NetworkTest, TestCreateAndDumpNetworks) {
+  BasicNetworkManager manager;
+  NetworkManager::NetworkList list = GetNetworks(manager, true);
+  bool changed;
+  MergeNetworkList(manager, list, &changed);
+  manager.DumpNetworks();
+}
+
+// Test that we can toggle IPv6 on and off.
+// Crashes on Linux. See webrtc:4923.
+#if defined(WEBRTC_LINUX)
+#define MAYBE_TestIPv6Toggle DISABLED_TestIPv6Toggle
+#else
+#define MAYBE_TestIPv6Toggle TestIPv6Toggle
+#endif
+TEST_F(NetworkTest, MAYBE_TestIPv6Toggle) {
+  BasicNetworkManager manager;
+  bool ipv6_found = false;
+  NetworkManager::NetworkList list;
+#if !defined(WEBRTC_WIN)
+  // There should be at least one IPv6 network (fe80::/64 should be in there).
+  // TODO(thaloun): Disabling this test on windows for the moment as the test
+  // machines don't seem to have IPv6 installed on them at all.
+  manager.set_ipv6_enabled(true);
+  list = GetNetworks(manager, true);
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    if ((*it)->prefix().family() == AF_INET6) {
+      ipv6_found = true;
+      break;
+    }
+  }
+  EXPECT_TRUE(ipv6_found);
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    delete (*it);
+  }
+#endif
+  ipv6_found = false;
+  manager.set_ipv6_enabled(false);
+  list = GetNetworks(manager, true);
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    if ((*it)->prefix().family() == AF_INET6) {
+      ipv6_found = true;
+      break;
+    }
+  }
+  EXPECT_FALSE(ipv6_found);
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    delete (*it);
+  }
+}
+
+TEST_F(NetworkTest, TestNetworkListSorting) {
+  BasicNetworkManager manager;
+  Network ipv4_network1("test_eth0", "Test Network Adapter 1",
+                        IPAddress(0x12345600U), 24);
+  ipv4_network1.AddIP(IPAddress(0x12345600U));
+
+  IPAddress ip;
+  IPAddress prefix;
+  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:abcd:efab:cdef", &ip));
+  prefix = TruncateIP(ip, 64);
+  Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 2",
+                                       prefix, 64);
+  ipv6_eth1_publicnetwork1_ip1.AddIP(ip);
+
+  NetworkManager::NetworkList list;
+  list.push_back(new Network(ipv4_network1));
+  list.push_back(new Network(ipv6_eth1_publicnetwork1_ip1));
+  Network* net1 = list[0];
+  Network* net2 = list[1];
+
+  bool changed = false;
+  MergeNetworkList(manager, list, &changed);
+  ASSERT_TRUE(changed);
+  // After sorting IPv6 network should be higher order than IPv4 networks.
+  EXPECT_TRUE(net1->preference() < net2->preference());
+}
+
+TEST_F(NetworkTest, TestNetworkAdapterTypes) {
+  Network wifi("wlan0", "Wireless Adapter", IPAddress(0x12345600U), 24,
+               ADAPTER_TYPE_WIFI);
+  EXPECT_EQ(ADAPTER_TYPE_WIFI, wifi.type());
+  Network ethernet("eth0", "Ethernet", IPAddress(0x12345600U), 24,
+                   ADAPTER_TYPE_ETHERNET);
+  EXPECT_EQ(ADAPTER_TYPE_ETHERNET, ethernet.type());
+  Network cellular("test_cell", "Cellular Adapter", IPAddress(0x12345600U), 24,
+                   ADAPTER_TYPE_CELLULAR);
+  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, cellular.type());
+  Network vpn("bridge_test", "VPN Adapter", IPAddress(0x12345600U), 24,
+              ADAPTER_TYPE_VPN);
+  EXPECT_EQ(ADAPTER_TYPE_VPN, vpn.type());
+  Network unknown("test", "Test Adapter", IPAddress(0x12345600U), 24,
+                  ADAPTER_TYPE_UNKNOWN);
+  EXPECT_EQ(ADAPTER_TYPE_UNKNOWN, unknown.type());
+}
+
+#if defined(WEBRTC_POSIX)
+// Verify that we correctly handle interfaces with no address.
+TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) {
+  ifaddrs list;
+  memset(&list, 0, sizeof(list));
+  list.ifa_name = const_cast<char*>("test_iface");
+
+  NetworkManager::NetworkList result;
+  BasicNetworkManager manager;
+  CallConvertIfAddrs(manager, &list, true, &result);
+  EXPECT_TRUE(result.empty());
+}
+
+// Verify that if there are two addresses on one interface, only one network
+// is generated.
+TEST_F(NetworkTest, TestConvertIfAddrsMultiAddressesOnOneInterface) {
+  char if_name[20] = "rmnet0";
+  ifaddrs* list = nullptr;
+  list = AddIpv6Address(list, if_name, "1000:2000:3000:4000:0:0:0:1",
+                        "FFFF:FFFF:FFFF:FFFF::", 0);
+  list = AddIpv6Address(list, if_name, "1000:2000:3000:4000:0:0:0:2",
+                        "FFFF:FFFF:FFFF:FFFF::", 0);
+  NetworkManager::NetworkList result;
+  BasicNetworkManager manager;
+  CallConvertIfAddrs(manager, list, true, &result);
+  EXPECT_EQ(1U, result.size());
+  bool changed;
+  // This ensures we release the objects created in CallConvertIfAddrs.
+  MergeNetworkList(manager, result, &changed);
+  ReleaseIfAddrs(list);
+}
+
+TEST_F(NetworkTest, TestConvertIfAddrsNotRunning) {
+  ifaddrs list;
+  memset(&list, 0, sizeof(list));
+  list.ifa_name = const_cast<char*>("test_iface");
+  sockaddr ifa_addr;
+  sockaddr ifa_netmask;
+  list.ifa_addr = &ifa_addr;
+  list.ifa_netmask = &ifa_netmask;
+
+  NetworkManager::NetworkList result;
+  BasicNetworkManager manager;
+  CallConvertIfAddrs(manager, &list, true, &result);
+  EXPECT_TRUE(result.empty());
+}
+
+// Tests that the network type can be updated after the network monitor is
+// started.
+TEST_F(NetworkTest, TestGetAdapterTypeFromNetworkMonitor) {
+  char if_name1[20] = "wifi0";
+  std::string ipv6_address1 = "1000:2000:3000:4000:0:0:0:1";
+  std::string ipv6_address2 = "1000:2000:3000:8000:0:0:0:1";
+  std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF::";
+  BasicNetworkManager manager;
+  // A network created before the network monitor is started will get
+  // UNKNOWN type.
+  ifaddrs* addr_list =
+      InstallIpv6Network(if_name1, ipv6_address1, ipv6_mask, manager);
+  EXPECT_EQ(ADAPTER_TYPE_UNKNOWN, GetAdapterType(manager));
+  ReleaseIfAddrs(addr_list);
+  // Note: Do not call ClearNetworks here in order to test that the type
+  // of an existing network can be changed after the network monitor starts
+  // and detects the network type correctly.
+
+  // After the network monitor starts, the type will be updated.
+  FakeNetworkMonitorFactory* factory = new FakeNetworkMonitorFactory();
+  NetworkMonitorFactory::SetFactory(factory);
+  // This brings up the hook with the network monitor.
+  manager.StartUpdating();
+  // Add the same ipv6 address as before but it has the right network type
+  // detected by the network monitor now.
+  addr_list = InstallIpv6Network(if_name1, ipv6_address1, ipv6_mask, manager);
+  EXPECT_EQ(ADAPTER_TYPE_WIFI, GetAdapterType(manager));
+  ReleaseIfAddrs(addr_list);
+  ClearNetworks(manager);
+
+  // Add another network with the type inferred from the network monitor.
+  char if_name2[20] = "cellular0";
+  addr_list = InstallIpv6Network(if_name2, ipv6_address2, ipv6_mask, manager);
+  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager));
+  ReleaseIfAddrs(addr_list);
+  ClearNetworks(manager);
+}
+
+// Test that the network type can be determined based on name matching in
+// a few cases. Note that UNKNOWN type for non-matching strings has been tested
+// in the above test.
+TEST_F(NetworkTest, TestGetAdapterTypeFromNameMatching) {
+  std::string ipv6_address1 = "1000:2000:3000:4000:0:0:0:1";
+  std::string ipv6_address2 = "1000:2000:3000:8000:0:0:0:1";
+  std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF::";
+  BasicNetworkManager manager;
+
+#if defined(WEBRTC_IOS)
+  char if_name[20] = "pdp_ip0";
+  ifaddrs* addr_list =
+      InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
+
+  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager));
+  ClearNetworks(manager);
+  ReleaseIfAddrs(addr_list);
+
+  strcpy(if_name, "en0");
+  addr_list = InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
+  EXPECT_EQ(ADAPTER_TYPE_WIFI, GetAdapterType(manager));
+  ClearNetworks(manager);
+  ReleaseIfAddrs(addr_list);
+
+#elif defined(WEBRTC_ANDROID)
+  char if_name[20] = "rmnet0";
+  ifaddrs* addr_list =
+      InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
+
+  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager));
+  ClearNetworks(manager);
+  ReleaseIfAddrs(addr_list);
+
+  strcpy(if_name, "wlan1");
+  addr_list = InstallIpv6Network(if_name, ipv6_address2, ipv6_mask, manager);
+  EXPECT_EQ(ADAPTER_TYPE_WIFI, GetAdapterType(manager));
+  ClearNetworks(manager);
+  ReleaseIfAddrs(addr_list);
+
+  strcpy(if_name, "v4-rmnet_data0");
+  addr_list = InstallIpv6Network(if_name, ipv6_address2, ipv6_mask, manager);
+  EXPECT_EQ(ADAPTER_TYPE_CELLULAR, GetAdapterType(manager));
+  ClearNetworks(manager);
+  ReleaseIfAddrs(addr_list);
+#else
+  char if_name[20] = "wlan0";
+  ifaddrs* addr_list =
+      InstallIpv6Network(if_name, ipv6_address1, ipv6_mask, manager);
+
+  EXPECT_EQ(ADAPTER_TYPE_UNKNOWN, GetAdapterType(manager));
+  ClearNetworks(manager);
+  ReleaseIfAddrs(addr_list);
+#endif
+}
+#endif  // defined(WEBRTC_POSIX)
+
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+// If you want to test non-default routes, you can do the following on a linux
+// machine:
+// 1) Load the dummy network driver:
+// sudo modprobe dummy
+// sudo ifconfig dummy0 127.0.0.1
+// 2) Run this test and confirm the output says it found a dummy route (and
+// passes).
+// 3) When done:
+// sudo rmmmod dummy
+TEST_F(NetworkTest, TestIgnoreNonDefaultRoutes) {
+  BasicNetworkManager manager;
+  NetworkManager::NetworkList list;
+  list = GetNetworks(manager, false);
+  bool found_dummy = false;
+  LOG(LS_INFO) << "Looking for dummy network: ";
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    LOG(LS_INFO) << "  Network name: " << (*it)->name();
+    found_dummy |= (*it)->name().find("dummy0") != std::string::npos;
+  }
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    delete (*it);
+  }
+  if (!found_dummy) {
+    LOG(LS_INFO) << "No dummy found, quitting.";
+    return;
+  }
+  LOG(LS_INFO) << "Found dummy, running again while ignoring non-default "
+               << "routes.";
+  manager.set_ignore_non_default_routes(true);
+  list = GetNetworks(manager, false);
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    LOG(LS_INFO) << "  Network name: " << (*it)->name();
+    EXPECT_TRUE((*it)->name().find("dummy0") == std::string::npos);
+  }
+  for (NetworkManager::NetworkList::iterator it = list.begin();
+       it != list.end(); ++it) {
+    delete (*it);
+  }
+}
+#endif
+
+// Test MergeNetworkList successfully combines all IPs for the same
+// prefix/length into a single Network.
+TEST_F(NetworkTest, TestMergeNetworkList) {
+  BasicNetworkManager manager;
+  NetworkManager::NetworkList list;
+
+  // Create 2 IPAddress classes with only last digit different.
+  IPAddress ip1, ip2;
+  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1));
+  EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:2", &ip2));
+
+  // Create 2 networks with the same prefix and length.
+  Network* net1 = new Network("em1", "em1", TruncateIP(ip1, 64), 64);
+  Network* net2 = new Network("em1", "em1", TruncateIP(ip1, 64), 64);
+
+  // Add different IP into each.
+  net1->AddIP(ip1);
+  net2->AddIP(ip2);
+
+  list.push_back(net1);
+  list.push_back(net2);
+  bool changed;
+  MergeNetworkList(manager, list, &changed);
+  EXPECT_TRUE(changed);
+
+  NetworkManager::NetworkList list2;
+  manager.GetNetworks(&list2);
+
+  // Make sure the resulted networklist has only 1 element and 2
+  // IPAddresses.
+  EXPECT_EQ(list2.size(), 1uL);
+  EXPECT_EQ(list2[0]->GetIPs().size(), 2uL);
+  EXPECT_EQ(list2[0]->GetIPs()[0], ip1);
+  EXPECT_EQ(list2[0]->GetIPs()[1], ip2);
+}
+
+// Test that MergeNetworkList successfully detects the change if
+// a network becomes inactive and then active again.
+TEST_F(NetworkTest, TestMergeNetworkListWithInactiveNetworks) {
+  BasicNetworkManager manager;
+  Network network1("test_wifi", "Test Network Adapter 1",
+                   IPAddress(0x12345600U), 24);
+  Network network2("test_eth0", "Test Network Adapter 2",
+                   IPAddress(0x00010000U), 16);
+  network1.AddIP(IPAddress(0x12345678));
+  network2.AddIP(IPAddress(0x00010004));
+  NetworkManager::NetworkList list;
+  Network* net1 = new Network(network1);
+  list.push_back(net1);
+  bool changed;
+  MergeNetworkList(manager, list, &changed);
+  EXPECT_TRUE(changed);
+  list.clear();
+  manager.GetNetworks(&list);
+  ASSERT_EQ(1U, list.size());
+  EXPECT_EQ(net1, list[0]);
+
+  list.clear();
+  Network* net2 = new Network(network2);
+  list.push_back(net2);
+  MergeNetworkList(manager, list, &changed);
+  EXPECT_TRUE(changed);
+  list.clear();
+  manager.GetNetworks(&list);
+  ASSERT_EQ(1U, list.size());
+  EXPECT_EQ(net2, list[0]);
+
+  // Now network1 is inactive. Try to merge it again.
+  list.clear();
+  list.push_back(new Network(network1));
+  MergeNetworkList(manager, list, &changed);
+  EXPECT_TRUE(changed);
+  list.clear();
+  manager.GetNetworks(&list);
+  ASSERT_EQ(1U, list.size());
+  EXPECT_TRUE(list[0]->active());
+  EXPECT_EQ(net1, list[0]);
+}
+
+// Test that the filtering logic follows the defined ruleset in network.h.
+TEST_F(NetworkTest, TestIPv6Selection) {
+  InterfaceAddress ip;
+  std::string ipstr;
+
+  ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c3";
+  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_DEPRECATED, &ip));
+
+  // Create a network with this prefix.
+  Network ipv6_network(
+      "test_eth0", "Test NetworkAdapter", TruncateIP(ip, 64), 64);
+
+  // When there is no address added, it should return an unspecified
+  // address.
+  EXPECT_EQ(ipv6_network.GetBestIP(), IPAddress());
+  EXPECT_TRUE(IPIsUnspec(ipv6_network.GetBestIP()));
+
+  // Deprecated one should not be returned.
+  ipv6_network.AddIP(ip);
+  EXPECT_EQ(ipv6_network.GetBestIP(), IPAddress());
+
+  // Add ULA one. ULA is unique local address which is starting either
+  // with 0xfc or 0xfd.
+  ipstr = "fd00:fa00:4:1000:be30:5bff:fee5:c4";
+  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &ip));
+  ipv6_network.AddIP(ip);
+  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));
+
+  // Add global one.
+  ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c5";
+  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_NONE, &ip));
+  ipv6_network.AddIP(ip);
+  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));
+
+  // Add global dynamic temporary one.
+  ipstr = "2401:fa00:4:1000:be30:5bff:fee5:c6";
+  ASSERT_TRUE(IPFromString(ipstr, IPV6_ADDRESS_FLAG_TEMPORARY, &ip));
+  ipv6_network.AddIP(ip);
+  EXPECT_EQ(ipv6_network.GetBestIP(), static_cast<IPAddress>(ip));
+}
+
+TEST_F(NetworkTest, TestNetworkMonitoring) {
+  BasicNetworkManager manager;
+  manager.SignalNetworksChanged.connect(static_cast<NetworkTest*>(this),
+                                        &NetworkTest::OnNetworksChanged);
+  FakeNetworkMonitorFactory* factory = new FakeNetworkMonitorFactory();
+  NetworkMonitorFactory::SetFactory(factory);
+  manager.StartUpdating();
+  FakeNetworkMonitor* network_monitor = GetNetworkMonitor(manager);
+  EXPECT_TRUE(network_monitor && network_monitor->started());
+  EXPECT_TRUE_WAIT(callback_called_, 1000);
+  callback_called_ = false;
+
+  // Clear the networks so that there will be network changes below.
+  ClearNetworks(manager);
+  // Network manager is started, so the callback is called when the network
+  // monitor fires the network-change event.
+  network_monitor->OnNetworksChanged();
+  EXPECT_TRUE_WAIT(callback_called_, 1000);
+
+  // Network manager is stopped.
+  manager.StopUpdating();
+  EXPECT_FALSE(GetNetworkMonitor(manager)->started());
+
+  NetworkMonitorFactory::ReleaseFactory(factory);
+}
+
+// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_DefaultLocalAddress DISABLED_DefaultLocalAddress
+#else
+#define MAYBE_DefaultLocalAddress DefaultLocalAddress
+#endif
+TEST_F(NetworkTest, MAYBE_DefaultLocalAddress) {
+  IPAddress ip;
+  TestBasicNetworkManager manager;
+  manager.SignalNetworksChanged.connect(static_cast<NetworkTest*>(this),
+                                        &NetworkTest::OnNetworksChanged);
+  FakeNetworkMonitorFactory* factory = new FakeNetworkMonitorFactory();
+  NetworkMonitorFactory::SetFactory(factory);
+  manager.StartUpdating();
+  EXPECT_TRUE_WAIT(callback_called_, 1000);
+
+  // Make sure we can query default local address when an address for such
+  // address family exists.
+  std::vector<Network*> networks;
+  manager.GetNetworks(&networks);
+  EXPECT_TRUE(!networks.empty());
+  for (auto& network : networks) {
+    if (network->GetBestIP().family() == AF_INET) {
+      EXPECT_TRUE(manager.QueryDefaultLocalAddress(AF_INET) != IPAddress());
+    } else if (network->GetBestIP().family() == AF_INET6 &&
+               !IPIsLoopback(network->GetBestIP())) {
+      // Existence of an IPv6 loopback address doesn't mean it has IPv6 network
+      // enabled.
+      EXPECT_TRUE(manager.QueryDefaultLocalAddress(AF_INET6) != IPAddress());
+    }
+  }
+
+  // GetDefaultLocalAddress should return the valid default address after set.
+  manager.set_default_local_addresses(GetLoopbackIP(AF_INET),
+                                      GetLoopbackIP(AF_INET6));
+  EXPECT_TRUE(manager.GetDefaultLocalAddress(AF_INET, &ip));
+  EXPECT_EQ(ip, GetLoopbackIP(AF_INET));
+  EXPECT_TRUE(manager.GetDefaultLocalAddress(AF_INET6, &ip));
+  EXPECT_EQ(ip, GetLoopbackIP(AF_INET6));
+
+  // More tests on GetDefaultLocalAddress with ipv6 addresses where the set
+  // default address may be different from the best IP address of any network.
+  InterfaceAddress ip1;
+  EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:1111",
+                           IPV6_ADDRESS_FLAG_TEMPORARY, &ip1));
+  // Create a network with a prefix of ip1.
+  Network ipv6_network("test_eth0", "Test NetworkAdapter", TruncateIP(ip1, 64),
+                       64);
+  IPAddress ip2;
+  EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:2222", &ip2));
+  ipv6_network.AddIP(ip1);
+  ipv6_network.AddIP(ip2);
+  BasicNetworkManager::NetworkList list(1, new Network(ipv6_network));
+  bool changed;
+  MergeNetworkList(manager, list, &changed);
+  // If the set default address is not in any network, GetDefaultLocalAddress
+  // should return it.
+  IPAddress ip3;
+  EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:3333", &ip3));
+  manager.set_default_local_addresses(GetLoopbackIP(AF_INET), ip3);
+  EXPECT_TRUE(manager.GetDefaultLocalAddress(AF_INET6, &ip));
+  EXPECT_EQ(ip3, ip);
+  // If the set default address is in a network, GetDefaultLocalAddress will
+  // return the best IP in that network.
+  manager.set_default_local_addresses(GetLoopbackIP(AF_INET), ip2);
+  EXPECT_TRUE(manager.GetDefaultLocalAddress(AF_INET6, &ip));
+  EXPECT_EQ(static_cast<IPAddress>(ip1), ip);
+
+  manager.StopUpdating();
+}
+
+}  // namespace rtc
diff --git a/base/networkmonitor.cc b/base/networkmonitor.cc
new file mode 100644
index 0000000..e11e812
--- /dev/null
+++ b/base/networkmonitor.cc
@@ -0,0 +1,62 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/networkmonitor.h"
+
+#include "webrtc/base/checks.h"
+
+namespace {
+const uint32_t UPDATE_NETWORKS_MESSAGE = 1;
+
+// This is set by NetworkMonitorFactory::SetFactory and the caller of
+// NetworkMonitorFactory::SetFactory must be responsible for calling
+// ReleaseFactory to destroy the factory.
+rtc::NetworkMonitorFactory* network_monitor_factory = nullptr;
+}  // namespace
+
+namespace rtc {
+NetworkMonitorInterface::NetworkMonitorInterface() {}
+
+NetworkMonitorInterface::~NetworkMonitorInterface() {}
+
+NetworkMonitorBase::NetworkMonitorBase() : worker_thread_(Thread::Current()) {}
+NetworkMonitorBase::~NetworkMonitorBase() {}
+
+void NetworkMonitorBase::OnNetworksChanged() {
+  LOG(LS_VERBOSE) << "Network change is received at the network monitor";
+  worker_thread_->Post(RTC_FROM_HERE, this, UPDATE_NETWORKS_MESSAGE);
+}
+
+void NetworkMonitorBase::OnMessage(Message* msg) {
+  RTC_DCHECK(msg->message_id == UPDATE_NETWORKS_MESSAGE);
+  SignalNetworksChanged();
+}
+
+NetworkMonitorFactory::NetworkMonitorFactory() {}
+NetworkMonitorFactory::~NetworkMonitorFactory() {}
+
+void NetworkMonitorFactory::SetFactory(NetworkMonitorFactory* factory) {
+  if (network_monitor_factory != nullptr) {
+    delete network_monitor_factory;
+  }
+  network_monitor_factory = factory;
+}
+
+void NetworkMonitorFactory::ReleaseFactory(NetworkMonitorFactory* factory) {
+  if (factory == network_monitor_factory) {
+    SetFactory(nullptr);
+  }
+}
+
+NetworkMonitorFactory* NetworkMonitorFactory::GetFactory() {
+  return network_monitor_factory;
+}
+
+}  // namespace rtc
diff --git a/base/networkmonitor.h b/base/networkmonitor.h
index 290da4f..72b07b4 100644
--- a/base/networkmonitor.h
+++ b/base/networkmonitor.h
@@ -11,9 +11,118 @@
 #ifndef WEBRTC_BASE_NETWORKMONITOR_H_
 #define WEBRTC_BASE_NETWORKMONITOR_H_
 
+#include "webrtc/base/logging.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/thread.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/networkmonitor.h"
+namespace rtc {
+
+class IPAddress;
+
+enum class NetworkBindingResult {
+  SUCCESS = 0,   // No error
+  FAILURE = -1,  // Generic error
+  NOT_IMPLEMENTED = -2,
+  ADDRESS_NOT_FOUND = -3,
+  NETWORK_CHANGED = -4
+};
+
+enum AdapterType {
+  // This enum resembles the one in Chromium net::ConnectionType.
+  ADAPTER_TYPE_UNKNOWN = 0,
+  ADAPTER_TYPE_ETHERNET = 1 << 0,
+  ADAPTER_TYPE_WIFI = 1 << 1,
+  ADAPTER_TYPE_CELLULAR = 1 << 2,
+  ADAPTER_TYPE_VPN = 1 << 3,
+  ADAPTER_TYPE_LOOPBACK = 1 << 4
+};
+
+class NetworkBinderInterface {
+ public:
+  // Binds a socket to the network that is attached to |address| so that all
+  // packets on the socket |socket_fd| will be sent via that network.
+  // This is needed because some operating systems (like Android) require a
+  // special bind call to put packets on a non-default network interface.
+  virtual NetworkBindingResult BindSocketToNetwork(
+      int socket_fd,
+      const IPAddress& address) = 0;
+  virtual ~NetworkBinderInterface() {}
+};
+
+/*
+ * Receives network-change events via |OnNetworksChanged| and signals the
+ * networks changed event.
+ *
+ * Threading consideration:
+ * It is expected that all upstream operations (from native to Java) are
+ * performed from the worker thread. This includes creating, starting and
+ * stopping the monitor. This avoids the potential race condition when creating
+ * the singleton Java NetworkMonitor class. Downstream operations can be from
+ * any thread, but this class will forward all the downstream operations onto
+ * the worker thread.
+ *
+ * Memory consideration:
+ * NetworkMonitor is owned by the caller (NetworkManager). The global network
+ * monitor factory is owned by the factory itself but needs to be released from
+ * the factory creator.
+ */
+// Generic network monitor interface. It starts and stops monitoring network
+// changes, and fires the SignalNetworksChanged event when networks change.
+class NetworkMonitorInterface {
+ public:
+  NetworkMonitorInterface();
+  virtual ~NetworkMonitorInterface();
+
+  sigslot::signal0<> SignalNetworksChanged;
+
+  virtual void Start() = 0;
+  virtual void Stop() = 0;
+
+  // Implementations should call this method on the base when networks change,
+  // and the base will fire SignalNetworksChanged on the right thread.
+  virtual void OnNetworksChanged() = 0;
+
+  virtual AdapterType GetAdapterType(const std::string& interface_name) = 0;
+};
+
+class NetworkMonitorBase : public NetworkMonitorInterface,
+                           public MessageHandler,
+                           public sigslot::has_slots<> {
+ public:
+  NetworkMonitorBase();
+  ~NetworkMonitorBase() override;
+
+  void OnNetworksChanged() override;
+
+  void OnMessage(Message* msg) override;
+
+ protected:
+  Thread* worker_thread() { return worker_thread_; }
+
+ private:
+  Thread* worker_thread_;
+};
+
+/*
+ * NetworkMonitorFactory creates NetworkMonitors.
+ */
+class NetworkMonitorFactory {
+ public:
+  // This is not thread-safe; it should be called once (or once per audio/video
+  // call) during the call initialization.
+  static void SetFactory(NetworkMonitorFactory* factory);
+
+  static void ReleaseFactory(NetworkMonitorFactory* factory);
+  static NetworkMonitorFactory* GetFactory();
+
+  virtual NetworkMonitorInterface* CreateNetworkMonitor() = 0;
+
+  virtual ~NetworkMonitorFactory();
+
+ protected:
+  NetworkMonitorFactory();
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_NETWORKMONITOR_H_
diff --git a/base/networkroute.h b/base/networkroute.h
index b5e8c13..a34e6d3 100644
--- a/base/networkroute.h
+++ b/base/networkroute.h
@@ -11,9 +11,43 @@
 #ifndef WEBRTC_BASE_NETWORKROUTE_H_
 #define WEBRTC_BASE_NETWORKROUTE_H_
 
+// TODO(honghaiz): Make a directory that describes the interfaces and structs
+// the media code can rely on and the network code can implement, and both can
+// depend on that, but not depend on each other. Then, move this file to that
+// directory.
+namespace rtc {
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/networkroute.h"
+struct NetworkRoute {
+  bool connected;
+  uint16_t local_network_id;
+  uint16_t remote_network_id;
+  int last_sent_packet_id;  // Last packet id sent on the PREVIOUS route.
+
+  NetworkRoute()
+      : connected(false),
+        local_network_id(0),
+        remote_network_id(0),
+        last_sent_packet_id(-1) {}
+
+  // The route is connected if the local and remote network ids are provided.
+  NetworkRoute(bool connected,
+               uint16_t local_net_id,
+               uint16_t remote_net_id,
+               int last_packet_id)
+      : connected(connected),
+        local_network_id(local_net_id),
+        remote_network_id(remote_net_id),
+        last_sent_packet_id(last_packet_id) {}
+
+  // |last_sent_packet_id| does not affect the NetworkRoute comparison.
+  bool operator==(const NetworkRoute& nr) const {
+    return connected == nr.connected &&
+           local_network_id == nr.local_network_id &&
+           remote_network_id == nr.remote_network_id;
+  }
+
+  bool operator!=(const NetworkRoute& nr) const { return !(*this == nr); }
+};
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_NETWORKROUTE_H_
diff --git a/base/nullsocketserver.cc b/base/nullsocketserver.cc
new file mode 100644
index 0000000..5dfd490
--- /dev/null
+++ b/base/nullsocketserver.cc
@@ -0,0 +1,49 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/nullsocketserver.h"
+
+namespace rtc {
+
+NullSocketServer::NullSocketServer() : event_(false, false) {}
+NullSocketServer::~NullSocketServer() {}
+
+bool NullSocketServer::Wait(int cms, bool process_io) {
+  event_.Wait(cms);
+  return true;
+}
+
+void NullSocketServer::WakeUp() {
+  event_.Set();
+}
+
+rtc::Socket* NullSocketServer::CreateSocket(int /* type */) {
+  RTC_NOTREACHED();
+  return nullptr;
+}
+
+rtc::Socket* NullSocketServer::CreateSocket(int /* family */, int /* type */) {
+  RTC_NOTREACHED();
+  return nullptr;
+}
+
+rtc::AsyncSocket* NullSocketServer::CreateAsyncSocket(int /* type */) {
+  RTC_NOTREACHED();
+  return nullptr;
+}
+
+rtc::AsyncSocket* NullSocketServer::CreateAsyncSocket(int /* family */,
+                                                      int /* type */) {
+  RTC_NOTREACHED();
+  return nullptr;
+}
+
+}  // namespace rtc
diff --git a/base/nullsocketserver.h b/base/nullsocketserver.h
index 214c542..e59f2fa 100644
--- a/base/nullsocketserver.h
+++ b/base/nullsocketserver.h
@@ -11,9 +11,28 @@
 #ifndef WEBRTC_BASE_NULLSOCKETSERVER_H_
 #define WEBRTC_BASE_NULLSOCKETSERVER_H_
 
+#include "webrtc/base/event.h"
+#include "webrtc/base/socketserver.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/nullsocketserver.h"
+namespace rtc {
+
+class NullSocketServer : public SocketServer {
+ public:
+  NullSocketServer();
+  ~NullSocketServer() override;
+
+  bool Wait(int cms, bool process_io) override;
+  void WakeUp() override;
+
+  Socket* CreateSocket(int type) override;
+  Socket* CreateSocket(int family, int type) override;
+  AsyncSocket* CreateAsyncSocket(int type) override;
+  AsyncSocket* CreateAsyncSocket(int family, int type) override;
+
+ private:
+  Event event_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_NULLSOCKETSERVER_H_
diff --git a/base/nullsocketserver_unittest.cc b/base/nullsocketserver_unittest.cc
new file mode 100644
index 0000000..fb059c5
--- /dev/null
+++ b/base/nullsocketserver_unittest.cc
@@ -0,0 +1,47 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/nullsocketserver.h"
+
+namespace rtc {
+
+static const uint32_t kTimeout = 5000U;
+
+class NullSocketServerTest
+    : public testing::Test,
+      public MessageHandler {
+ public:
+  NullSocketServerTest() {}
+ protected:
+  virtual void OnMessage(Message* message) {
+    ss_.WakeUp();
+  }
+  NullSocketServer ss_;
+};
+
+TEST_F(NullSocketServerTest, WaitAndSet) {
+  Thread thread;
+  EXPECT_TRUE(thread.Start());
+  thread.Post(RTC_FROM_HERE, this, 0);
+  // The process_io will be ignored.
+  const bool process_io = true;
+  EXPECT_TRUE_WAIT(ss_.Wait(SocketServer::kForever, process_io), kTimeout);
+}
+
+TEST_F(NullSocketServerTest, TestWait) {
+  int64_t start = TimeMillis();
+  ss_.Wait(200, true);
+  // The actual wait time is dependent on the resolution of the timer used by
+  // the Event class. Allow for the event to signal ~20ms early.
+  EXPECT_GE(TimeSince(start), 180);
+}
+
+}  // namespace rtc
diff --git a/base/numerics/exp_filter.cc b/base/numerics/exp_filter.cc
new file mode 100644
index 0000000..a4754a6
--- /dev/null
+++ b/base/numerics/exp_filter.cc
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/numerics/exp_filter.h"
+
+#include <math.h>
+
+namespace rtc {
+
+const float ExpFilter::kValueUndefined = -1.0f;
+
+void ExpFilter::Reset(float alpha) {
+  alpha_ = alpha;
+  filtered_ = kValueUndefined;
+}
+
+float ExpFilter::Apply(float exp, float sample) {
+  if (filtered_ == kValueUndefined) {
+    // Initialize filtered value.
+    filtered_ = sample;
+  } else if (exp == 1.0) {
+    filtered_ = alpha_ * filtered_ + (1 - alpha_) * sample;
+  } else {
+    float alpha = pow(alpha_, exp);
+    filtered_ = alpha * filtered_ + (1 - alpha) * sample;
+  }
+  if (max_ != kValueUndefined && filtered_ > max_) {
+    filtered_ = max_;
+  }
+  return filtered_;
+}
+
+void ExpFilter::UpdateBase(float alpha) {
+  alpha_ = alpha;
+}
+}  // namespace rtc
diff --git a/base/numerics/exp_filter.h b/base/numerics/exp_filter.h
index a4eaea2..2361702 100644
--- a/base/numerics/exp_filter.h
+++ b/base/numerics/exp_filter.h
@@ -11,9 +11,38 @@
 #ifndef WEBRTC_BASE_NUMERICS_EXP_FILTER_H_
 #define WEBRTC_BASE_NUMERICS_EXP_FILTER_H_
 
+namespace rtc {
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/numerics/exp_filter.h"
+// This class can be used, for example, for smoothing the result of bandwidth
+// estimation and packet loss estimation.
+
+class ExpFilter {
+ public:
+  static const float kValueUndefined;
+
+  explicit ExpFilter(float alpha, float max = kValueUndefined) : max_(max) {
+    Reset(alpha);
+  }
+
+  // Resets the filter to its initial state, and resets filter factor base to
+  // the given value |alpha|.
+  void Reset(float alpha);
+
+  // Applies the filter with a given exponent on the provided sample:
+  // y(k) = min(alpha_^ exp * y(k-1) + (1 - alpha_^ exp) * sample, max_).
+  float Apply(float exp, float sample);
+
+  // Returns current filtered value.
+  float filtered() const { return filtered_; }
+
+  // Changes the filter factor base to the given value |alpha|.
+  void UpdateBase(float alpha);
+
+ private:
+  float alpha_;     // Filter factor base.
+  float filtered_;  // Current filter output.
+  const float max_;
+};
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_NUMERICS_EXP_FILTER_H_
diff --git a/base/numerics/exp_filter_unittest.cc b/base/numerics/exp_filter_unittest.cc
new file mode 100644
index 0000000..fbe88e0
--- /dev/null
+++ b/base/numerics/exp_filter_unittest.cc
@@ -0,0 +1,71 @@
+/*
+ *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <math.h>
+
+#include "webrtc/base/numerics/exp_filter.h"
+#include "webrtc/test/gtest.h"
+
+namespace rtc {
+
+TEST(ExpFilterTest, FirstTimeOutputEqualInput) {
+  // No max value defined.
+  ExpFilter filter = ExpFilter(0.9f);
+  filter.Apply(100.0f, 10.0f);
+
+  // First time, first argument no effect.
+  double value = 10.0f;
+  EXPECT_FLOAT_EQ(value, filter.filtered());
+}
+
+TEST(ExpFilterTest, SecondTime) {
+  double value;
+
+  ExpFilter filter = ExpFilter(0.9f);
+  filter.Apply(100.0f, 10.0f);
+
+  // First time, first argument no effect.
+  value = 10.0f;
+
+  filter.Apply(10.0f, 20.0f);
+  double alpha = pow(0.9f, 10.0f);
+  value = alpha * value + (1.0f - alpha) * 20.0f;
+  EXPECT_FLOAT_EQ(value, filter.filtered());
+}
+
+TEST(ExpFilterTest, Reset) {
+  ExpFilter filter = ExpFilter(0.9f);
+  filter.Apply(100.0f, 10.0f);
+
+  filter.Reset(0.8f);
+  filter.Apply(100.0f, 1.0f);
+
+  // Become first time after a reset.
+  double value = 1.0f;
+  EXPECT_FLOAT_EQ(value, filter.filtered());
+}
+
+TEST(ExpfilterTest, OutputLimitedByMax) {
+  double value;
+
+  // Max value defined.
+  ExpFilter filter = ExpFilter(0.9f, 1.0f);
+  filter.Apply(100.0f, 10.0f);
+
+  // Limited to max value.
+  value = 1.0f;
+  EXPECT_EQ(value, filter.filtered());
+
+  filter.Apply(1.0f, 0.0f);
+  value = 0.9f * value;
+  EXPECT_FLOAT_EQ(value, filter.filtered());
+}
+
+}  // namespace rtc
diff --git a/base/numerics/percentile_filter.h b/base/numerics/percentile_filter.h
index a9058a2..638857d 100644
--- a/base/numerics/percentile_filter.h
+++ b/base/numerics/percentile_filter.h
@@ -11,9 +11,105 @@
 #ifndef WEBRTC_BASE_NUMERICS_PERCENTILE_FILTER_H_
 #define WEBRTC_BASE_NUMERICS_PERCENTILE_FILTER_H_
 
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/numerics/percentile_filter.h"
+#include <iterator>
+#include <set>
+
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+// Class to efficiently get the percentile value from a group of observations.
+// The percentile is the value below which a given percentage of the
+// observations fall.
+template <typename T>
+class PercentileFilter {
+ public:
+  // Construct filter. |percentile| should be between 0 and 1.
+  explicit PercentileFilter(float percentile);
+
+  // Insert one observation. The complexity of this operation is logarithmic in
+  // the size of the container.
+  void Insert(const T& value);
+
+  // Remove one observation or return false if |value| doesn't exist in the
+  // container. The complexity of this operation is logarithmic in the size of
+  // the container.
+  bool Erase(const T& value);
+
+  // Get the percentile value. The complexity of this operation is constant.
+  T GetPercentileValue() const;
+
+ private:
+  // Update iterator and index to point at target percentile value.
+  void UpdatePercentileIterator();
+
+  const float percentile_;
+  std::multiset<T> set_;
+  // Maintain iterator and index of current target percentile value.
+  typename std::multiset<T>::iterator percentile_it_;
+  int64_t percentile_index_;
+};
+
+template <typename T>
+PercentileFilter<T>::PercentileFilter(float percentile)
+    : percentile_(percentile),
+      percentile_it_(set_.begin()),
+      percentile_index_(0) {
+  RTC_CHECK_GE(percentile, 0.0f);
+  RTC_CHECK_LE(percentile, 1.0f);
+}
+
+template <typename T>
+void PercentileFilter<T>::Insert(const T& value) {
+  // Insert element at the upper bound.
+  set_.insert(value);
+  if (set_.size() == 1u) {
+    // First element inserted - initialize percentile iterator and index.
+    percentile_it_ = set_.begin();
+    percentile_index_ = 0;
+  } else if (value < *percentile_it_) {
+    // If new element is before us, increment |percentile_index_|.
+    ++percentile_index_;
+  }
+  UpdatePercentileIterator();
+}
+
+template <typename T>
+bool PercentileFilter<T>::Erase(const T& value) {
+  typename std::multiset<T>::const_iterator it = set_.lower_bound(value);
+  // Ignore erase operation if the element is not present in the current set.
+  if (it == set_.end() || *it != value)
+    return false;
+  if (it == percentile_it_) {
+    // If same iterator, update to the following element. Index is not
+    // affected.
+    percentile_it_ = set_.erase(it);
+  } else {
+    set_.erase(it);
+    // If erased element was before us, decrement |percentile_index_|.
+    if (value <= *percentile_it_)
+      --percentile_index_;
+  }
+  UpdatePercentileIterator();
+  return true;
+}
+
+template <typename T>
+void PercentileFilter<T>::UpdatePercentileIterator() {
+  if (set_.empty())
+    return;
+  const int64_t index = static_cast<int64_t>(percentile_ * (set_.size() - 1));
+  std::advance(percentile_it_, index - percentile_index_);
+  percentile_index_ = index;
+}
+
+template <typename T>
+T PercentileFilter<T>::GetPercentileValue() const {
+  return set_.empty() ? 0 : *percentile_it_;
+}
+
+}  // namespace webrtc
 
 #endif  // WEBRTC_BASE_NUMERICS_PERCENTILE_FILTER_H_
diff --git a/base/numerics/percentile_filter_unittest.cc b/base/numerics/percentile_filter_unittest.cc
new file mode 100644
index 0000000..23e63ec
--- /dev/null
+++ b/base/numerics/percentile_filter_unittest.cc
@@ -0,0 +1,138 @@
+/*
+ *  Copyright 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+#include <climits>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/numerics/percentile_filter.h"
+#include "webrtc/test/gtest.h"
+
+namespace webrtc {
+
+class PercentileFilterTest : public ::testing::TestWithParam<float> {
+ public:
+  PercentileFilterTest() : filter_(GetParam()) {
+    // Make sure the tests are deterministic by seeding with a constant.
+    srand(42);
+  }
+
+ protected:
+  PercentileFilter<int64_t> filter_;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(PercentileFilterTest);
+};
+
+INSTANTIATE_TEST_CASE_P(PercentileFilterTests,
+                        PercentileFilterTest,
+                        ::testing::Values(0.0f, 0.1f, 0.5f, 0.9f, 1.0f));
+
+TEST(PercentileFilterTest, MinFilter) {
+  PercentileFilter<int64_t> filter(0.0f);
+  filter.Insert(4);
+  EXPECT_EQ(4, filter.GetPercentileValue());
+  filter.Insert(3);
+  EXPECT_EQ(3, filter.GetPercentileValue());
+}
+
+TEST(PercentileFilterTest, MaxFilter) {
+  PercentileFilter<int64_t> filter(1.0f);
+  filter.Insert(3);
+  EXPECT_EQ(3, filter.GetPercentileValue());
+  filter.Insert(4);
+  EXPECT_EQ(4, filter.GetPercentileValue());
+}
+
+TEST(PercentileFilterTest, MedianFilterDouble) {
+  PercentileFilter<double> filter(0.5f);
+  filter.Insert(2.71828);
+  filter.Insert(3.14159);
+  filter.Insert(1.41421);
+  EXPECT_EQ(2.71828, filter.GetPercentileValue());
+}
+
+TEST(PercentileFilterTest, MedianFilterInt) {
+  PercentileFilter<int> filter(0.5f);
+  filter.Insert(INT_MIN);
+  filter.Insert(1);
+  filter.Insert(2);
+  EXPECT_EQ(1, filter.GetPercentileValue());
+  filter.Insert(INT_MAX);
+  filter.Erase(INT_MIN);
+  EXPECT_EQ(2, filter.GetPercentileValue());
+}
+
+TEST(PercentileFilterTest, MedianFilterUnsigned) {
+  PercentileFilter<unsigned> filter(0.5f);
+  filter.Insert(UINT_MAX);
+  filter.Insert(2u);
+  filter.Insert(1u);
+  EXPECT_EQ(2u, filter.GetPercentileValue());
+  filter.Insert(0u);
+  filter.Erase(UINT_MAX);
+  EXPECT_EQ(1u, filter.GetPercentileValue());
+}
+
+TEST_P(PercentileFilterTest, EmptyFilter) {
+  EXPECT_EQ(0, filter_.GetPercentileValue());
+  filter_.Insert(3);
+  bool success = filter_.Erase(3);
+  EXPECT_TRUE(success);
+  EXPECT_EQ(0, filter_.GetPercentileValue());
+}
+
+TEST_P(PercentileFilterTest, EraseNonExistingElement) {
+  bool success = filter_.Erase(3);
+  EXPECT_FALSE(success);
+  EXPECT_EQ(0, filter_.GetPercentileValue());
+  filter_.Insert(4);
+  success = filter_.Erase(3);
+  EXPECT_FALSE(success);
+  EXPECT_EQ(4, filter_.GetPercentileValue());
+}
+
+TEST_P(PercentileFilterTest, DuplicateElements) {
+  filter_.Insert(3);
+  filter_.Insert(3);
+  filter_.Erase(3);
+  EXPECT_EQ(3, filter_.GetPercentileValue());
+}
+
+TEST_P(PercentileFilterTest, InsertAndEraseTenValuesInRandomOrder) {
+  int64_t zero_to_nine[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+  // The percentile value of the ten values above.
+  const int64_t expected_value = static_cast<int64_t>(GetParam() * 9);
+
+  // Insert two sets of |zero_to_nine| in random order.
+  for (int i = 0; i < 2; ++i) {
+    std::random_shuffle(zero_to_nine, zero_to_nine + 10);
+    for (int64_t value : zero_to_nine)
+      filter_.Insert(value);
+    // After inserting a full set of |zero_to_nine|, the percentile should
+    // stay constant.
+    EXPECT_EQ(expected_value, filter_.GetPercentileValue());
+  }
+
+  // Insert and erase sets of |zero_to_nine| in random order a few times.
+  for (int i = 0; i < 3; ++i) {
+    std::random_shuffle(zero_to_nine, zero_to_nine + 10);
+    for (int64_t value : zero_to_nine)
+      filter_.Erase(value);
+    EXPECT_EQ(expected_value, filter_.GetPercentileValue());
+
+    std::random_shuffle(zero_to_nine, zero_to_nine + 10);
+    for (int64_t value : zero_to_nine)
+      filter_.Insert(value);
+    EXPECT_EQ(expected_value, filter_.GetPercentileValue());
+  }
+}
+
+}  // namespace webrtc
diff --git a/base/onetimeevent.h b/base/onetimeevent.h
index 6849bac..240cf14 100644
--- a/base/onetimeevent.h
+++ b/base/onetimeevent.h
@@ -11,9 +11,51 @@
 #ifndef WEBRTC_BASE_ONETIMEEVENT_H_
 #define WEBRTC_BASE_ONETIMEEVENT_H_
 
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/typedefs.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/onetimeevent.h"
+namespace webrtc {
+// Provides a simple way to perform an operation (such as logging) one
+// time in a certain scope.
+// Example:
+//   OneTimeEvent firstFrame;
+//   ...
+//   if (firstFrame()) {
+//     LOG(LS_INFO) << "This is the first frame".
+//   }
+class OneTimeEvent {
+ public:
+  OneTimeEvent() {}
+  bool operator()() {
+    rtc::CritScope cs(&critsect_);
+    if (happened_) {
+      return false;
+    }
+    happened_ = true;
+    return true;
+  }
+
+ private:
+  bool happened_ = false;
+  rtc::CriticalSection critsect_;
+};
+
+// A non-thread-safe, ligher-weight version of the OneTimeEvent class.
+class ThreadUnsafeOneTimeEvent {
+ public:
+  ThreadUnsafeOneTimeEvent() {}
+  bool operator()() {
+    if (happened_) {
+      return false;
+    }
+    happened_ = true;
+    return true;
+  }
+
+ private:
+  bool happened_ = false;
+};
+
+}  // namespace webrtc
 
 #endif  // WEBRTC_BASE_ONETIMEEVENT_H_
diff --git a/base/onetimeevent_unittest.cc b/base/onetimeevent_unittest.cc
new file mode 100644
index 0000000..4ebc139
--- /dev/null
+++ b/base/onetimeevent_unittest.cc
@@ -0,0 +1,33 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/onetimeevent.h"
+
+namespace webrtc {
+
+TEST(OneTimeEventTest, ThreadSafe) {
+  OneTimeEvent ot;
+
+  // The one time event is expected to evaluate to true only the first time.
+  EXPECT_TRUE(ot());
+  EXPECT_FALSE(ot());
+  EXPECT_FALSE(ot());
+}
+
+TEST(OneTimeEventTest, ThreadUnsafe) {
+  ThreadUnsafeOneTimeEvent ot;
+
+  EXPECT_TRUE(ot());
+  EXPECT_FALSE(ot());
+  EXPECT_FALSE(ot());
+}
+
+}  // namespace webrtc
diff --git a/base/openssl.h b/base/openssl.h
index 795af70..2071619 100644
--- a/base/openssl.h
+++ b/base/openssl.h
@@ -11,9 +11,10 @@
 #ifndef WEBRTC_BASE_OPENSSL_H_
 #define WEBRTC_BASE_OPENSSL_H_
 
+#include <openssl/ssl.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/openssl.h"
+#if (OPENSSL_VERSION_NUMBER < 0x10000000L)
+#error OpenSSL is older than 1.0.0, which is the minimum supported version.
+#endif
 
 #endif  // WEBRTC_BASE_OPENSSL_H_
diff --git a/base/openssladapter.cc b/base/openssladapter.cc
new file mode 100644
index 0000000..9cc5ca9
--- /dev/null
+++ b/base/openssladapter.cc
@@ -0,0 +1,1028 @@
+/*
+ *  Copyright 2008 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/openssladapter.h"
+
+#if defined(WEBRTC_POSIX)
+#include <unistd.h>
+#endif
+
+// Must be included first before openssl headers.
+#include "webrtc/base/win32.h"  // NOLINT
+
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/opensslv.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/openssl.h"
+#include "webrtc/base/safe_conversions.h"
+#include "webrtc/base/sslroots.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread.h"
+
+#ifndef OPENSSL_IS_BORINGSSL
+
+// TODO: Use a nicer abstraction for mutex.
+
+#if defined(WEBRTC_WIN)
+  #define MUTEX_TYPE HANDLE
+#define MUTEX_SETUP(x) (x) = CreateMutex(nullptr, FALSE, nullptr)
+#define MUTEX_CLEANUP(x) CloseHandle(x)
+#define MUTEX_LOCK(x) WaitForSingleObject((x), INFINITE)
+#define MUTEX_UNLOCK(x) ReleaseMutex(x)
+#define THREAD_ID GetCurrentThreadId()
+#elif defined(WEBRTC_POSIX)
+  #define MUTEX_TYPE pthread_mutex_t
+  #define MUTEX_SETUP(x) pthread_mutex_init(&(x), nullptr)
+  #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
+  #define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
+  #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
+  #define THREAD_ID pthread_self()
+#else
+  #error You must define mutex operations appropriate for your platform!
+#endif
+
+struct CRYPTO_dynlock_value {
+  MUTEX_TYPE mutex;
+};
+
+#endif  // #ifndef OPENSSL_IS_BORINGSSL
+
+//////////////////////////////////////////////////////////////////////
+// SocketBIO
+//////////////////////////////////////////////////////////////////////
+
+static int socket_write(BIO* h, const char* buf, int num);
+static int socket_read(BIO* h, char* buf, int size);
+static int socket_puts(BIO* h, const char* str);
+static long socket_ctrl(BIO* h, int cmd, long arg1, void* arg2);
+static int socket_new(BIO* h);
+static int socket_free(BIO* data);
+
+// TODO(davidben): This should be const once BoringSSL is assumed.
+static BIO_METHOD methods_socket = {
+    BIO_TYPE_BIO, "socket",   socket_write, socket_read, socket_puts, 0,
+    socket_ctrl,  socket_new, socket_free,  nullptr,
+};
+
+static BIO_METHOD* BIO_s_socket2() { return(&methods_socket); }
+
+static BIO* BIO_new_socket(rtc::AsyncSocket* socket) {
+  BIO* ret = BIO_new(BIO_s_socket2());
+  if (ret == nullptr) {
+    return nullptr;
+  }
+  ret->ptr = socket;
+  return ret;
+}
+
+static int socket_new(BIO* b) {
+  b->shutdown = 0;
+  b->init = 1;
+  b->num = 0; // 1 means socket closed
+  b->ptr = 0;
+  return 1;
+}
+
+static int socket_free(BIO* b) {
+  if (b == nullptr)
+    return 0;
+  return 1;
+}
+
+static int socket_read(BIO* b, char* out, int outl) {
+  if (!out)
+    return -1;
+  rtc::AsyncSocket* socket = static_cast<rtc::AsyncSocket*>(b->ptr);
+  BIO_clear_retry_flags(b);
+  int result = socket->Recv(out, outl, nullptr);
+  if (result > 0) {
+    return result;
+  } else if (result == 0) {
+    b->num = 1;
+  } else if (socket->IsBlocking()) {
+    BIO_set_retry_read(b);
+  }
+  return -1;
+}
+
+static int socket_write(BIO* b, const char* in, int inl) {
+  if (!in)
+    return -1;
+  rtc::AsyncSocket* socket = static_cast<rtc::AsyncSocket*>(b->ptr);
+  BIO_clear_retry_flags(b);
+  int result = socket->Send(in, inl);
+  if (result > 0) {
+    return result;
+  } else if (socket->IsBlocking()) {
+    BIO_set_retry_write(b);
+  }
+  return -1;
+}
+
+static int socket_puts(BIO* b, const char* str) {
+  return socket_write(b, str, rtc::checked_cast<int>(strlen(str)));
+}
+
+static long socket_ctrl(BIO* b, int cmd, long num, void* ptr) {
+  switch (cmd) {
+  case BIO_CTRL_RESET:
+    return 0;
+  case BIO_CTRL_EOF:
+    return b->num;
+  case BIO_CTRL_WPENDING:
+  case BIO_CTRL_PENDING:
+    return 0;
+  case BIO_CTRL_FLUSH:
+    return 1;
+  default:
+    return 0;
+  }
+}
+
+static void LogSslError() {
+  // Walk down the error stack to find the SSL error.
+  uint32_t error_code;
+  const char* file;
+  int line;
+  do {
+    error_code = ERR_get_error_line(&file, &line);
+    if (ERR_GET_LIB(error_code) == ERR_LIB_SSL) {
+      LOG(LS_ERROR) << "ERR_LIB_SSL: " << error_code << ", " << file << ":"
+                    << line;
+      break;
+    }
+  } while (error_code != 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// OpenSSLAdapter
+/////////////////////////////////////////////////////////////////////////////
+
+namespace rtc {
+
+#ifndef OPENSSL_IS_BORINGSSL
+
+// This array will store all of the mutexes available to OpenSSL.
+static MUTEX_TYPE* mutex_buf = nullptr;
+
+static void locking_function(int mode, int n, const char * file, int line) {
+  if (mode & CRYPTO_LOCK) {
+    MUTEX_LOCK(mutex_buf[n]);
+  } else {
+    MUTEX_UNLOCK(mutex_buf[n]);
+  }
+}
+
+static unsigned long id_function() {  // NOLINT
+  // Use old-style C cast because THREAD_ID's type varies with the platform,
+  // in some cases requiring static_cast, and in others requiring
+  // reinterpret_cast.
+  return (unsigned long)THREAD_ID; // NOLINT
+}
+
+static CRYPTO_dynlock_value* dyn_create_function(const char* file, int line) {
+  CRYPTO_dynlock_value* value = new CRYPTO_dynlock_value;
+  if (!value)
+    return nullptr;
+  MUTEX_SETUP(value->mutex);
+  return value;
+}
+
+static void dyn_lock_function(int mode, CRYPTO_dynlock_value* l,
+                              const char* file, int line) {
+  if (mode & CRYPTO_LOCK) {
+    MUTEX_LOCK(l->mutex);
+  } else {
+    MUTEX_UNLOCK(l->mutex);
+  }
+}
+
+static void dyn_destroy_function(CRYPTO_dynlock_value* l,
+                                 const char* file, int line) {
+  MUTEX_CLEANUP(l->mutex);
+  delete l;
+}
+
+#endif  // #ifndef OPENSSL_IS_BORINGSSL
+
+VerificationCallback OpenSSLAdapter::custom_verify_callback_ = nullptr;
+
+bool OpenSSLAdapter::InitializeSSL(VerificationCallback callback) {
+  if (!InitializeSSLThread() || !SSL_library_init())
+      return false;
+#if !defined(ADDRESS_SANITIZER) || !defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
+  // Loading the error strings crashes mac_asan.  Omit this debugging aid there.
+  SSL_load_error_strings();
+#endif
+  ERR_load_BIO_strings();
+  OpenSSL_add_all_algorithms();
+  RAND_poll();
+  custom_verify_callback_ = callback;
+  return true;
+}
+
+bool OpenSSLAdapter::InitializeSSLThread() {
+  // BoringSSL is doing the locking internally, so the callbacks are not used
+  // in this case (and are no-ops anyways).
+#ifndef OPENSSL_IS_BORINGSSL
+  mutex_buf = new MUTEX_TYPE[CRYPTO_num_locks()];
+  if (!mutex_buf)
+    return false;
+  for (int i = 0; i < CRYPTO_num_locks(); ++i)
+    MUTEX_SETUP(mutex_buf[i]);
+
+  // we need to cast our id_function to return an unsigned long -- pthread_t is
+  // a pointer
+  CRYPTO_set_id_callback(id_function);
+  CRYPTO_set_locking_callback(locking_function);
+  CRYPTO_set_dynlock_create_callback(dyn_create_function);
+  CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
+  CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
+#endif  // #ifndef OPENSSL_IS_BORINGSSL
+  return true;
+}
+
+bool OpenSSLAdapter::CleanupSSL() {
+#ifndef OPENSSL_IS_BORINGSSL
+  if (!mutex_buf)
+    return false;
+  CRYPTO_set_id_callback(nullptr);
+  CRYPTO_set_locking_callback(nullptr);
+  CRYPTO_set_dynlock_create_callback(nullptr);
+  CRYPTO_set_dynlock_lock_callback(nullptr);
+  CRYPTO_set_dynlock_destroy_callback(nullptr);
+  for (int i = 0; i < CRYPTO_num_locks(); ++i)
+    MUTEX_CLEANUP(mutex_buf[i]);
+  delete [] mutex_buf;
+  mutex_buf = nullptr;
+#endif  // #ifndef OPENSSL_IS_BORINGSSL
+  return true;
+}
+
+OpenSSLAdapter::OpenSSLAdapter(AsyncSocket* socket)
+    : SSLAdapter(socket),
+      state_(SSL_NONE),
+      ssl_read_needs_write_(false),
+      ssl_write_needs_read_(false),
+      restartable_(false),
+      ssl_(nullptr),
+      ssl_ctx_(nullptr),
+      ssl_mode_(SSL_MODE_TLS),
+      custom_verification_succeeded_(false) {}
+
+OpenSSLAdapter::~OpenSSLAdapter() {
+  Cleanup();
+}
+
+void
+OpenSSLAdapter::SetMode(SSLMode mode) {
+  RTC_DCHECK(state_ == SSL_NONE);
+  ssl_mode_ = mode;
+}
+
+int
+OpenSSLAdapter::StartSSL(const char* hostname, bool restartable) {
+  if (state_ != SSL_NONE)
+    return -1;
+
+  ssl_host_name_ = hostname;
+  restartable_ = restartable;
+
+  if (socket_->GetState() != Socket::CS_CONNECTED) {
+    state_ = SSL_WAIT;
+    return 0;
+  }
+
+  state_ = SSL_CONNECTING;
+  if (int err = BeginSSL()) {
+    Error("BeginSSL", err, false);
+    return err;
+  }
+
+  return 0;
+}
+
+int
+OpenSSLAdapter::BeginSSL() {
+  LOG(LS_INFO) << "BeginSSL: " << ssl_host_name_;
+  RTC_DCHECK(state_ == SSL_CONNECTING);
+
+  int err = 0;
+  BIO* bio = nullptr;
+
+  // First set up the context
+  if (!ssl_ctx_)
+    ssl_ctx_ = SetupSSLContext();
+
+  if (!ssl_ctx_) {
+    err = -1;
+    goto ssl_error;
+  }
+
+  bio = BIO_new_socket(socket_);
+  if (!bio) {
+    err = -1;
+    goto ssl_error;
+  }
+
+  ssl_ = SSL_new(ssl_ctx_);
+  if (!ssl_) {
+    err = -1;
+    goto ssl_error;
+  }
+
+  SSL_set_app_data(ssl_, this);
+
+  SSL_set_bio(ssl_, bio, bio);
+  // SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER allows different buffers to be passed
+  // into SSL_write when a record could only be partially transmitted (and thus
+  // requires another call to SSL_write to finish transmission). This allows us
+  // to copy the data into our own buffer when this occurs, since the original
+  // buffer can't safely be accessed after control exits Send.
+  // TODO(deadbeef): Do we want SSL_MODE_ENABLE_PARTIAL_WRITE? It doesn't
+  // appear Send handles partial writes properly, though maybe we never notice
+  // since we never send more than 16KB at once..
+  SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE |
+                     SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+  // Enable SNI.
+  if (!ssl_host_name_.empty()) {
+    SSL_set_tlsext_host_name(ssl_, ssl_host_name_.c_str());
+  }
+
+  // the SSL object owns the bio now
+  bio = nullptr;
+
+  // Do the connect
+  err = ContinueSSL();
+  if (err != 0)
+    goto ssl_error;
+
+  return err;
+
+ssl_error:
+  Cleanup();
+  if (bio)
+    BIO_free(bio);
+
+  return err;
+}
+
+int
+OpenSSLAdapter::ContinueSSL() {
+  RTC_DCHECK(state_ == SSL_CONNECTING);
+
+  // Clear the DTLS timer
+  Thread::Current()->Clear(this, MSG_TIMEOUT);
+
+  int code = SSL_connect(ssl_);
+  switch (SSL_get_error(ssl_, code)) {
+  case SSL_ERROR_NONE:
+    if (!SSLPostConnectionCheck(ssl_, ssl_host_name_.c_str())) {
+      LOG(LS_ERROR) << "TLS post connection check failed";
+      // make sure we close the socket
+      Cleanup();
+      // The connect failed so return -1 to shut down the socket
+      return -1;
+    }
+
+    state_ = SSL_CONNECTED;
+    AsyncSocketAdapter::OnConnectEvent(this);
+#if 0  // TODO: worry about this
+    // Don't let ourselves go away during the callbacks
+    PRefPtr<OpenSSLAdapter> lock(this);
+    LOG(LS_INFO) << " -- onStreamReadable";
+    AsyncSocketAdapter::OnReadEvent(this);
+    LOG(LS_INFO) << " -- onStreamWriteable";
+    AsyncSocketAdapter::OnWriteEvent(this);
+#endif
+    break;
+
+  case SSL_ERROR_WANT_READ:
+    LOG(LS_VERBOSE) << " -- error want read";
+    struct timeval timeout;
+    if (DTLSv1_get_timeout(ssl_, &timeout)) {
+      int delay = timeout.tv_sec * 1000 + timeout.tv_usec/1000;
+
+      Thread::Current()->PostDelayed(RTC_FROM_HERE, delay, this, MSG_TIMEOUT,
+                                     0);
+    }
+    break;
+
+  case SSL_ERROR_WANT_WRITE:
+    break;
+
+  case SSL_ERROR_ZERO_RETURN:
+  default:
+    LOG(LS_WARNING) << "ContinueSSL -- error " << code;
+    return (code != 0) ? code : -1;
+  }
+
+  return 0;
+}
+
+void
+OpenSSLAdapter::Error(const char* context, int err, bool signal) {
+  LOG(LS_WARNING) << "OpenSSLAdapter::Error("
+                  << context << ", " << err << ")";
+  state_ = SSL_ERROR;
+  SetError(err);
+  if (signal)
+    AsyncSocketAdapter::OnCloseEvent(this, err);
+}
+
+void
+OpenSSLAdapter::Cleanup() {
+  LOG(LS_INFO) << "Cleanup";
+
+  state_ = SSL_NONE;
+  ssl_read_needs_write_ = false;
+  ssl_write_needs_read_ = false;
+  custom_verification_succeeded_ = false;
+  pending_data_.Clear();
+
+  if (ssl_) {
+    SSL_free(ssl_);
+    ssl_ = nullptr;
+  }
+
+  if (ssl_ctx_) {
+    SSL_CTX_free(ssl_ctx_);
+    ssl_ctx_ = nullptr;
+  }
+
+  // Clear the DTLS timer
+  Thread::Current()->Clear(this, MSG_TIMEOUT);
+}
+
+int OpenSSLAdapter::DoSslWrite(const void* pv, size_t cb, int* error) {
+  // If we have pending data (that was previously only partially written by
+  // SSL_write), we shouldn't be attempting to write anything else.
+  RTC_DCHECK(pending_data_.empty() || pv == pending_data_.data());
+  RTC_DCHECK(error != nullptr);
+
+  ssl_write_needs_read_ = false;
+  int ret = SSL_write(ssl_, pv, checked_cast<int>(cb));
+  *error = SSL_get_error(ssl_, ret);
+  switch (*error) {
+    case SSL_ERROR_NONE:
+      // Success!
+      return ret;
+    case SSL_ERROR_WANT_READ:
+      LOG(LS_INFO) << " -- error want read";
+      ssl_write_needs_read_ = true;
+      SetError(EWOULDBLOCK);
+      break;
+    case SSL_ERROR_WANT_WRITE:
+      LOG(LS_INFO) << " -- error want write";
+      SetError(EWOULDBLOCK);
+      break;
+    case SSL_ERROR_ZERO_RETURN:
+      // LOG(LS_INFO) << " -- remote side closed";
+      SetError(EWOULDBLOCK);
+      // do we need to signal closure?
+      break;
+    case SSL_ERROR_SSL:
+      LogSslError();
+      Error("SSL_write", ret ? ret : -1, false);
+      break;
+    default:
+      LOG(LS_WARNING) << "Unknown error from SSL_write: " << *error;
+      Error("SSL_write", ret ? ret : -1, false);
+      break;
+  }
+
+  return SOCKET_ERROR;
+}
+
+//
+// AsyncSocket Implementation
+//
+
+int
+OpenSSLAdapter::Send(const void* pv, size_t cb) {
+  //LOG(LS_INFO) << "OpenSSLAdapter::Send(" << cb << ")";
+
+  switch (state_) {
+  case SSL_NONE:
+    return AsyncSocketAdapter::Send(pv, cb);
+
+  case SSL_WAIT:
+  case SSL_CONNECTING:
+    SetError(ENOTCONN);
+    return SOCKET_ERROR;
+
+  case SSL_CONNECTED:
+    break;
+
+  case SSL_ERROR:
+  default:
+    return SOCKET_ERROR;
+  }
+
+  int ret;
+  int error;
+
+  if (!pending_data_.empty()) {
+    ret = DoSslWrite(pending_data_.data(), pending_data_.size(), &error);
+    if (ret != static_cast<int>(pending_data_.size())) {
+      // We couldn't finish sending the pending data, so we definitely can't
+      // send any more data. Return with an EWOULDBLOCK error.
+      SetError(EWOULDBLOCK);
+      return SOCKET_ERROR;
+    }
+    // We completed sending the data previously passed into SSL_write! Now
+    // we're allowed to send more data.
+    pending_data_.Clear();
+  }
+
+  // OpenSSL will return an error if we try to write zero bytes
+  if (cb == 0)
+    return 0;
+
+  ret = DoSslWrite(pv, cb, &error);
+
+  // If SSL_write fails with SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, this
+  // means the underlying socket is blocked on reading or (more typically)
+  // writing. When this happens, OpenSSL requires that the next call to
+  // SSL_write uses the same arguments (though, with
+  // SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER, the actual buffer pointer may be
+  // different).
+  //
+  // However, after Send exits, we will have lost access to data the user of
+  // this class is trying to send, and there's no guarantee that the user of
+  // this class will call Send with the same arguements when it fails. So, we
+  // buffer the data ourselves. When we know the underlying socket is writable
+  // again from OnWriteEvent (or if Send is called again before that happens),
+  // we'll retry sending this buffered data.
+  if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
+    // Shouldn't be able to get to this point if we already have pending data.
+    RTC_DCHECK(pending_data_.empty());
+    LOG(LS_WARNING)
+        << "SSL_write couldn't write to the underlying socket; buffering data.";
+    pending_data_.SetData(static_cast<const uint8_t*>(pv), cb);
+    // Since we're taking responsibility for sending this data, return its full
+    // size. The user of this class can consider it sent.
+    return cb;
+  }
+
+  return ret;
+}
+
+int
+OpenSSLAdapter::SendTo(const void* pv, size_t cb, const SocketAddress& addr) {
+  if (socket_->GetState() == Socket::CS_CONNECTED &&
+      addr == socket_->GetRemoteAddress()) {
+    return Send(pv, cb);
+  }
+
+  SetError(ENOTCONN);
+
+  return SOCKET_ERROR;
+}
+
+int OpenSSLAdapter::Recv(void* pv, size_t cb, int64_t* timestamp) {
+  //LOG(LS_INFO) << "OpenSSLAdapter::Recv(" << cb << ")";
+  switch (state_) {
+
+  case SSL_NONE:
+    return AsyncSocketAdapter::Recv(pv, cb, timestamp);
+
+  case SSL_WAIT:
+  case SSL_CONNECTING:
+    SetError(ENOTCONN);
+    return SOCKET_ERROR;
+
+  case SSL_CONNECTED:
+    break;
+
+  case SSL_ERROR:
+  default:
+    return SOCKET_ERROR;
+  }
+
+  // Don't trust OpenSSL with zero byte reads
+  if (cb == 0)
+    return 0;
+
+  ssl_read_needs_write_ = false;
+
+  int code = SSL_read(ssl_, pv, checked_cast<int>(cb));
+  int error = SSL_get_error(ssl_, code);
+  switch (error) {
+    case SSL_ERROR_NONE:
+      // LOG(LS_INFO) << " -- success";
+      return code;
+    case SSL_ERROR_WANT_READ:
+      // LOG(LS_INFO) << " -- error want read";
+      SetError(EWOULDBLOCK);
+      break;
+    case SSL_ERROR_WANT_WRITE:
+      // LOG(LS_INFO) << " -- error want write";
+      ssl_read_needs_write_ = true;
+      SetError(EWOULDBLOCK);
+      break;
+    case SSL_ERROR_ZERO_RETURN:
+      // LOG(LS_INFO) << " -- remote side closed";
+      SetError(EWOULDBLOCK);
+      // do we need to signal closure?
+      break;
+    case SSL_ERROR_SSL:
+      LogSslError();
+      Error("SSL_read", (code ? code : -1), false);
+      break;
+    default:
+      LOG(LS_WARNING) << "Unknown error from SSL_read: " << error;
+      Error("SSL_read", (code ? code : -1), false);
+      break;
+  }
+
+  return SOCKET_ERROR;
+}
+
+int OpenSSLAdapter::RecvFrom(void* pv,
+                             size_t cb,
+                             SocketAddress* paddr,
+                             int64_t* timestamp) {
+  if (socket_->GetState() == Socket::CS_CONNECTED) {
+    int ret = Recv(pv, cb, timestamp);
+
+    *paddr = GetRemoteAddress();
+
+    return ret;
+  }
+
+  SetError(ENOTCONN);
+
+  return SOCKET_ERROR;
+}
+
+int
+OpenSSLAdapter::Close() {
+  Cleanup();
+  state_ = restartable_ ? SSL_WAIT : SSL_NONE;
+  return AsyncSocketAdapter::Close();
+}
+
+Socket::ConnState
+OpenSSLAdapter::GetState() const {
+  //if (signal_close_)
+  //  return CS_CONNECTED;
+  ConnState state = socket_->GetState();
+  if ((state == CS_CONNECTED)
+      && ((state_ == SSL_WAIT) || (state_ == SSL_CONNECTING)))
+    state = CS_CONNECTING;
+  return state;
+}
+
+void
+OpenSSLAdapter::OnMessage(Message* msg) {
+  if (MSG_TIMEOUT == msg->message_id) {
+    LOG(LS_INFO) << "DTLS timeout expired";
+    DTLSv1_handle_timeout(ssl_);
+    ContinueSSL();
+  }
+}
+
+void
+OpenSSLAdapter::OnConnectEvent(AsyncSocket* socket) {
+  LOG(LS_INFO) << "OpenSSLAdapter::OnConnectEvent";
+  if (state_ != SSL_WAIT) {
+    RTC_DCHECK(state_ == SSL_NONE);
+    AsyncSocketAdapter::OnConnectEvent(socket);
+    return;
+  }
+
+  state_ = SSL_CONNECTING;
+  if (int err = BeginSSL()) {
+    AsyncSocketAdapter::OnCloseEvent(socket, err);
+  }
+}
+
+void
+OpenSSLAdapter::OnReadEvent(AsyncSocket* socket) {
+  //LOG(LS_INFO) << "OpenSSLAdapter::OnReadEvent";
+
+  if (state_ == SSL_NONE) {
+    AsyncSocketAdapter::OnReadEvent(socket);
+    return;
+  }
+
+  if (state_ == SSL_CONNECTING) {
+    if (int err = ContinueSSL()) {
+      Error("ContinueSSL", err);
+    }
+    return;
+  }
+
+  if (state_ != SSL_CONNECTED)
+    return;
+
+  // Don't let ourselves go away during the callbacks
+  //PRefPtr<OpenSSLAdapter> lock(this); // TODO: fix this
+  if (ssl_write_needs_read_)  {
+    //LOG(LS_INFO) << " -- onStreamWriteable";
+    AsyncSocketAdapter::OnWriteEvent(socket);
+  }
+
+  //LOG(LS_INFO) << " -- onStreamReadable";
+  AsyncSocketAdapter::OnReadEvent(socket);
+}
+
+void
+OpenSSLAdapter::OnWriteEvent(AsyncSocket* socket) {
+  //LOG(LS_INFO) << "OpenSSLAdapter::OnWriteEvent";
+
+  if (state_ == SSL_NONE) {
+    AsyncSocketAdapter::OnWriteEvent(socket);
+    return;
+  }
+
+  if (state_ == SSL_CONNECTING) {
+    if (int err = ContinueSSL()) {
+      Error("ContinueSSL", err);
+    }
+    return;
+  }
+
+  if (state_ != SSL_CONNECTED)
+    return;
+
+  // Don't let ourselves go away during the callbacks
+  //PRefPtr<OpenSSLAdapter> lock(this); // TODO: fix this
+
+  if (ssl_read_needs_write_)  {
+    //LOG(LS_INFO) << " -- onStreamReadable";
+    AsyncSocketAdapter::OnReadEvent(socket);
+  }
+
+  // If a previous SSL_write failed due to the underlying socket being blocked,
+  // this will attempt finishing the write operation.
+  if (!pending_data_.empty()) {
+    int error;
+    if (DoSslWrite(pending_data_.data(), pending_data_.size(), &error) ==
+        static_cast<int>(pending_data_.size())) {
+      pending_data_.Clear();
+    }
+  }
+
+  //LOG(LS_INFO) << " -- onStreamWriteable";
+  AsyncSocketAdapter::OnWriteEvent(socket);
+}
+
+void
+OpenSSLAdapter::OnCloseEvent(AsyncSocket* socket, int err) {
+  LOG(LS_INFO) << "OpenSSLAdapter::OnCloseEvent(" << err << ")";
+  AsyncSocketAdapter::OnCloseEvent(socket, err);
+}
+
+bool OpenSSLAdapter::VerifyServerName(SSL* ssl, const char* host,
+                                      bool ignore_bad_cert) {
+  if (!host)
+    return false;
+
+  // Checking the return from SSL_get_peer_certificate here is not strictly
+  // necessary.  With our setup, it is not possible for it to return
+  // null.  However, it is good form to check the return.
+  X509* certificate = SSL_get_peer_certificate(ssl);
+  if (!certificate)
+    return false;
+
+  // Logging certificates is extremely verbose. So it is disabled by default.
+#ifdef LOG_CERTIFICATES
+  {
+    LOG(LS_INFO) << "Certificate from server:";
+    BIO* mem = BIO_new(BIO_s_mem());
+    X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER);
+    BIO_write(mem, "\0", 1);
+    char* buffer;
+    BIO_get_mem_data(mem, &buffer);
+    LOG(LS_INFO) << buffer;
+    BIO_free(mem);
+
+    char* cipher_description =
+        SSL_CIPHER_description(SSL_get_current_cipher(ssl), nullptr, 128);
+    LOG(LS_INFO) << "Cipher: " << cipher_description;
+    OPENSSL_free(cipher_description);
+  }
+#endif
+
+  bool ok = false;
+  GENERAL_NAMES* names = reinterpret_cast<GENERAL_NAMES*>(
+      X509_get_ext_d2i(certificate, NID_subject_alt_name, nullptr, nullptr));
+  if (names) {
+    for (size_t i = 0; i < sk_GENERAL_NAME_num(names); i++) {
+      const GENERAL_NAME* name = sk_GENERAL_NAME_value(names, i);
+      if (name->type != GEN_DNS)
+        continue;
+      std::string value(
+          reinterpret_cast<const char*>(ASN1_STRING_data(name->d.dNSName)),
+          ASN1_STRING_length(name->d.dNSName));
+      // string_match takes NUL-terminated strings, so check for embedded NULs.
+      if (value.find('\0') != std::string::npos)
+        continue;
+      if (string_match(host, value.c_str())) {
+        ok = true;
+        break;
+      }
+    }
+    GENERAL_NAMES_free(names);
+  }
+
+  char data[256];
+  X509_NAME* subject;
+  if (!ok && ((subject = X509_get_subject_name(certificate)) != nullptr) &&
+      (X509_NAME_get_text_by_NID(subject, NID_commonName, data, sizeof(data)) >
+       0)) {
+    data[sizeof(data)-1] = 0;
+    if (_stricmp(data, host) == 0)
+      ok = true;
+  }
+
+  X509_free(certificate);
+
+  // This should only ever be turned on for debugging and development.
+  if (!ok && ignore_bad_cert) {
+    LOG(LS_WARNING) << "TLS certificate check FAILED.  "
+      << "Allowing connection anyway.";
+    ok = true;
+  }
+
+  return ok;
+}
+
+bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) {
+  bool ok = VerifyServerName(ssl, host, ignore_bad_cert());
+
+  if (ok) {
+    ok = (SSL_get_verify_result(ssl) == X509_V_OK ||
+          custom_verification_succeeded_);
+  }
+
+  if (!ok && ignore_bad_cert()) {
+    LOG(LS_INFO) << "Other TLS post connection checks failed.";
+    ok = true;
+  }
+
+  return ok;
+}
+
+#if !defined(NDEBUG)
+
+// We only use this for tracing and so it is only needed in debug mode
+
+void
+OpenSSLAdapter::SSLInfoCallback(const SSL* s, int where, int ret) {
+  const char* str = "undefined";
+  int w = where & ~SSL_ST_MASK;
+  if (w & SSL_ST_CONNECT) {
+    str = "SSL_connect";
+  } else if (w & SSL_ST_ACCEPT) {
+    str = "SSL_accept";
+  }
+  if (where & SSL_CB_LOOP) {
+    LOG(LS_INFO) <<  str << ":" << SSL_state_string_long(s);
+  } else if (where & SSL_CB_ALERT) {
+    str = (where & SSL_CB_READ) ? "read" : "write";
+    LOG(LS_INFO) <<  "SSL3 alert " << str
+      << ":" << SSL_alert_type_string_long(ret)
+      << ":" << SSL_alert_desc_string_long(ret);
+  } else if (where & SSL_CB_EXIT) {
+    if (ret == 0) {
+      LOG(LS_INFO) << str << ":failed in " << SSL_state_string_long(s);
+    } else if (ret < 0) {
+      LOG(LS_INFO) << str << ":error in " << SSL_state_string_long(s);
+    }
+  }
+}
+
+#endif
+
+int
+OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
+#if !defined(NDEBUG)
+  if (!ok) {
+    char data[256];
+    X509* cert = X509_STORE_CTX_get_current_cert(store);
+    int depth = X509_STORE_CTX_get_error_depth(store);
+    int err = X509_STORE_CTX_get_error(store);
+
+    LOG(LS_INFO) << "Error with certificate at depth: " << depth;
+    X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data));
+    LOG(LS_INFO) << "  issuer  = " << data;
+    X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data));
+    LOG(LS_INFO) << "  subject = " << data;
+    LOG(LS_INFO) << "  err     = " << err
+      << ":" << X509_verify_cert_error_string(err);
+  }
+#endif
+
+  // Get our stream pointer from the store
+  SSL* ssl = reinterpret_cast<SSL*>(
+                X509_STORE_CTX_get_ex_data(store,
+                  SSL_get_ex_data_X509_STORE_CTX_idx()));
+
+  OpenSSLAdapter* stream =
+    reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
+
+  if (!ok && custom_verify_callback_) {
+    void* cert =
+        reinterpret_cast<void*>(X509_STORE_CTX_get_current_cert(store));
+    if (custom_verify_callback_(cert)) {
+      stream->custom_verification_succeeded_ = true;
+      LOG(LS_INFO) << "validated certificate using custom callback";
+      ok = true;
+    }
+  }
+
+  // Should only be used for debugging and development.
+  if (!ok && stream->ignore_bad_cert()) {
+    LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
+    ok = 1;
+  }
+
+  return ok;
+}
+
+bool OpenSSLAdapter::ConfigureTrustedRootCertificates(SSL_CTX* ctx) {
+  // Add the root cert that we care about to the SSL context
+  int count_of_added_certs = 0;
+  for (size_t i = 0; i < arraysize(kSSLCertCertificateList); i++) {
+    const unsigned char* cert_buffer = kSSLCertCertificateList[i];
+    size_t cert_buffer_len = kSSLCertCertificateSizeList[i];
+    X509* cert =
+        d2i_X509(nullptr, &cert_buffer, checked_cast<long>(cert_buffer_len));
+    if (cert) {
+      int return_value = X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert);
+      if (return_value == 0) {
+        LOG(LS_WARNING) << "Unable to add certificate.";
+      } else {
+        count_of_added_certs++;
+      }
+      X509_free(cert);
+    }
+  }
+  return count_of_added_certs > 0;
+}
+
+SSL_CTX*
+OpenSSLAdapter::SetupSSLContext() {
+  // Use (D)TLS 1.2.
+  // Note: BoringSSL supports a range of versions by setting max/min version
+  // (Default V1.0 to V1.2). However (D)TLSv1_2_client_method functions used
+  // below in OpenSSL only support V1.2.
+  SSL_CTX* ctx = nullptr;
+#ifdef OPENSSL_IS_BORINGSSL
+  ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
+#else
+  ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLSv1_2_client_method()
+                                               : TLSv1_2_client_method());
+#endif  // OPENSSL_IS_BORINGSSL
+  if (ctx == nullptr) {
+    unsigned long error = ERR_get_error();  // NOLINT: type used by OpenSSL.
+    LOG(LS_WARNING) << "SSL_CTX creation failed: "
+                    << '"' << ERR_reason_error_string(error) << "\" "
+                    << "(error=" << error << ')';
+    return nullptr;
+  }
+  if (!ConfigureTrustedRootCertificates(ctx)) {
+    SSL_CTX_free(ctx);
+    return nullptr;
+  }
+
+#if !defined(NDEBUG)
+  SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
+#endif
+
+  SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
+  SSL_CTX_set_verify_depth(ctx, 4);
+  // Use defaults, but disable HMAC-SHA256 and HMAC-SHA384 ciphers
+  // (note that SHA256 and SHA384 only select legacy CBC ciphers).
+  // Additionally disable HMAC-SHA1 ciphers in ECDSA. These are the remaining
+  // CBC-mode ECDSA ciphers.
+  SSL_CTX_set_cipher_list(
+      ctx, "ALL:!SHA256:!SHA384:!aPSK:!ECDSA+SHA1:!ADH:!LOW:!EXP:!MD5");
+
+  if (ssl_mode_ == SSL_MODE_DTLS) {
+    SSL_CTX_set_read_ahead(ctx, 1);
+  }
+
+  return ctx;
+}
+
+} // namespace rtc
diff --git a/base/openssladapter.h b/base/openssladapter.h
index 6444215..2f0150f 100644
--- a/base/openssladapter.h
+++ b/base/openssladapter.h
@@ -8,12 +8,105 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_OPENSSLADAPTER_H_
-#define WEBRTC_BASE_OPENSSLADAPTER_H_
+#ifndef WEBRTC_BASE_OPENSSLADAPTER_H__
+#define WEBRTC_BASE_OPENSSLADAPTER_H__
 
+#include <string>
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/messagehandler.h"
+#include "webrtc/base/messagequeue.h"
+#include "webrtc/base/ssladapter.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/openssladapter.h"
+typedef struct ssl_st SSL;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
 
-#endif // WEBRTC_BASE_OPENSSLADAPTER_H_
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class OpenSSLAdapter : public SSLAdapter, public MessageHandler {
+public:
+  static bool InitializeSSL(VerificationCallback callback);
+  static bool InitializeSSLThread();
+  static bool CleanupSSL();
+
+  OpenSSLAdapter(AsyncSocket* socket);
+  ~OpenSSLAdapter() override;
+
+  void SetMode(SSLMode mode) override;
+  int StartSSL(const char* hostname, bool restartable) override;
+  int Send(const void* pv, size_t cb) override;
+  int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override;
+  int Recv(void* pv, size_t cb, int64_t* timestamp) override;
+  int RecvFrom(void* pv,
+               size_t cb,
+               SocketAddress* paddr,
+               int64_t* timestamp) override;
+  int Close() override;
+
+  // Note that the socket returns ST_CONNECTING while SSL is being negotiated.
+  ConnState GetState() const override;
+
+protected:
+ void OnConnectEvent(AsyncSocket* socket) override;
+ void OnReadEvent(AsyncSocket* socket) override;
+ void OnWriteEvent(AsyncSocket* socket) override;
+ void OnCloseEvent(AsyncSocket* socket, int err) override;
+
+private:
+  enum SSLState {
+    SSL_NONE, SSL_WAIT, SSL_CONNECTING, SSL_CONNECTED, SSL_ERROR
+  };
+
+  enum { MSG_TIMEOUT };
+
+  int BeginSSL();
+  int ContinueSSL();
+  void Error(const char* context, int err, bool signal = true);
+  void Cleanup();
+
+  // Return value and arguments have the same meanings as for Send; |error| is
+  // an output parameter filled with the result of SSL_get_error.
+  int DoSslWrite(const void* pv, size_t cb, int* error);
+
+  void OnMessage(Message* msg) override;
+
+  static bool VerifyServerName(SSL* ssl, const char* host,
+                               bool ignore_bad_cert);
+  bool SSLPostConnectionCheck(SSL* ssl, const char* host);
+#if !defined(NDEBUG)
+  static void SSLInfoCallback(const SSL* s, int where, int ret);
+#endif
+  static int SSLVerifyCallback(int ok, X509_STORE_CTX* store);
+  static VerificationCallback custom_verify_callback_;
+  friend class OpenSSLStreamAdapter;  // for custom_verify_callback_;
+
+  static bool ConfigureTrustedRootCertificates(SSL_CTX* ctx);
+  SSL_CTX* SetupSSLContext();
+
+  SSLState state_;
+  bool ssl_read_needs_write_;
+  bool ssl_write_needs_read_;
+  // If true, socket will retain SSL configuration after Close.
+  bool restartable_;
+
+  // This buffer is used if SSL_write fails with SSL_ERROR_WANT_WRITE, which
+  // means we need to keep retrying with *the same exact data* until it
+  // succeeds. Afterwards it will be cleared.
+  Buffer pending_data_;
+
+  SSL* ssl_;
+  SSL_CTX* ssl_ctx_;
+  std::string ssl_host_name_;
+  // Do DTLS or not
+  SSLMode ssl_mode_;
+
+  bool custom_verification_succeeded_;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+} // namespace rtc
+
+#endif // WEBRTC_BASE_OPENSSLADAPTER_H__
diff --git a/base/openssldigest.cc b/base/openssldigest.cc
new file mode 100644
index 0000000..3115074
--- /dev/null
+++ b/base/openssldigest.cc
@@ -0,0 +1,117 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/openssldigest.h"
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/openssl.h"
+
+namespace rtc {
+
+OpenSSLDigest::OpenSSLDigest(const std::string& algorithm) {
+  EVP_MD_CTX_init(&ctx_);
+  if (GetDigestEVP(algorithm, &md_)) {
+    EVP_DigestInit_ex(&ctx_, md_, nullptr);
+  } else {
+    md_ = nullptr;
+  }
+}
+
+OpenSSLDigest::~OpenSSLDigest() {
+  EVP_MD_CTX_cleanup(&ctx_);
+}
+
+size_t OpenSSLDigest::Size() const {
+  if (!md_) {
+    return 0;
+  }
+  return EVP_MD_size(md_);
+}
+
+void OpenSSLDigest::Update(const void* buf, size_t len) {
+  if (!md_) {
+    return;
+  }
+  EVP_DigestUpdate(&ctx_, buf, len);
+}
+
+size_t OpenSSLDigest::Finish(void* buf, size_t len) {
+  if (!md_ || len < Size()) {
+    return 0;
+  }
+  unsigned int md_len;
+  EVP_DigestFinal_ex(&ctx_, static_cast<unsigned char*>(buf), &md_len);
+  EVP_DigestInit_ex(&ctx_, md_, nullptr);  // prepare for future Update()s
+  RTC_DCHECK(md_len == Size());
+  return md_len;
+}
+
+bool OpenSSLDigest::GetDigestEVP(const std::string& algorithm,
+                                 const EVP_MD** mdp) {
+  const EVP_MD* md;
+  if (algorithm == DIGEST_MD5) {
+    md = EVP_md5();
+  } else if (algorithm == DIGEST_SHA_1) {
+    md = EVP_sha1();
+  } else if (algorithm == DIGEST_SHA_224) {
+    md = EVP_sha224();
+  } else if (algorithm == DIGEST_SHA_256) {
+    md = EVP_sha256();
+  } else if (algorithm == DIGEST_SHA_384) {
+    md = EVP_sha384();
+  } else if (algorithm == DIGEST_SHA_512) {
+    md = EVP_sha512();
+  } else {
+    return false;
+  }
+
+  // Can't happen
+  RTC_DCHECK(EVP_MD_size(md) >= 16);
+  *mdp = md;
+  return true;
+}
+
+bool OpenSSLDigest::GetDigestName(const EVP_MD* md,
+                                  std::string* algorithm) {
+  RTC_DCHECK(md != nullptr);
+  RTC_DCHECK(algorithm != nullptr);
+
+  int md_type = EVP_MD_type(md);
+  if (md_type == NID_md5) {
+    *algorithm = DIGEST_MD5;
+  } else if (md_type == NID_sha1) {
+    *algorithm = DIGEST_SHA_1;
+  } else if (md_type == NID_sha224) {
+    *algorithm = DIGEST_SHA_224;
+  } else if (md_type == NID_sha256) {
+    *algorithm = DIGEST_SHA_256;
+  } else if (md_type == NID_sha384) {
+    *algorithm = DIGEST_SHA_384;
+  } else if (md_type == NID_sha512) {
+    *algorithm = DIGEST_SHA_512;
+  } else {
+    algorithm->clear();
+    return false;
+  }
+
+  return true;
+}
+
+bool OpenSSLDigest::GetDigestSize(const std::string& algorithm,
+                                  size_t* length) {
+  const EVP_MD *md;
+  if (!GetDigestEVP(algorithm, &md))
+    return false;
+
+  *length = EVP_MD_size(md);
+  return true;
+}
+
+}  // namespace rtc
diff --git a/base/openssldigest.h b/base/openssldigest.h
index 031c0b1..413df45 100644
--- a/base/openssldigest.h
+++ b/base/openssldigest.h
@@ -11,9 +11,40 @@
 #ifndef WEBRTC_BASE_OPENSSLDIGEST_H_
 #define WEBRTC_BASE_OPENSSLDIGEST_H_
 
+#include <openssl/evp.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/openssldigest.h"
+#include "webrtc/base/messagedigest.h"
+
+namespace rtc {
+
+// An implementation of the digest class that uses OpenSSL.
+class OpenSSLDigest : public MessageDigest {
+ public:
+  // Creates an OpenSSLDigest with |algorithm| as the hash algorithm.
+  explicit OpenSSLDigest(const std::string& algorithm);
+  ~OpenSSLDigest() override;
+  // Returns the digest output size (e.g. 16 bytes for MD5).
+  size_t Size() const override;
+  // Updates the digest with |len| bytes from |buf|.
+  void Update(const void* buf, size_t len) override;
+  // Outputs the digest value to |buf| with length |len|.
+  size_t Finish(void* buf, size_t len) override;
+
+  // Helper function to look up a digest's EVP by name.
+  static bool GetDigestEVP(const std::string &algorithm,
+                           const EVP_MD** md);
+  // Helper function to look up a digest's name by EVP.
+  static bool GetDigestName(const EVP_MD* md,
+                            std::string* algorithm);
+  // Helper function to get the length of a digest.
+  static bool GetDigestSize(const std::string &algorithm,
+                            size_t* len);
+
+ private:
+  EVP_MD_CTX ctx_;
+  const EVP_MD* md_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_OPENSSLDIGEST_H_
diff --git a/base/opensslidentity.cc b/base/opensslidentity.cc
new file mode 100644
index 0000000..9495496
--- /dev/null
+++ b/base/opensslidentity.cc
@@ -0,0 +1,576 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/opensslidentity.h"
+
+#include <memory>
+
+// Must be included first before openssl headers.
+#include "webrtc/base/win32.h"  // NOLINT
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/crypto.h>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/openssl.h"
+#include "webrtc/base/openssldigest.h"
+
+namespace rtc {
+
+// We could have exposed a myriad of parameters for the crypto stuff,
+// but keeping it simple seems best.
+
+// Random bits for certificate serial number
+static const int SERIAL_RAND_BITS = 64;
+
+// Generate a key pair. Caller is responsible for freeing the returned object.
+static EVP_PKEY* MakeKey(const KeyParams& key_params) {
+  LOG(LS_INFO) << "Making key pair";
+  EVP_PKEY* pkey = EVP_PKEY_new();
+  if (key_params.type() == KT_RSA) {
+    int key_length = key_params.rsa_params().mod_size;
+    BIGNUM* exponent = BN_new();
+    RSA* rsa = RSA_new();
+    if (!pkey || !exponent || !rsa ||
+        !BN_set_word(exponent, key_params.rsa_params().pub_exp) ||
+        !RSA_generate_key_ex(rsa, key_length, exponent, nullptr) ||
+        !EVP_PKEY_assign_RSA(pkey, rsa)) {
+      EVP_PKEY_free(pkey);
+      BN_free(exponent);
+      RSA_free(rsa);
+      LOG(LS_ERROR) << "Failed to make RSA key pair";
+      return nullptr;
+    }
+    // ownership of rsa struct was assigned, don't free it.
+    BN_free(exponent);
+  } else if (key_params.type() == KT_ECDSA) {
+    if (key_params.ec_curve() == EC_NIST_P256) {
+      EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+
+      // Ensure curve name is included when EC key is serialized.
+      // Without this call, OpenSSL versions before 1.1.0 will create
+      // certificates that don't work for TLS.
+      // This is a no-op for BoringSSL and OpenSSL 1.1.0+
+      EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);
+
+      if (!pkey || !ec_key || !EC_KEY_generate_key(ec_key) ||
+          !EVP_PKEY_assign_EC_KEY(pkey, ec_key)) {
+        EVP_PKEY_free(pkey);
+        EC_KEY_free(ec_key);
+        LOG(LS_ERROR) << "Failed to make EC key pair";
+        return nullptr;
+      }
+      // ownership of ec_key struct was assigned, don't free it.
+    } else {
+      // Add generation of any other curves here.
+      EVP_PKEY_free(pkey);
+      LOG(LS_ERROR) << "ECDSA key requested for unknown curve";
+      return nullptr;
+    }
+  } else {
+    EVP_PKEY_free(pkey);
+    LOG(LS_ERROR) << "Key type requested not understood";
+    return nullptr;
+  }
+
+  LOG(LS_INFO) << "Returning key pair";
+  return pkey;
+}
+
+// Generate a self-signed certificate, with the public key from the
+// given key pair. Caller is responsible for freeing the returned object.
+static X509* MakeCertificate(EVP_PKEY* pkey, const SSLIdentityParams& params) {
+  LOG(LS_INFO) << "Making certificate for " << params.common_name;
+  X509* x509 = nullptr;
+  BIGNUM* serial_number = nullptr;
+  X509_NAME* name = nullptr;
+  time_t epoch_off = 0;  // Time offset since epoch.
+
+  if ((x509 = X509_new()) == nullptr)
+    goto error;
+
+  if (!X509_set_pubkey(x509, pkey))
+    goto error;
+
+  // serial number
+  // temporary reference to serial number inside x509 struct
+  ASN1_INTEGER* asn1_serial_number;
+  if ((serial_number = BN_new()) == nullptr ||
+      !BN_pseudo_rand(serial_number, SERIAL_RAND_BITS, 0, 0) ||
+      (asn1_serial_number = X509_get_serialNumber(x509)) == nullptr ||
+      !BN_to_ASN1_INTEGER(serial_number, asn1_serial_number))
+    goto error;
+
+  if (!X509_set_version(x509, 2L))  // version 3
+    goto error;
+
+  // There are a lot of possible components for the name entries. In
+  // our P2P SSL mode however, the certificates are pre-exchanged
+  // (through the secure XMPP channel), and so the certificate
+  // identification is arbitrary. It can't be empty, so we set some
+  // arbitrary common_name. Note that this certificate goes out in
+  // clear during SSL negotiation, so there may be a privacy issue in
+  // putting anything recognizable here.
+  if ((name = X509_NAME_new()) == nullptr ||
+      !X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_UTF8,
+                                  (unsigned char*)params.common_name.c_str(),
+                                  -1, -1, 0) ||
+      !X509_set_subject_name(x509, name) || !X509_set_issuer_name(x509, name))
+    goto error;
+
+  if (!X509_time_adj(X509_get_notBefore(x509), params.not_before, &epoch_off) ||
+      !X509_time_adj(X509_get_notAfter(x509), params.not_after, &epoch_off))
+    goto error;
+
+  if (!X509_sign(x509, pkey, EVP_sha256()))
+    goto error;
+
+  BN_free(serial_number);
+  X509_NAME_free(name);
+  LOG(LS_INFO) << "Returning certificate";
+  return x509;
+
+ error:
+  BN_free(serial_number);
+  X509_NAME_free(name);
+  X509_free(x509);
+  return nullptr;
+}
+
+// This dumps the SSL error stack to the log.
+static void LogSSLErrors(const std::string& prefix) {
+  char error_buf[200];
+  unsigned long err;
+
+  while ((err = ERR_get_error()) != 0) {
+    ERR_error_string_n(err, error_buf, sizeof(error_buf));
+    LOG(LS_ERROR) << prefix << ": " << error_buf << "\n";
+  }
+}
+
+OpenSSLKeyPair* OpenSSLKeyPair::Generate(const KeyParams& key_params) {
+  EVP_PKEY* pkey = MakeKey(key_params);
+  if (!pkey) {
+    LogSSLErrors("Generating key pair");
+    return nullptr;
+  }
+  return new OpenSSLKeyPair(pkey);
+}
+
+OpenSSLKeyPair* OpenSSLKeyPair::FromPrivateKeyPEMString(
+    const std::string& pem_string) {
+  BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
+  if (!bio) {
+    LOG(LS_ERROR) << "Failed to create a new BIO buffer.";
+    return nullptr;
+  }
+  BIO_set_mem_eof_return(bio, 0);
+  EVP_PKEY* pkey =
+      PEM_read_bio_PrivateKey(bio, nullptr, nullptr, const_cast<char*>("\0"));
+  BIO_free(bio);  // Frees the BIO, but not the pointed-to string.
+  if (!pkey) {
+    LOG(LS_ERROR) << "Failed to create the private key from PEM string.";
+    return nullptr;
+  }
+  if (EVP_PKEY_missing_parameters(pkey) != 0) {
+    LOG(LS_ERROR) << "The resulting key pair is missing public key parameters.";
+    EVP_PKEY_free(pkey);
+    return nullptr;
+  }
+  return new OpenSSLKeyPair(pkey);
+}
+
+OpenSSLKeyPair::~OpenSSLKeyPair() {
+  EVP_PKEY_free(pkey_);
+}
+
+OpenSSLKeyPair* OpenSSLKeyPair::GetReference() {
+  AddReference();
+  return new OpenSSLKeyPair(pkey_);
+}
+
+void OpenSSLKeyPair::AddReference() {
+#if defined(OPENSSL_IS_BORINGSSL)
+  EVP_PKEY_up_ref(pkey_);
+#else
+  CRYPTO_add(&pkey_->references, 1, CRYPTO_LOCK_EVP_PKEY);
+#endif
+}
+
+std::string OpenSSLKeyPair::PrivateKeyToPEMString() const {
+  BIO* temp_memory_bio = BIO_new(BIO_s_mem());
+  if (!temp_memory_bio) {
+    LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
+    RTC_NOTREACHED();
+    return "";
+  }
+  if (!PEM_write_bio_PrivateKey(
+      temp_memory_bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr)) {
+    LOG_F(LS_ERROR) << "Failed to write private key";
+    BIO_free(temp_memory_bio);
+    RTC_NOTREACHED();
+    return "";
+  }
+  BIO_write(temp_memory_bio, "\0", 1);
+  char* buffer;
+  BIO_get_mem_data(temp_memory_bio, &buffer);
+  std::string priv_key_str = buffer;
+  BIO_free(temp_memory_bio);
+  return priv_key_str;
+}
+
+std::string OpenSSLKeyPair::PublicKeyToPEMString() const {
+  BIO* temp_memory_bio = BIO_new(BIO_s_mem());
+  if (!temp_memory_bio) {
+    LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
+    RTC_NOTREACHED();
+    return "";
+  }
+  if (!PEM_write_bio_PUBKEY(temp_memory_bio, pkey_)) {
+    LOG_F(LS_ERROR) << "Failed to write public key";
+    BIO_free(temp_memory_bio);
+    RTC_NOTREACHED();
+    return "";
+  }
+  BIO_write(temp_memory_bio, "\0", 1);
+  char* buffer;
+  BIO_get_mem_data(temp_memory_bio, &buffer);
+  std::string pub_key_str = buffer;
+  BIO_free(temp_memory_bio);
+  return pub_key_str;
+}
+
+bool OpenSSLKeyPair::operator==(const OpenSSLKeyPair& other) const {
+  return EVP_PKEY_cmp(this->pkey_, other.pkey_) == 1;
+}
+
+bool OpenSSLKeyPair::operator!=(const OpenSSLKeyPair& other) const {
+  return !(*this == other);
+}
+
+#if !defined(NDEBUG)
+// Print a certificate to the log, for debugging.
+static void PrintCert(X509* x509) {
+  BIO* temp_memory_bio = BIO_new(BIO_s_mem());
+  if (!temp_memory_bio) {
+    LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
+    return;
+  }
+  X509_print_ex(temp_memory_bio, x509, XN_FLAG_SEP_CPLUS_SPC, 0);
+  BIO_write(temp_memory_bio, "\0", 1);
+  char* buffer;
+  BIO_get_mem_data(temp_memory_bio, &buffer);
+  LOG(LS_VERBOSE) << buffer;
+  BIO_free(temp_memory_bio);
+}
+#endif
+
+OpenSSLCertificate* OpenSSLCertificate::Generate(
+    OpenSSLKeyPair* key_pair, const SSLIdentityParams& params) {
+  SSLIdentityParams actual_params(params);
+  if (actual_params.common_name.empty()) {
+    // Use a random string, arbitrarily 8chars long.
+    actual_params.common_name = CreateRandomString(8);
+  }
+  X509* x509 = MakeCertificate(key_pair->pkey(), actual_params);
+  if (!x509) {
+    LogSSLErrors("Generating certificate");
+    return nullptr;
+  }
+#if !defined(NDEBUG)
+  PrintCert(x509);
+#endif
+  OpenSSLCertificate* ret = new OpenSSLCertificate(x509);
+  X509_free(x509);
+  return ret;
+}
+
+OpenSSLCertificate* OpenSSLCertificate::FromPEMString(
+    const std::string& pem_string) {
+  BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
+  if (!bio)
+    return nullptr;
+  BIO_set_mem_eof_return(bio, 0);
+  X509* x509 =
+      PEM_read_bio_X509(bio, nullptr, nullptr, const_cast<char*>("\0"));
+  BIO_free(bio);  // Frees the BIO, but not the pointed-to string.
+
+  if (!x509)
+    return nullptr;
+
+  OpenSSLCertificate* ret = new OpenSSLCertificate(x509);
+  X509_free(x509);
+  return ret;
+}
+
+// NOTE: This implementation only functions correctly after InitializeSSL
+// and before CleanupSSL.
+bool OpenSSLCertificate::GetSignatureDigestAlgorithm(
+    std::string* algorithm) const {
+  int nid = OBJ_obj2nid(x509_->sig_alg->algorithm);
+  switch (nid) {
+    case NID_md5WithRSA:
+    case NID_md5WithRSAEncryption:
+      *algorithm = DIGEST_MD5;
+      break;
+    case NID_ecdsa_with_SHA1:
+    case NID_dsaWithSHA1:
+    case NID_dsaWithSHA1_2:
+    case NID_sha1WithRSA:
+    case NID_sha1WithRSAEncryption:
+      *algorithm = DIGEST_SHA_1;
+      break;
+    case NID_ecdsa_with_SHA224:
+    case NID_sha224WithRSAEncryption:
+    case NID_dsa_with_SHA224:
+      *algorithm = DIGEST_SHA_224;
+      break;
+    case NID_ecdsa_with_SHA256:
+    case NID_sha256WithRSAEncryption:
+    case NID_dsa_with_SHA256:
+      *algorithm = DIGEST_SHA_256;
+      break;
+    case NID_ecdsa_with_SHA384:
+    case NID_sha384WithRSAEncryption:
+      *algorithm = DIGEST_SHA_384;
+      break;
+    case NID_ecdsa_with_SHA512:
+    case NID_sha512WithRSAEncryption:
+      *algorithm = DIGEST_SHA_512;
+      break;
+    default:
+      // Unknown algorithm.  There are several unhandled options that are less
+      // common and more complex.
+      LOG(LS_ERROR) << "Unknown signature algorithm NID: " << nid;
+      algorithm->clear();
+      return false;
+  }
+  return true;
+}
+
+std::unique_ptr<SSLCertChain> OpenSSLCertificate::GetChain() const {
+  // Chains are not yet supported when using OpenSSL.
+  // OpenSSLStreamAdapter::SSLVerifyCallback currently requires the remote
+  // certificate to be self-signed.
+  return nullptr;
+}
+
+bool OpenSSLCertificate::ComputeDigest(const std::string& algorithm,
+                                       unsigned char* digest,
+                                       size_t size,
+                                       size_t* length) const {
+  return ComputeDigest(x509_, algorithm, digest, size, length);
+}
+
+bool OpenSSLCertificate::ComputeDigest(const X509* x509,
+                                       const std::string& algorithm,
+                                       unsigned char* digest,
+                                       size_t size,
+                                       size_t* length) {
+  const EVP_MD* md;
+  unsigned int n;
+
+  if (!OpenSSLDigest::GetDigestEVP(algorithm, &md))
+    return false;
+
+  if (size < static_cast<size_t>(EVP_MD_size(md)))
+    return false;
+
+  X509_digest(x509, md, digest, &n);
+
+  *length = n;
+
+  return true;
+}
+
+OpenSSLCertificate::~OpenSSLCertificate() {
+  X509_free(x509_);
+}
+
+OpenSSLCertificate* OpenSSLCertificate::GetReference() const {
+  return new OpenSSLCertificate(x509_);
+}
+
+std::string OpenSSLCertificate::ToPEMString() const {
+  BIO* bio = BIO_new(BIO_s_mem());
+  if (!bio) {
+    FATAL() << "unreachable code";
+  }
+  if (!PEM_write_bio_X509(bio, x509_)) {
+    BIO_free(bio);
+    FATAL() << "unreachable code";
+  }
+  BIO_write(bio, "\0", 1);
+  char* buffer;
+  BIO_get_mem_data(bio, &buffer);
+  std::string ret(buffer);
+  BIO_free(bio);
+  return ret;
+}
+
+void OpenSSLCertificate::ToDER(Buffer* der_buffer) const {
+  // In case of failure, make sure to leave the buffer empty.
+  der_buffer->SetSize(0);
+
+  // Calculates the DER representation of the certificate, from scratch.
+  BIO* bio = BIO_new(BIO_s_mem());
+  if (!bio) {
+    FATAL() << "unreachable code";
+  }
+  if (!i2d_X509_bio(bio, x509_)) {
+    BIO_free(bio);
+    FATAL() << "unreachable code";
+  }
+  char* data;
+  size_t length = BIO_get_mem_data(bio, &data);
+  der_buffer->SetData(data, length);
+  BIO_free(bio);
+}
+
+void OpenSSLCertificate::AddReference() const {
+  RTC_DCHECK(x509_ != nullptr);
+#if defined(OPENSSL_IS_BORINGSSL)
+  X509_up_ref(x509_);
+#else
+  CRYPTO_add(&x509_->references, 1, CRYPTO_LOCK_X509);
+#endif
+}
+
+bool OpenSSLCertificate::operator==(const OpenSSLCertificate& other) const {
+  return X509_cmp(this->x509_, other.x509_) == 0;
+}
+
+bool OpenSSLCertificate::operator!=(const OpenSSLCertificate& other) const {
+  return !(*this == other);
+}
+
+// Documented in sslidentity.h.
+int64_t OpenSSLCertificate::CertificateExpirationTime() const {
+  ASN1_TIME* expire_time = X509_get_notAfter(x509_);
+  bool long_format;
+
+  if (expire_time->type == V_ASN1_UTCTIME) {
+    long_format = false;
+  } else if (expire_time->type == V_ASN1_GENERALIZEDTIME) {
+    long_format = true;
+  } else {
+    return -1;
+  }
+
+  return ASN1TimeToSec(expire_time->data, expire_time->length, long_format);
+}
+
+OpenSSLIdentity::OpenSSLIdentity(OpenSSLKeyPair* key_pair,
+                                 OpenSSLCertificate* certificate)
+    : key_pair_(key_pair), certificate_(certificate) {
+  RTC_DCHECK(key_pair != nullptr);
+  RTC_DCHECK(certificate != nullptr);
+}
+
+OpenSSLIdentity::~OpenSSLIdentity() = default;
+
+OpenSSLIdentity* OpenSSLIdentity::GenerateInternal(
+    const SSLIdentityParams& params) {
+  OpenSSLKeyPair* key_pair = OpenSSLKeyPair::Generate(params.key_params);
+  if (key_pair) {
+    OpenSSLCertificate* certificate =
+        OpenSSLCertificate::Generate(key_pair, params);
+    if (certificate)
+      return new OpenSSLIdentity(key_pair, certificate);
+    delete key_pair;
+  }
+  LOG(LS_INFO) << "Identity generation failed";
+  return nullptr;
+}
+
+OpenSSLIdentity* OpenSSLIdentity::GenerateWithExpiration(
+    const std::string& common_name,
+    const KeyParams& key_params,
+    time_t certificate_lifetime) {
+  SSLIdentityParams params;
+  params.key_params = key_params;
+  params.common_name = common_name;
+  time_t now = time(nullptr);
+  params.not_before = now + kCertificateWindowInSeconds;
+  params.not_after = now + certificate_lifetime;
+  if (params.not_before > params.not_after)
+    return nullptr;
+  return GenerateInternal(params);
+}
+
+OpenSSLIdentity* OpenSSLIdentity::GenerateForTest(
+    const SSLIdentityParams& params) {
+  return GenerateInternal(params);
+}
+
+SSLIdentity* OpenSSLIdentity::FromPEMStrings(
+    const std::string& private_key,
+    const std::string& certificate) {
+  std::unique_ptr<OpenSSLCertificate> cert(
+      OpenSSLCertificate::FromPEMString(certificate));
+  if (!cert) {
+    LOG(LS_ERROR) << "Failed to create OpenSSLCertificate from PEM string.";
+    return nullptr;
+  }
+
+  OpenSSLKeyPair* key_pair =
+      OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
+  if (!key_pair) {
+    LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
+    return nullptr;
+  }
+
+  return new OpenSSLIdentity(key_pair,
+                             cert.release());
+}
+
+const OpenSSLCertificate& OpenSSLIdentity::certificate() const {
+  return *certificate_;
+}
+
+OpenSSLIdentity* OpenSSLIdentity::GetReference() const {
+  return new OpenSSLIdentity(key_pair_->GetReference(),
+                             certificate_->GetReference());
+}
+
+bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
+  // 1 is the documented success return code.
+  if (SSL_CTX_use_certificate(ctx, certificate_->x509()) != 1 ||
+     SSL_CTX_use_PrivateKey(ctx, key_pair_->pkey()) != 1) {
+    LogSSLErrors("Configuring key and certificate");
+    return false;
+  }
+  return true;
+}
+
+std::string OpenSSLIdentity::PrivateKeyToPEMString() const {
+  return key_pair_->PrivateKeyToPEMString();
+}
+
+std::string OpenSSLIdentity::PublicKeyToPEMString() const {
+  return key_pair_->PublicKeyToPEMString();
+}
+
+bool OpenSSLIdentity::operator==(const OpenSSLIdentity& other) const {
+  return *this->key_pair_ == *other.key_pair_ &&
+         *this->certificate_ == *other.certificate_;
+}
+
+bool OpenSSLIdentity::operator!=(const OpenSSLIdentity& other) const {
+  return !(*this == other);
+}
+
+}  // namespace rtc
diff --git a/base/opensslidentity.h b/base/opensslidentity.h
index 59fa571..84716d1 100644
--- a/base/opensslidentity.h
+++ b/base/opensslidentity.h
@@ -11,9 +11,137 @@
 #ifndef WEBRTC_BASE_OPENSSLIDENTITY_H_
 #define WEBRTC_BASE_OPENSSLIDENTITY_H_
 
+#include <openssl/evp.h>
+#include <openssl/x509.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/opensslidentity.h"
+#include <memory>
+#include <string>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/sslidentity.h"
+
+typedef struct ssl_ctx_st SSL_CTX;
+
+namespace rtc {
+
+// OpenSSLKeyPair encapsulates an OpenSSL EVP_PKEY* keypair object,
+// which is reference counted inside the OpenSSL library.
+class OpenSSLKeyPair {
+ public:
+  explicit OpenSSLKeyPair(EVP_PKEY* pkey) : pkey_(pkey) {
+    RTC_DCHECK(pkey_ != nullptr);
+  }
+
+  static OpenSSLKeyPair* Generate(const KeyParams& key_params);
+  // Constructs a key pair from the private key PEM string. This must not result
+  // in missing public key parameters. Returns null on error.
+  static OpenSSLKeyPair* FromPrivateKeyPEMString(
+      const std::string& pem_string);
+
+  virtual ~OpenSSLKeyPair();
+
+  virtual OpenSSLKeyPair* GetReference();
+
+  EVP_PKEY* pkey() const { return pkey_; }
+  std::string PrivateKeyToPEMString() const;
+  std::string PublicKeyToPEMString() const;
+  bool operator==(const OpenSSLKeyPair& other) const;
+  bool operator!=(const OpenSSLKeyPair& other) const;
+
+ private:
+  void AddReference();
+
+  EVP_PKEY* pkey_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLKeyPair);
+};
+
+// OpenSSLCertificate encapsulates an OpenSSL X509* certificate object,
+// which is also reference counted inside the OpenSSL library.
+class OpenSSLCertificate : public SSLCertificate {
+ public:
+  // Caller retains ownership of the X509 object.
+  explicit OpenSSLCertificate(X509* x509) : x509_(x509) {
+    AddReference();
+  }
+
+  static OpenSSLCertificate* Generate(OpenSSLKeyPair* key_pair,
+                                      const SSLIdentityParams& params);
+  static OpenSSLCertificate* FromPEMString(const std::string& pem_string);
+
+  ~OpenSSLCertificate() override;
+
+  OpenSSLCertificate* GetReference() const override;
+
+  X509* x509() const { return x509_; }
+
+  std::string ToPEMString() const override;
+  void ToDER(Buffer* der_buffer) const override;
+  bool operator==(const OpenSSLCertificate& other) const;
+  bool operator!=(const OpenSSLCertificate& other) const;
+
+  // Compute the digest of the certificate given algorithm
+  bool ComputeDigest(const std::string& algorithm,
+                     unsigned char* digest,
+                     size_t size,
+                     size_t* length) const override;
+
+  // Compute the digest of a certificate as an X509 *
+  static bool ComputeDigest(const X509* x509,
+                            const std::string& algorithm,
+                            unsigned char* digest,
+                            size_t size,
+                            size_t* length);
+
+  bool GetSignatureDigestAlgorithm(std::string* algorithm) const override;
+  std::unique_ptr<SSLCertChain> GetChain() const override;
+
+  int64_t CertificateExpirationTime() const override;
+
+ private:
+  void AddReference() const;
+
+  X509* x509_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLCertificate);
+};
+
+// Holds a keypair and certificate together, and a method to generate
+// them consistently.
+class OpenSSLIdentity : public SSLIdentity {
+ public:
+  static OpenSSLIdentity* GenerateWithExpiration(const std::string& common_name,
+                                                 const KeyParams& key_params,
+                                                 time_t certificate_lifetime);
+  static OpenSSLIdentity* GenerateForTest(const SSLIdentityParams& params);
+  static SSLIdentity* FromPEMStrings(const std::string& private_key,
+                                     const std::string& certificate);
+  ~OpenSSLIdentity() override;
+
+  const OpenSSLCertificate& certificate() const override;
+  OpenSSLIdentity* GetReference() const override;
+
+  // Configure an SSL context object to use our key and certificate.
+  bool ConfigureIdentity(SSL_CTX* ctx);
+
+  std::string PrivateKeyToPEMString() const override;
+  std::string PublicKeyToPEMString() const override;
+  bool operator==(const OpenSSLIdentity& other) const;
+  bool operator!=(const OpenSSLIdentity& other) const;
+
+ private:
+  OpenSSLIdentity(OpenSSLKeyPair* key_pair, OpenSSLCertificate* certificate);
+
+  static OpenSSLIdentity* GenerateInternal(const SSLIdentityParams& params);
+
+  std::unique_ptr<OpenSSLKeyPair> key_pair_;
+  std::unique_ptr<OpenSSLCertificate> certificate_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLIdentity);
+};
+
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_OPENSSLIDENTITY_H_
diff --git a/base/opensslstreamadapter.cc b/base/opensslstreamadapter.cc
new file mode 100644
index 0000000..d1c16a1
--- /dev/null
+++ b/base/opensslstreamadapter.cc
@@ -0,0 +1,1219 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/opensslstreamadapter.h"
+
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/tls1.h>
+#include <openssl/x509v3.h>
+#ifndef OPENSSL_IS_BORINGSSL
+#include <openssl/dtls1.h>
+#include <openssl/ssl.h>
+#endif
+
+#include <memory>
+#include <vector>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/safe_conversions.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/openssl.h"
+#include "webrtc/base/openssladapter.h"
+#include "webrtc/base/openssldigest.h"
+#include "webrtc/base/opensslidentity.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/base/thread.h"
+
+namespace {
+  bool g_use_time_callback_for_testing = false;
+}
+
+namespace rtc {
+
+#if (OPENSSL_VERSION_NUMBER < 0x10001000L)
+#error "webrtc requires at least OpenSSL version 1.0.1, to support DTLS-SRTP"
+#endif
+
+// SRTP cipher suite table. |internal_name| is used to construct a
+// colon-separated profile strings which is needed by
+// SSL_CTX_set_tlsext_use_srtp().
+struct SrtpCipherMapEntry {
+  const char* internal_name;
+  const int id;
+};
+
+// This isn't elegant, but it's better than an external reference
+static SrtpCipherMapEntry SrtpCipherMap[] = {
+    {"SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80},
+    {"SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32},
+    {"SRTP_AEAD_AES_128_GCM", SRTP_AEAD_AES_128_GCM},
+    {"SRTP_AEAD_AES_256_GCM", SRTP_AEAD_AES_256_GCM},
+    {nullptr, 0}};
+
+#ifdef OPENSSL_IS_BORINGSSL
+// Not used in production code. Actual time should be relative to Jan 1, 1970.
+static void TimeCallbackForTesting(const SSL* ssl, struct timeval* out_clock) {
+  int64_t time = TimeNanos();
+  out_clock->tv_sec = time / kNumNanosecsPerSec;
+  out_clock->tv_usec = (time % kNumNanosecsPerSec) / kNumNanosecsPerMicrosec;
+}
+#else  // #ifdef OPENSSL_IS_BORINGSSL
+
+// Cipher name table. Maps internal OpenSSL cipher ids to the RFC name.
+struct SslCipherMapEntry {
+  uint32_t openssl_id;
+  const char* rfc_name;
+};
+
+#define DEFINE_CIPHER_ENTRY_SSL3(name)  {SSL3_CK_##name, "TLS_"#name}
+#define DEFINE_CIPHER_ENTRY_TLS1(name)  {TLS1_CK_##name, "TLS_"#name}
+
+// There currently is no method available to get a RFC-compliant name for a
+// cipher suite from BoringSSL, so we need to define the mapping manually here.
+// This should go away once BoringSSL supports "SSL_CIPHER_standard_name"
+// (as available in OpenSSL if compiled with tracing enabled) or a similar
+// method.
+static const SslCipherMapEntry kSslCipherMap[] = {
+    // TLS v1.0 ciphersuites from RFC2246.
+    DEFINE_CIPHER_ENTRY_SSL3(RSA_RC4_128_SHA),
+    {SSL3_CK_RSA_DES_192_CBC3_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
+
+    // AES ciphersuites from RFC3268.
+    {TLS1_CK_RSA_WITH_AES_128_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"},
+    {TLS1_CK_DHE_RSA_WITH_AES_128_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"},
+    {TLS1_CK_RSA_WITH_AES_256_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"},
+    {TLS1_CK_DHE_RSA_WITH_AES_256_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"},
+
+    // ECC ciphersuites from RFC4492.
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_RC4_128_SHA),
+    {TLS1_CK_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA,
+     "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"},
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_128_CBC_SHA),
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
+
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_RC4_128_SHA),
+    {TLS1_CK_ECDHE_RSA_WITH_DES_192_CBC3_SHA,
+     "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"},
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_128_CBC_SHA),
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_256_CBC_SHA),
+
+    // TLS v1.2 ciphersuites.
+    {TLS1_CK_RSA_WITH_AES_128_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"},
+    {TLS1_CK_RSA_WITH_AES_256_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"},
+    {TLS1_CK_DHE_RSA_WITH_AES_128_SHA256,
+     "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"},
+    {TLS1_CK_DHE_RSA_WITH_AES_256_SHA256,
+     "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"},
+
+    // TLS v1.2 GCM ciphersuites from RFC5288.
+    DEFINE_CIPHER_ENTRY_TLS1(RSA_WITH_AES_128_GCM_SHA256),
+    DEFINE_CIPHER_ENTRY_TLS1(RSA_WITH_AES_256_GCM_SHA384),
+    DEFINE_CIPHER_ENTRY_TLS1(DHE_RSA_WITH_AES_128_GCM_SHA256),
+    DEFINE_CIPHER_ENTRY_TLS1(DHE_RSA_WITH_AES_256_GCM_SHA384),
+    DEFINE_CIPHER_ENTRY_TLS1(DH_RSA_WITH_AES_128_GCM_SHA256),
+    DEFINE_CIPHER_ENTRY_TLS1(DH_RSA_WITH_AES_256_GCM_SHA384),
+
+    // ECDH HMAC based ciphersuites from RFC5289.
+    {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256,
+     "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"},
+    {TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384,
+     "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"},
+    {TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
+     "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"},
+    {TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
+     "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"},
+
+    // ECDH GCM based ciphersuites from RFC5289.
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_128_GCM_SHA256),
+    DEFINE_CIPHER_ENTRY_TLS1(ECDHE_RSA_WITH_AES_256_GCM_SHA384),
+
+    {0, nullptr}};
+#endif  // #ifndef OPENSSL_IS_BORINGSSL
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4309)
+#pragma warning(disable : 4310)
+#endif  // defined(_MSC_VER)
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif  // defined(_MSC_VER)
+
+//////////////////////////////////////////////////////////////////////
+// StreamBIO
+//////////////////////////////////////////////////////////////////////
+
+static int stream_write(BIO* h, const char* buf, int num);
+static int stream_read(BIO* h, char* buf, int size);
+static int stream_puts(BIO* h, const char* str);
+static long stream_ctrl(BIO* h, int cmd, long arg1, void* arg2);
+static int stream_new(BIO* h);
+static int stream_free(BIO* data);
+
+// TODO(davidben): This should be const once BoringSSL is assumed.
+static BIO_METHOD methods_stream = {
+    BIO_TYPE_BIO, "stream",   stream_write, stream_read, stream_puts, 0,
+    stream_ctrl,  stream_new, stream_free,  nullptr,
+};
+
+static BIO_METHOD* BIO_s_stream() { return(&methods_stream); }
+
+static BIO* BIO_new_stream(StreamInterface* stream) {
+  BIO* ret = BIO_new(BIO_s_stream());
+  if (ret == nullptr)
+    return nullptr;
+  ret->ptr = stream;
+  return ret;
+}
+
+// bio methods return 1 (or at least non-zero) on success and 0 on failure.
+
+static int stream_new(BIO* b) {
+  b->shutdown = 0;
+  b->init = 1;
+  b->num = 0;  // 1 means end-of-stream
+  b->ptr = 0;
+  return 1;
+}
+
+static int stream_free(BIO* b) {
+  if (b == nullptr)
+    return 0;
+  return 1;
+}
+
+static int stream_read(BIO* b, char* out, int outl) {
+  if (!out)
+    return -1;
+  StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
+  BIO_clear_retry_flags(b);
+  size_t read;
+  int error;
+  StreamResult result = stream->Read(out, outl, &read, &error);
+  if (result == SR_SUCCESS) {
+    return checked_cast<int>(read);
+  } else if (result == SR_EOS) {
+    b->num = 1;
+  } else if (result == SR_BLOCK) {
+    BIO_set_retry_read(b);
+  }
+  return -1;
+}
+
+static int stream_write(BIO* b, const char* in, int inl) {
+  if (!in)
+    return -1;
+  StreamInterface* stream = static_cast<StreamInterface*>(b->ptr);
+  BIO_clear_retry_flags(b);
+  size_t written;
+  int error;
+  StreamResult result = stream->Write(in, inl, &written, &error);
+  if (result == SR_SUCCESS) {
+    return checked_cast<int>(written);
+  } else if (result == SR_BLOCK) {
+    BIO_set_retry_write(b);
+  }
+  return -1;
+}
+
+static int stream_puts(BIO* b, const char* str) {
+  return stream_write(b, str, checked_cast<int>(strlen(str)));
+}
+
+static long stream_ctrl(BIO* b, int cmd, long num, void* ptr) {
+  switch (cmd) {
+    case BIO_CTRL_RESET:
+      return 0;
+    case BIO_CTRL_EOF:
+      return b->num;
+    case BIO_CTRL_WPENDING:
+    case BIO_CTRL_PENDING:
+      return 0;
+    case BIO_CTRL_FLUSH:
+      return 1;
+    case BIO_CTRL_DGRAM_QUERY_MTU:
+      // openssl defaults to mtu=256 unless we return something here.
+      // The handshake doesn't actually need to send packets above 1k,
+      // so this seems like a sensible value that should work in most cases.
+      // Webrtc uses the same value for video packets.
+      return 1200;
+    default:
+      return 0;
+  }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// OpenSSLStreamAdapter
+/////////////////////////////////////////////////////////////////////////////
+
+OpenSSLStreamAdapter::OpenSSLStreamAdapter(StreamInterface* stream)
+    : SSLStreamAdapter(stream),
+      state_(SSL_NONE),
+      role_(SSL_CLIENT),
+      ssl_read_needs_write_(false),
+      ssl_write_needs_read_(false),
+      ssl_(nullptr),
+      ssl_ctx_(nullptr),
+      ssl_mode_(SSL_MODE_TLS),
+      ssl_max_version_(SSL_PROTOCOL_TLS_12) {}
+
+OpenSSLStreamAdapter::~OpenSSLStreamAdapter() {
+  Cleanup(0);
+}
+
+void OpenSSLStreamAdapter::SetIdentity(SSLIdentity* identity) {
+  RTC_DCHECK(!identity_);
+  identity_.reset(static_cast<OpenSSLIdentity*>(identity));
+}
+
+void OpenSSLStreamAdapter::SetServerRole(SSLRole role) {
+  role_ = role;
+}
+
+std::unique_ptr<SSLCertificate> OpenSSLStreamAdapter::GetPeerCertificate()
+    const {
+  return peer_certificate_ ? std::unique_ptr<SSLCertificate>(
+                                 peer_certificate_->GetReference())
+                           : nullptr;
+}
+
+bool OpenSSLStreamAdapter::SetPeerCertificateDigest(
+    const std::string& digest_alg,
+    const unsigned char* digest_val,
+    size_t digest_len,
+    SSLPeerCertificateDigestError* error) {
+  RTC_DCHECK(!peer_certificate_verified_);
+  RTC_DCHECK(!has_peer_certificate_digest());
+  size_t expected_len;
+  if (error) {
+    *error = SSLPeerCertificateDigestError::NONE;
+  }
+
+  if (!OpenSSLDigest::GetDigestSize(digest_alg, &expected_len)) {
+    LOG(LS_WARNING) << "Unknown digest algorithm: " << digest_alg;
+    if (error) {
+      *error = SSLPeerCertificateDigestError::UNKNOWN_ALGORITHM;
+    }
+    return false;
+  }
+  if (expected_len != digest_len) {
+    if (error) {
+      *error = SSLPeerCertificateDigestError::INVALID_LENGTH;
+    }
+    return false;
+  }
+
+  peer_certificate_digest_value_.SetData(digest_val, digest_len);
+  peer_certificate_digest_algorithm_ = digest_alg;
+
+  if (!peer_certificate_) {
+    // Normal case, where the digest is set before we obtain the certificate
+    // from the handshake.
+    return true;
+  }
+
+  if (!VerifyPeerCertificate()) {
+    Error("SetPeerCertificateDigest", -1, SSL_AD_BAD_CERTIFICATE, false);
+    if (error) {
+      *error = SSLPeerCertificateDigestError::VERIFICATION_FAILED;
+    }
+    return false;
+  }
+
+  if (state_ == SSL_CONNECTED) {
+    // Post the event asynchronously to unwind the stack. The caller
+    // of ContinueSSL may be the same object listening for these
+    // events and may not be prepared for reentrancy.
+    PostEvent(SE_OPEN | SE_READ | SE_WRITE, 0);
+  }
+
+  return true;
+}
+
+std::string OpenSSLStreamAdapter::SslCipherSuiteToName(int cipher_suite) {
+#ifdef OPENSSL_IS_BORINGSSL
+  const SSL_CIPHER* ssl_cipher = SSL_get_cipher_by_value(cipher_suite);
+  if (!ssl_cipher) {
+    return std::string();
+  }
+  char* cipher_name = SSL_CIPHER_get_rfc_name(ssl_cipher);
+  std::string rfc_name = std::string(cipher_name);
+  OPENSSL_free(cipher_name);
+  return rfc_name;
+#else
+  for (const SslCipherMapEntry* entry = kSslCipherMap; entry->rfc_name;
+       ++entry) {
+    if (cipher_suite == static_cast<int>(entry->openssl_id)) {
+      return entry->rfc_name;
+    }
+  }
+  return std::string();
+#endif
+}
+
+bool OpenSSLStreamAdapter::GetSslCipherSuite(int* cipher_suite) {
+  if (state_ != SSL_CONNECTED)
+    return false;
+
+  const SSL_CIPHER* current_cipher = SSL_get_current_cipher(ssl_);
+  if (current_cipher == nullptr) {
+    return false;
+  }
+
+  *cipher_suite = static_cast<uint16_t>(SSL_CIPHER_get_id(current_cipher));
+  return true;
+}
+
+int OpenSSLStreamAdapter::GetSslVersion() const {
+  if (state_ != SSL_CONNECTED)
+    return -1;
+
+  int ssl_version = SSL_version(ssl_);
+  if (ssl_mode_ == SSL_MODE_DTLS) {
+    if (ssl_version == DTLS1_VERSION)
+      return SSL_PROTOCOL_DTLS_10;
+    else if (ssl_version == DTLS1_2_VERSION)
+      return SSL_PROTOCOL_DTLS_12;
+  } else {
+    if (ssl_version == TLS1_VERSION)
+      return SSL_PROTOCOL_TLS_10;
+    else if (ssl_version == TLS1_1_VERSION)
+      return SSL_PROTOCOL_TLS_11;
+    else if (ssl_version == TLS1_2_VERSION)
+      return SSL_PROTOCOL_TLS_12;
+  }
+
+  return -1;
+}
+
+// Key Extractor interface
+bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label,
+                                                const uint8_t* context,
+                                                size_t context_len,
+                                                bool use_context,
+                                                uint8_t* result,
+                                                size_t result_len) {
+  int i;
+
+  i = SSL_export_keying_material(ssl_, result, result_len, label.c_str(),
+                                 label.length(), const_cast<uint8_t*>(context),
+                                 context_len, use_context);
+
+  if (i != 1)
+    return false;
+
+  return true;
+}
+
+bool OpenSSLStreamAdapter::SetDtlsSrtpCryptoSuites(
+    const std::vector<int>& ciphers) {
+  std::string internal_ciphers;
+
+  if (state_ != SSL_NONE)
+    return false;
+
+  for (std::vector<int>::const_iterator cipher = ciphers.begin();
+       cipher != ciphers.end(); ++cipher) {
+    bool found = false;
+    for (SrtpCipherMapEntry* entry = SrtpCipherMap; entry->internal_name;
+         ++entry) {
+      if (*cipher == entry->id) {
+        found = true;
+        if (!internal_ciphers.empty())
+          internal_ciphers += ":";
+        internal_ciphers += entry->internal_name;
+        break;
+      }
+    }
+
+    if (!found) {
+      LOG(LS_ERROR) << "Could not find cipher: " << *cipher;
+      return false;
+    }
+  }
+
+  if (internal_ciphers.empty())
+    return false;
+
+  srtp_ciphers_ = internal_ciphers;
+  return true;
+}
+
+bool OpenSSLStreamAdapter::GetDtlsSrtpCryptoSuite(int* crypto_suite) {
+  RTC_DCHECK(state_ == SSL_CONNECTED);
+  if (state_ != SSL_CONNECTED)
+    return false;
+
+  const SRTP_PROTECTION_PROFILE *srtp_profile =
+      SSL_get_selected_srtp_profile(ssl_);
+
+  if (!srtp_profile)
+    return false;
+
+  *crypto_suite = srtp_profile->id;
+  RTC_DCHECK(!SrtpCryptoSuiteToName(*crypto_suite).empty());
+  return true;
+}
+
+bool OpenSSLStreamAdapter::IsTlsConnected() {
+  return state_ == SSL_CONNECTED;
+}
+
+int OpenSSLStreamAdapter::StartSSL() {
+  if (state_ != SSL_NONE) {
+    // Don't allow StartSSL to be called twice.
+    return -1;
+  }
+
+  if (StreamAdapterInterface::GetState() != SS_OPEN) {
+    state_ = SSL_WAIT;
+    return 0;
+  }
+
+  state_ = SSL_CONNECTING;
+  if (int err = BeginSSL()) {
+    Error("BeginSSL", err, 0, false);
+    return err;
+  }
+
+  return 0;
+}
+
+void OpenSSLStreamAdapter::SetMode(SSLMode mode) {
+  RTC_DCHECK(state_ == SSL_NONE);
+  ssl_mode_ = mode;
+}
+
+void OpenSSLStreamAdapter::SetMaxProtocolVersion(SSLProtocolVersion version) {
+  RTC_DCHECK(ssl_ctx_ == nullptr);
+  ssl_max_version_ = version;
+}
+
+void OpenSSLStreamAdapter::SetInitialRetransmissionTimeout(
+    int timeout_ms) {
+  RTC_DCHECK(ssl_ctx_ == nullptr);
+  dtls_handshake_timeout_ms_ = timeout_ms;
+}
+
+//
+// StreamInterface Implementation
+//
+
+StreamResult OpenSSLStreamAdapter::Write(const void* data, size_t data_len,
+                                         size_t* written, int* error) {
+  LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::Write(" << data_len << ")";
+
+  switch (state_) {
+  case SSL_NONE:
+    // pass-through in clear text
+    return StreamAdapterInterface::Write(data, data_len, written, error);
+
+  case SSL_WAIT:
+  case SSL_CONNECTING:
+    return SR_BLOCK;
+
+  case SSL_CONNECTED:
+    if (waiting_to_verify_peer_certificate()) {
+      return SR_BLOCK;
+    }
+    break;
+
+  case SSL_ERROR:
+  case SSL_CLOSED:
+  default:
+    if (error)
+      *error = ssl_error_code_;
+    return SR_ERROR;
+  }
+
+  // OpenSSL will return an error if we try to write zero bytes
+  if (data_len == 0) {
+    if (written)
+      *written = 0;
+    return SR_SUCCESS;
+  }
+
+  ssl_write_needs_read_ = false;
+
+  int code = SSL_write(ssl_, data, checked_cast<int>(data_len));
+  int ssl_error = SSL_get_error(ssl_, code);
+  switch (ssl_error) {
+  case SSL_ERROR_NONE:
+    LOG(LS_VERBOSE) << " -- success";
+    RTC_DCHECK(0 < code && static_cast<unsigned>(code) <= data_len);
+    if (written)
+      *written = code;
+    return SR_SUCCESS;
+  case SSL_ERROR_WANT_READ:
+    LOG(LS_VERBOSE) << " -- error want read";
+    ssl_write_needs_read_ = true;
+    return SR_BLOCK;
+  case SSL_ERROR_WANT_WRITE:
+    LOG(LS_VERBOSE) << " -- error want write";
+    return SR_BLOCK;
+
+  case SSL_ERROR_ZERO_RETURN:
+  default:
+    Error("SSL_write", (ssl_error ? ssl_error : -1), 0, false);
+    if (error)
+      *error = ssl_error_code_;
+    return SR_ERROR;
+  }
+  // not reached
+}
+
+StreamResult OpenSSLStreamAdapter::Read(void* data, size_t data_len,
+                                        size_t* read, int* error) {
+  LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::Read(" << data_len << ")";
+  switch (state_) {
+    case SSL_NONE:
+      // pass-through in clear text
+      return StreamAdapterInterface::Read(data, data_len, read, error);
+
+    case SSL_WAIT:
+    case SSL_CONNECTING:
+      return SR_BLOCK;
+
+    case SSL_CONNECTED:
+      if (waiting_to_verify_peer_certificate()) {
+        return SR_BLOCK;
+      }
+      break;
+
+    case SSL_CLOSED:
+      return SR_EOS;
+
+    case SSL_ERROR:
+    default:
+      if (error)
+        *error = ssl_error_code_;
+      return SR_ERROR;
+  }
+
+  // Don't trust OpenSSL with zero byte reads
+  if (data_len == 0) {
+    if (read)
+      *read = 0;
+    return SR_SUCCESS;
+  }
+
+  ssl_read_needs_write_ = false;
+
+  int code = SSL_read(ssl_, data, checked_cast<int>(data_len));
+  int ssl_error = SSL_get_error(ssl_, code);
+  switch (ssl_error) {
+    case SSL_ERROR_NONE:
+      LOG(LS_VERBOSE) << " -- success";
+      RTC_DCHECK(0 < code && static_cast<unsigned>(code) <= data_len);
+      if (read)
+        *read = code;
+
+      if (ssl_mode_ == SSL_MODE_DTLS) {
+        // Enforce atomic reads -- this is a short read
+        unsigned int pending = SSL_pending(ssl_);
+
+        if (pending) {
+          LOG(LS_INFO) << " -- short DTLS read. flushing";
+          FlushInput(pending);
+          if (error)
+            *error = SSE_MSG_TRUNC;
+          return SR_ERROR;
+        }
+      }
+      return SR_SUCCESS;
+    case SSL_ERROR_WANT_READ:
+      LOG(LS_VERBOSE) << " -- error want read";
+      return SR_BLOCK;
+    case SSL_ERROR_WANT_WRITE:
+      LOG(LS_VERBOSE) << " -- error want write";
+      ssl_read_needs_write_ = true;
+      return SR_BLOCK;
+    case SSL_ERROR_ZERO_RETURN:
+      LOG(LS_VERBOSE) << " -- remote side closed";
+      Close();
+      return SR_EOS;
+      break;
+    default:
+      LOG(LS_VERBOSE) << " -- error " << code;
+      Error("SSL_read", (ssl_error ? ssl_error : -1), 0, false);
+      if (error)
+        *error = ssl_error_code_;
+      return SR_ERROR;
+  }
+  // not reached
+}
+
+void OpenSSLStreamAdapter::FlushInput(unsigned int left) {
+  unsigned char buf[2048];
+
+  while (left) {
+    // This should always succeed
+    int toread = (sizeof(buf) < left) ? sizeof(buf) : left;
+    int code = SSL_read(ssl_, buf, toread);
+
+    int ssl_error = SSL_get_error(ssl_, code);
+    RTC_DCHECK(ssl_error == SSL_ERROR_NONE);
+
+    if (ssl_error != SSL_ERROR_NONE) {
+      LOG(LS_VERBOSE) << " -- error " << code;
+      Error("SSL_read", (ssl_error ? ssl_error : -1), 0, false);
+      return;
+    }
+
+    LOG(LS_VERBOSE) << " -- flushed " << code << " bytes";
+    left -= code;
+  }
+}
+
+void OpenSSLStreamAdapter::Close() {
+  Cleanup(0);
+  RTC_DCHECK(state_ == SSL_CLOSED || state_ == SSL_ERROR);
+  // When we're closed at SSL layer, also close the stream level which
+  // performs necessary clean up. Otherwise, a new incoming packet after
+  // this could overflow the stream buffer.
+  StreamAdapterInterface::Close();
+}
+
+StreamState OpenSSLStreamAdapter::GetState() const {
+  switch (state_) {
+    case SSL_WAIT:
+    case SSL_CONNECTING:
+      return SS_OPENING;
+    case SSL_CONNECTED:
+      if (waiting_to_verify_peer_certificate()) {
+        return SS_OPENING;
+      }
+      return SS_OPEN;
+    default:
+      return SS_CLOSED;
+  };
+  // not reached
+}
+
+void OpenSSLStreamAdapter::OnEvent(StreamInterface* stream, int events,
+                                   int err) {
+  int events_to_signal = 0;
+  int signal_error = 0;
+  RTC_DCHECK(stream == this->stream());
+  if ((events & SE_OPEN)) {
+    LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent SE_OPEN";
+    if (state_ != SSL_WAIT) {
+      RTC_DCHECK(state_ == SSL_NONE);
+      events_to_signal |= SE_OPEN;
+    } else {
+      state_ = SSL_CONNECTING;
+      if (int err = BeginSSL()) {
+        Error("BeginSSL", err, 0, true);
+        return;
+      }
+    }
+  }
+  if ((events & (SE_READ|SE_WRITE))) {
+    LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent"
+                 << ((events & SE_READ) ? " SE_READ" : "")
+                 << ((events & SE_WRITE) ? " SE_WRITE" : "");
+    if (state_ == SSL_NONE) {
+      events_to_signal |= events & (SE_READ|SE_WRITE);
+    } else if (state_ == SSL_CONNECTING) {
+      if (int err = ContinueSSL()) {
+        Error("ContinueSSL", err, 0, true);
+        return;
+      }
+    } else if (state_ == SSL_CONNECTED) {
+      if (((events & SE_READ) && ssl_write_needs_read_) ||
+          (events & SE_WRITE)) {
+        LOG(LS_VERBOSE) << " -- onStreamWriteable";
+        events_to_signal |= SE_WRITE;
+      }
+      if (((events & SE_WRITE) && ssl_read_needs_write_) ||
+          (events & SE_READ)) {
+        LOG(LS_VERBOSE) << " -- onStreamReadable";
+        events_to_signal |= SE_READ;
+      }
+    }
+  }
+  if ((events & SE_CLOSE)) {
+    LOG(LS_VERBOSE) << "OpenSSLStreamAdapter::OnEvent(SE_CLOSE, " << err << ")";
+    Cleanup(0);
+    events_to_signal |= SE_CLOSE;
+    // SE_CLOSE is the only event that uses the final parameter to OnEvent().
+    RTC_DCHECK(signal_error == 0);
+    signal_error = err;
+  }
+  if (events_to_signal)
+    StreamAdapterInterface::OnEvent(stream, events_to_signal, signal_error);
+}
+
+int OpenSSLStreamAdapter::BeginSSL() {
+  RTC_DCHECK(state_ == SSL_CONNECTING);
+  // The underlying stream has opened.
+  LOG(LS_INFO) << "BeginSSL with peer.";
+
+  BIO* bio = nullptr;
+
+  // First set up the context.
+  RTC_DCHECK(ssl_ctx_ == nullptr);
+  ssl_ctx_ = SetupSSLContext();
+  if (!ssl_ctx_)
+    return -1;
+
+  bio = BIO_new_stream(static_cast<StreamInterface*>(stream()));
+  if (!bio)
+    return -1;
+
+  ssl_ = SSL_new(ssl_ctx_);
+  if (!ssl_) {
+    BIO_free(bio);
+    return -1;
+  }
+
+  SSL_set_app_data(ssl_, this);
+
+  SSL_set_bio(ssl_, bio, bio);  // the SSL object owns the bio now.
+  if (ssl_mode_ == SSL_MODE_DTLS) {
+#ifdef OPENSSL_IS_BORINGSSL
+    DTLSv1_set_initial_timeout_duration(ssl_, dtls_handshake_timeout_ms_);
+#else
+    // Enable read-ahead for DTLS so whole packets are read from internal BIO
+    // before parsing. This is done internally by BoringSSL for DTLS.
+    SSL_set_read_ahead(ssl_, 1);
+#endif
+  }
+
+  SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE |
+               SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+#if !defined(OPENSSL_IS_BORINGSSL)
+  // Specify an ECDH group for ECDHE ciphers, otherwise OpenSSL cannot
+  // negotiate them when acting as the server. Use NIST's P-256 which is
+  // commonly supported. BoringSSL doesn't need explicit configuration and has
+  // a reasonable default set.
+  EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+  if (ecdh == nullptr)
+    return -1;
+  SSL_set_options(ssl_, SSL_OP_SINGLE_ECDH_USE);
+  SSL_set_tmp_ecdh(ssl_, ecdh);
+  EC_KEY_free(ecdh);
+#endif
+
+  // Do the connect
+  return ContinueSSL();
+}
+
+int OpenSSLStreamAdapter::ContinueSSL() {
+  LOG(LS_VERBOSE) << "ContinueSSL";
+  RTC_DCHECK(state_ == SSL_CONNECTING);
+
+  // Clear the DTLS timer
+  Thread::Current()->Clear(this, MSG_TIMEOUT);
+
+  int code = (role_ == SSL_CLIENT) ? SSL_connect(ssl_) : SSL_accept(ssl_);
+  int ssl_error;
+  switch (ssl_error = SSL_get_error(ssl_, code)) {
+    case SSL_ERROR_NONE:
+      LOG(LS_VERBOSE) << " -- success";
+      // By this point, OpenSSL should have given us a certificate, or errored
+      // out if one was missing.
+      RTC_DCHECK(peer_certificate_ || !client_auth_enabled());
+
+      state_ = SSL_CONNECTED;
+      if (!waiting_to_verify_peer_certificate()) {
+        // We have everything we need to start the connection, so signal
+        // SE_OPEN. If we need a client certificate fingerprint and don't have
+        // it yet, we'll instead signal SE_OPEN in SetPeerCertificateDigest.
+        //
+        // TODO(deadbeef): Post this event asynchronously to unwind the stack.
+        // The caller of ContinueSSL may be the same object listening for these
+        // events and may not be prepared for reentrancy.
+        // PostEvent(SE_OPEN | SE_READ | SE_WRITE, 0);
+        StreamAdapterInterface::OnEvent(stream(), SE_OPEN | SE_READ | SE_WRITE,
+                                        0);
+      }
+      break;
+
+    case SSL_ERROR_WANT_READ: {
+        LOG(LS_VERBOSE) << " -- error want read";
+        struct timeval timeout;
+        if (DTLSv1_get_timeout(ssl_, &timeout)) {
+          int delay = timeout.tv_sec * 1000 + timeout.tv_usec/1000;
+
+          Thread::Current()->PostDelayed(RTC_FROM_HERE, delay, this,
+                                         MSG_TIMEOUT, 0);
+        }
+      }
+      break;
+
+    case SSL_ERROR_WANT_WRITE:
+      LOG(LS_VERBOSE) << " -- error want write";
+      break;
+
+    case SSL_ERROR_ZERO_RETURN:
+    default:
+      LOG(LS_VERBOSE) << " -- error " << code;
+      SSLHandshakeError ssl_handshake_err = SSLHandshakeError::UNKNOWN;
+      int err_code = ERR_peek_last_error();
+      if (err_code != 0 && ERR_GET_REASON(err_code) == SSL_R_NO_SHARED_CIPHER) {
+        ssl_handshake_err = SSLHandshakeError::INCOMPATIBLE_CIPHERSUITE;
+      }
+      SignalSSLHandshakeError(ssl_handshake_err);
+      return (ssl_error != 0) ? ssl_error : -1;
+  }
+
+  return 0;
+}
+
+void OpenSSLStreamAdapter::Error(const char* context,
+                                 int err,
+                                 uint8_t alert,
+                                 bool signal) {
+  LOG(LS_WARNING) << "OpenSSLStreamAdapter::Error(" << context << ", " << err
+                  << ", " << static_cast<int>(alert) << ")";
+  state_ = SSL_ERROR;
+  ssl_error_code_ = err;
+  Cleanup(alert);
+  if (signal)
+    StreamAdapterInterface::OnEvent(stream(), SE_CLOSE, err);
+}
+
+void OpenSSLStreamAdapter::Cleanup(uint8_t alert) {
+  LOG(LS_INFO) << "Cleanup";
+
+  if (state_ != SSL_ERROR) {
+    state_ = SSL_CLOSED;
+    ssl_error_code_ = 0;
+  }
+
+  if (ssl_) {
+    int ret;
+// SSL_send_fatal_alert is only available in BoringSSL.
+#ifdef OPENSSL_IS_BORINGSSL
+    if (alert) {
+      ret = SSL_send_fatal_alert(ssl_, alert);
+      if (ret < 0) {
+        LOG(LS_WARNING) << "SSL_send_fatal_alert failed, error = "
+                        << SSL_get_error(ssl_, ret);
+      }
+    } else {
+#endif
+      ret = SSL_shutdown(ssl_);
+      if (ret < 0) {
+        LOG(LS_WARNING) << "SSL_shutdown failed, error = "
+                        << SSL_get_error(ssl_, ret);
+      }
+#ifdef OPENSSL_IS_BORINGSSL
+    }
+#endif
+    SSL_free(ssl_);
+    ssl_ = nullptr;
+  }
+  if (ssl_ctx_) {
+    SSL_CTX_free(ssl_ctx_);
+    ssl_ctx_ = nullptr;
+  }
+  identity_.reset();
+  peer_certificate_.reset();
+
+  // Clear the DTLS timer
+  Thread::Current()->Clear(this, MSG_TIMEOUT);
+}
+
+
+void OpenSSLStreamAdapter::OnMessage(Message* msg) {
+  // Process our own messages and then pass others to the superclass
+  if (MSG_TIMEOUT == msg->message_id) {
+    LOG(LS_INFO) << "DTLS timeout expired";
+    DTLSv1_handle_timeout(ssl_);
+    ContinueSSL();
+  } else {
+    StreamInterface::OnMessage(msg);
+  }
+}
+
+SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
+  SSL_CTX* ctx = nullptr;
+
+#ifdef OPENSSL_IS_BORINGSSL
+    ctx = SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ?
+        DTLS_method() : TLS_method());
+    // Version limiting for BoringSSL will be done below.
+#else
+  const SSL_METHOD* method;
+  switch (ssl_max_version_) {
+    case SSL_PROTOCOL_TLS_10:
+    case SSL_PROTOCOL_TLS_11:
+      // OpenSSL doesn't support setting min/max versions, so we always use
+      // (D)TLS 1.0 if a max. version below the max. available is requested.
+      if (ssl_mode_ == SSL_MODE_DTLS) {
+        if (role_ == SSL_CLIENT) {
+          method = DTLSv1_client_method();
+        } else {
+          method = DTLSv1_server_method();
+        }
+      } else {
+        if (role_ == SSL_CLIENT) {
+          method = TLSv1_client_method();
+        } else {
+          method = TLSv1_server_method();
+        }
+      }
+      break;
+    case SSL_PROTOCOL_TLS_12:
+    default:
+      if (ssl_mode_ == SSL_MODE_DTLS) {
+#if (OPENSSL_VERSION_NUMBER >= 0x10002000L)
+        // DTLS 1.2 only available starting from OpenSSL 1.0.2
+        if (role_ == SSL_CLIENT) {
+          method = DTLS_client_method();
+        } else {
+          method = DTLS_server_method();
+        }
+#else
+        if (role_ == SSL_CLIENT) {
+          method = DTLSv1_client_method();
+        } else {
+          method = DTLSv1_server_method();
+        }
+#endif
+      } else {
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+        // New API only available starting from OpenSSL 1.1.0
+        if (role_ == SSL_CLIENT) {
+          method = TLS_client_method();
+        } else {
+          method = TLS_server_method();
+        }
+#else
+        if (role_ == SSL_CLIENT) {
+          method = SSLv23_client_method();
+        } else {
+          method = SSLv23_server_method();
+        }
+#endif
+      }
+      break;
+  }
+  ctx = SSL_CTX_new(method);
+#endif  // OPENSSL_IS_BORINGSSL
+
+  if (ctx == nullptr)
+    return nullptr;
+
+#ifdef OPENSSL_IS_BORINGSSL
+  SSL_CTX_set_min_proto_version(ctx, ssl_mode_ == SSL_MODE_DTLS ?
+      DTLS1_VERSION : TLS1_VERSION);
+  switch (ssl_max_version_) {
+    case SSL_PROTOCOL_TLS_10:
+      SSL_CTX_set_max_proto_version(ctx, ssl_mode_ == SSL_MODE_DTLS ?
+          DTLS1_VERSION : TLS1_VERSION);
+      break;
+    case SSL_PROTOCOL_TLS_11:
+      SSL_CTX_set_max_proto_version(ctx, ssl_mode_ == SSL_MODE_DTLS ?
+          DTLS1_VERSION : TLS1_1_VERSION);
+      break;
+    case SSL_PROTOCOL_TLS_12:
+    default:
+      SSL_CTX_set_max_proto_version(ctx, ssl_mode_ == SSL_MODE_DTLS ?
+          DTLS1_2_VERSION : TLS1_2_VERSION);
+      break;
+  }
+  if (g_use_time_callback_for_testing) {
+    SSL_CTX_set_current_time_cb(ctx, &TimeCallbackForTesting);
+  }
+#endif
+
+  if (identity_ && !identity_->ConfigureIdentity(ctx)) {
+    SSL_CTX_free(ctx);
+    return nullptr;
+  }
+
+#if !defined(NDEBUG)
+  SSL_CTX_set_info_callback(ctx, OpenSSLAdapter::SSLInfoCallback);
+#endif
+
+  int mode = SSL_VERIFY_PEER;
+  if (client_auth_enabled()) {
+    // Require a certificate from the client.
+    // Note: Normally this is always true in production, but it may be disabled
+    // for testing purposes (e.g. SSLAdapter unit tests).
+    mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+  }
+
+  SSL_CTX_set_verify(ctx, mode, SSLVerifyCallback);
+  SSL_CTX_set_verify_depth(ctx, 4);
+  // Select list of available ciphers. Note that !SHA256 and !SHA384 only
+  // remove HMAC-SHA256 and HMAC-SHA384 cipher suites, not GCM cipher suites
+  // with SHA256 or SHA384 as the handshake hash.
+  // This matches the list of SSLClientSocketOpenSSL in Chromium.
+  SSL_CTX_set_cipher_list(
+      ctx, "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK");
+
+  if (!srtp_ciphers_.empty()) {
+    if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_ciphers_.c_str())) {
+      SSL_CTX_free(ctx);
+      return nullptr;
+    }
+  }
+
+  return ctx;
+}
+
+bool OpenSSLStreamAdapter::VerifyPeerCertificate() {
+  if (!has_peer_certificate_digest() || !peer_certificate_) {
+    LOG(LS_WARNING) << "Missing digest or peer certificate.";
+    return false;
+  }
+
+  unsigned char digest[EVP_MAX_MD_SIZE];
+  size_t digest_length;
+  if (!OpenSSLCertificate::ComputeDigest(
+          peer_certificate_->x509(), peer_certificate_digest_algorithm_, digest,
+          sizeof(digest), &digest_length)) {
+    LOG(LS_WARNING) << "Failed to compute peer cert digest.";
+    return false;
+  }
+
+  Buffer computed_digest(digest, digest_length);
+  if (computed_digest != peer_certificate_digest_value_) {
+    LOG(LS_WARNING) << "Rejected peer certificate due to mismatched digest.";
+    return false;
+  }
+  // Ignore any verification error if the digest matches, since there is no
+  // value in checking the validity of a self-signed cert issued by untrusted
+  // sources.
+  LOG(LS_INFO) << "Accepted peer certificate.";
+  peer_certificate_verified_ = true;
+  return true;
+}
+
+int OpenSSLStreamAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
+  // Get our SSL structure from the store
+  SSL* ssl = reinterpret_cast<SSL*>(
+      X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()));
+  X509* cert = X509_STORE_CTX_get_current_cert(store);
+  int depth = X509_STORE_CTX_get_error_depth(store);
+
+  // For now we ignore the parent certificates and verify the leaf against
+  // the digest.
+  //
+  // TODO(jiayl): Verify the chain is a proper chain and report the chain to
+  // |stream->peer_certificate_|.
+  if (depth > 0) {
+    LOG(LS_INFO) << "Ignored chained certificate at depth " << depth;
+    return 1;
+  }
+
+  OpenSSLStreamAdapter* stream =
+      reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
+
+  // Record the peer's certificate.
+  stream->peer_certificate_.reset(new OpenSSLCertificate(cert));
+
+  // If the peer certificate digest isn't known yet, we'll wait to verify
+  // until it's known, and for now just return a success status.
+  if (stream->peer_certificate_digest_algorithm_.empty()) {
+    LOG(LS_INFO) << "Waiting to verify certificate until digest is known.";
+    return 1;
+  }
+
+  return stream->VerifyPeerCertificate();
+}
+
+bool OpenSSLStreamAdapter::IsBoringSsl() {
+#ifdef OPENSSL_IS_BORINGSSL
+  return true;
+#else
+  return false;
+#endif
+}
+
+#define CDEF(X) \
+  { static_cast<uint16_t>(TLS1_CK_##X & 0xffff), "TLS_" #X }
+
+struct cipher_list {
+  uint16_t cipher;
+  const char* cipher_str;
+};
+
+// TODO(torbjorng): Perhaps add more cipher suites to these lists.
+static const cipher_list OK_RSA_ciphers[] = {
+  CDEF(ECDHE_RSA_WITH_AES_128_CBC_SHA),
+  CDEF(ECDHE_RSA_WITH_AES_256_CBC_SHA),
+  CDEF(ECDHE_RSA_WITH_AES_128_GCM_SHA256),
+#ifdef TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA256
+  CDEF(ECDHE_RSA_WITH_AES_256_GCM_SHA256),
+#endif
+#ifdef TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+  CDEF(ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
+#endif
+};
+
+static const cipher_list OK_ECDSA_ciphers[] = {
+  CDEF(ECDHE_ECDSA_WITH_AES_128_CBC_SHA),
+  CDEF(ECDHE_ECDSA_WITH_AES_256_CBC_SHA),
+  CDEF(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
+#ifdef TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA256
+  CDEF(ECDHE_ECDSA_WITH_AES_256_GCM_SHA256),
+#endif
+#ifdef TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
+  CDEF(ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256),
+#endif
+};
+#undef CDEF
+
+bool OpenSSLStreamAdapter::IsAcceptableCipher(int cipher, KeyType key_type) {
+  if (key_type == KT_RSA) {
+    for (const cipher_list& c : OK_RSA_ciphers) {
+      if (cipher == c.cipher)
+        return true;
+    }
+  }
+
+  if (key_type == KT_ECDSA) {
+    for (const cipher_list& c : OK_ECDSA_ciphers) {
+      if (cipher == c.cipher)
+        return true;
+    }
+  }
+
+  return false;
+}
+
+bool OpenSSLStreamAdapter::IsAcceptableCipher(const std::string& cipher,
+                                              KeyType key_type) {
+  if (key_type == KT_RSA) {
+    for (const cipher_list& c : OK_RSA_ciphers) {
+      if (cipher == c.cipher_str)
+        return true;
+    }
+  }
+
+  if (key_type == KT_ECDSA) {
+    for (const cipher_list& c : OK_ECDSA_ciphers) {
+      if (cipher == c.cipher_str)
+        return true;
+    }
+  }
+
+  return false;
+}
+
+void OpenSSLStreamAdapter::enable_time_callback_for_testing() {
+  g_use_time_callback_for_testing = true;
+}
+
+}  // namespace rtc
diff --git a/base/opensslstreamadapter.h b/base/opensslstreamadapter.h
index e17e029..16b62eb 100644
--- a/base/opensslstreamadapter.h
+++ b/base/opensslstreamadapter.h
@@ -8,12 +8,219 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_OPENSSLSTREAMADAPTER_H_
-#define WEBRTC_BASE_OPENSSLSTREAMADAPTER_H_
+#ifndef WEBRTC_BASE_OPENSSLSTREAMADAPTER_H__
+#define WEBRTC_BASE_OPENSSLSTREAMADAPTER_H__
 
+#include <string>
+#include <memory>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/opensslstreamadapter.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/sslstreamadapter.h"
+#include "webrtc/base/opensslidentity.h"
 
-#endif  // WEBRTC_BASE_OPENSSLSTREAMADAPTER_H_
+typedef struct ssl_st SSL;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct ssl_cipher_st SSL_CIPHER;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+
+namespace rtc {
+
+// This class was written with OpenSSLAdapter (a socket adapter) as a
+// starting point. It has similar structure and functionality, but uses a
+// "peer-to-peer" mode, verifying the peer's certificate using a digest
+// sent over a secure signaling channel.
+//
+// Static methods to initialize and deinit the SSL library are in
+// OpenSSLAdapter. These should probably be moved out to a neutral class.
+//
+// In a few cases I have factored out some OpenSSLAdapter code into static
+// methods so it can be reused from this class. Eventually that code should
+// probably be moved to a common support class. Unfortunately there remain a
+// few duplicated sections of code. I have not done more restructuring because
+// I did not want to affect existing code that uses OpenSSLAdapter.
+//
+// This class does not support the SSL connection restart feature present in
+// OpenSSLAdapter. I am not entirely sure how the feature is useful and I am
+// not convinced that it works properly.
+//
+// This implementation is careful to disallow data exchange after an SSL error,
+// and it has an explicit SSL_CLOSED state. It should not be possible to send
+// any data in clear after one of the StartSSL methods has been called.
+
+// Look in sslstreamadapter.h for documentation of the methods.
+
+class OpenSSLIdentity;
+
+///////////////////////////////////////////////////////////////////////////////
+
+class OpenSSLStreamAdapter : public SSLStreamAdapter {
+ public:
+  explicit OpenSSLStreamAdapter(StreamInterface* stream);
+  ~OpenSSLStreamAdapter() override;
+
+  void SetIdentity(SSLIdentity* identity) override;
+
+  // Default argument is for compatibility
+  void SetServerRole(SSLRole role = SSL_SERVER) override;
+  bool SetPeerCertificateDigest(
+      const std::string& digest_alg,
+      const unsigned char* digest_val,
+      size_t digest_len,
+      SSLPeerCertificateDigestError* error = nullptr) override;
+
+  std::unique_ptr<SSLCertificate> GetPeerCertificate() const override;
+
+  // Goes from state SSL_NONE to either SSL_CONNECTING or SSL_WAIT, depending
+  // on whether the underlying stream is already open or not.
+  int StartSSL() override;
+  void SetMode(SSLMode mode) override;
+  void SetMaxProtocolVersion(SSLProtocolVersion version) override;
+  void SetInitialRetransmissionTimeout(int timeout_ms) override;
+
+  StreamResult Read(void* data,
+                    size_t data_len,
+                    size_t* read,
+                    int* error) override;
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+  void Close() override;
+  StreamState GetState() const override;
+
+  // TODO(guoweis): Move this away from a static class method.
+  static std::string SslCipherSuiteToName(int crypto_suite);
+
+  bool GetSslCipherSuite(int* cipher) override;
+
+  int GetSslVersion() const override;
+
+  // Key Extractor interface
+  bool ExportKeyingMaterial(const std::string& label,
+                            const uint8_t* context,
+                            size_t context_len,
+                            bool use_context,
+                            uint8_t* result,
+                            size_t result_len) override;
+
+  // DTLS-SRTP interface
+  bool SetDtlsSrtpCryptoSuites(const std::vector<int>& crypto_suites) override;
+  bool GetDtlsSrtpCryptoSuite(int* crypto_suite) override;
+
+  bool IsTlsConnected() override;
+
+  // Capabilities interfaces.
+  static bool IsBoringSsl();
+
+  static bool IsAcceptableCipher(int cipher, KeyType key_type);
+  static bool IsAcceptableCipher(const std::string& cipher, KeyType key_type);
+
+  // Use our timeutils.h source of timing in BoringSSL, allowing us to test
+  // using a fake clock.
+  static void enable_time_callback_for_testing();
+
+ protected:
+  void OnEvent(StreamInterface* stream, int events, int err) override;
+
+ private:
+  enum SSLState {
+    // Before calling one of the StartSSL methods, data flows
+    // in clear text.
+    SSL_NONE,
+    SSL_WAIT,  // waiting for the stream to open to start SSL negotiation
+    SSL_CONNECTING,  // SSL negotiation in progress
+    SSL_CONNECTED,  // SSL stream successfully established
+    SSL_ERROR,  // some SSL error occurred, stream is closed
+    SSL_CLOSED  // Clean close
+  };
+
+  enum { MSG_TIMEOUT = MSG_MAX+1};
+
+  // The following three methods return 0 on success and a negative
+  // error code on failure. The error code may be from OpenSSL or -1
+  // on some other error cases, so it can't really be interpreted
+  // unfortunately.
+
+  // Prepare SSL library, state is SSL_CONNECTING.
+  int BeginSSL();
+  // Perform SSL negotiation steps.
+  int ContinueSSL();
+
+  // Error handler helper. signal is given as true for errors in
+  // asynchronous contexts (when an error method was not returned
+  // through some other method), and in that case an SE_CLOSE event is
+  // raised on the stream with the specified error.
+  // A 0 error means a graceful close, otherwise there is not really enough
+  // context to interpret the error code.
+  // |alert| indicates an alert description (one of the SSL_AD constants) to
+  // send to the remote endpoint when closing the association. If 0, a normal
+  // shutdown will be performed.
+  void Error(const char* context, int err, uint8_t alert, bool signal);
+  void Cleanup(uint8_t alert);
+
+  // Override MessageHandler
+  void OnMessage(Message* msg) override;
+
+  // Flush the input buffers by reading left bytes (for DTLS)
+  void FlushInput(unsigned int left);
+
+  // SSL library configuration
+  SSL_CTX* SetupSSLContext();
+  // Verify the peer certificate matches the signaled digest.
+  bool VerifyPeerCertificate();
+  // SSL certification verification error handler, called back from
+  // the openssl library. Returns an int interpreted as a boolean in
+  // the C style: zero means verification failure, non-zero means
+  // passed.
+  static int SSLVerifyCallback(int ok, X509_STORE_CTX* store);
+
+  bool waiting_to_verify_peer_certificate() const {
+    return client_auth_enabled() && !peer_certificate_verified_;
+  }
+
+  bool has_peer_certificate_digest() const {
+    return !peer_certificate_digest_algorithm_.empty() &&
+           !peer_certificate_digest_value_.empty();
+  }
+
+  SSLState state_;
+  SSLRole role_;
+  int ssl_error_code_;  // valid when state_ == SSL_ERROR or SSL_CLOSED
+  // Whether the SSL negotiation is blocked on needing to read or
+  // write to the wrapped stream.
+  bool ssl_read_needs_write_;
+  bool ssl_write_needs_read_;
+
+  SSL* ssl_;
+  SSL_CTX* ssl_ctx_;
+
+  // Our key and certificate.
+  std::unique_ptr<OpenSSLIdentity> identity_;
+  // The certificate that the peer presented. Initially null, until the
+  // connection is established.
+  std::unique_ptr<OpenSSLCertificate> peer_certificate_;
+  bool peer_certificate_verified_ = false;
+  // The digest of the certificate that the peer must present.
+  Buffer peer_certificate_digest_value_;
+  std::string peer_certificate_digest_algorithm_;
+
+  // The DtlsSrtp ciphers
+  std::string srtp_ciphers_;
+
+  // Do DTLS or not
+  SSLMode ssl_mode_;
+
+  // Max. allowed protocol version
+  SSLProtocolVersion ssl_max_version_;
+
+  // A 50-ms initial timeout ensures rapid setup on fast connections, but may
+  // be too aggressive for low bandwidth links.
+  int dtls_handshake_timeout_ms_ = 50;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_OPENSSLSTREAMADAPTER_H__
diff --git a/base/optional.cc b/base/optional.cc
new file mode 100644
index 0000000..6bebdd5
--- /dev/null
+++ b/base/optional.cc
@@ -0,0 +1,23 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/optional.h"
+
+namespace rtc {
+namespace optional_internal {
+
+#if RTC_HAS_ASAN
+
+void* FunctionThatDoesNothingImpl(void* x) { return x; }
+
+#endif
+
+}  // namespace optional_internal
+}  // namespace rtc
diff --git a/base/optional.h b/base/optional.h
index 7657ec3..4f883a8 100644
--- a/base/optional.h
+++ b/base/optional.h
@@ -11,9 +11,399 @@
 #ifndef WEBRTC_BASE_OPTIONAL_H_
 #define WEBRTC_BASE_OPTIONAL_H_
 
+#include <algorithm>
+#include <memory>
+#include <utility>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/optional.h"
+#ifdef UNIT_TEST
+#include <iomanip>
+#include <ostream>
+#endif  // UNIT_TEST
+
+#include "webrtc/base/array_view.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/sanitizer.h"
+
+namespace rtc {
+
+namespace optional_internal {
+
+#if RTC_HAS_ASAN
+
+// This is a non-inlined function. The optimizer can't see inside it.  It
+// prevents the compiler from generating optimized code that reads value_ even
+// if it is unset. Although safe, this causes memory sanitizers to complain.
+void* FunctionThatDoesNothingImpl(void*);
+
+template <typename T>
+inline T* FunctionThatDoesNothing(T* x) {
+  return reinterpret_cast<T*>(
+      FunctionThatDoesNothingImpl(reinterpret_cast<void*>(x)));
+}
+
+#else
+
+template <typename T>
+inline T* FunctionThatDoesNothing(T* x) { return x; }
+
+#endif
+
+}  // namespace optional_internal
+
+// Simple std::optional-wannabe. It either contains a T or not.
+//
+// A moved-from Optional<T> may only be destroyed, and assigned to if T allows
+// being assigned to after having been moved from. Specifically, you may not
+// assume that it just doesn't contain a value anymore.
+//
+// Examples of good places to use Optional:
+//
+// - As a class or struct member, when the member doesn't always have a value:
+//     struct Prisoner {
+//       std::string name;
+//       Optional<int> cell_number;  // Empty if not currently incarcerated.
+//     };
+//
+// - As a return value for functions that may fail to return a value on all
+//   allowed inputs. For example, a function that searches an array might
+//   return an Optional<size_t> (the index where it found the element, or
+//   nothing if it didn't find it); and a function that parses numbers might
+//   return Optional<double> (the parsed number, or nothing if parsing failed).
+//
+// Examples of bad places to use Optional:
+//
+// - As a return value for functions that may fail because of disallowed
+//   inputs. For example, a string length function should not return
+//   Optional<size_t> so that it can return nothing in case the caller passed
+//   it a null pointer; the function should probably use RTC_[D]CHECK instead,
+//   and return plain size_t.
+//
+// - As a return value for functions that may fail to return a value on all
+//   allowed inputs, but need to tell the caller what went wrong. Returning
+//   Optional<double> when parsing a single number as in the example above
+//   might make sense, but any larger parse job is probably going to need to
+//   tell the caller what the problem was, not just that there was one.
+//
+// - As a non-mutable function argument. When you want to pass a value of a
+//   type T that can fail to be there, const T* is almost always both fastest
+//   and cleanest. (If you're *sure* that the the caller will always already
+//   have an Optional<T>, const Optional<T>& is slightly faster than const T*,
+//   but this is a micro-optimization. In general, stick to const T*.)
+//
+// TODO(kwiberg): Get rid of this class when the standard library has
+// std::optional (and we're allowed to use it).
+template <typename T>
+class Optional final {
+ public:
+  // Construct an empty Optional.
+  Optional() : has_value_(false), empty_('\0') {
+    PoisonValue();
+  }
+
+  // Construct an Optional that contains a value.
+  explicit Optional(const T& value) : has_value_(true) {
+    new (&value_) T(value);
+  }
+  explicit Optional(T&& value) : has_value_(true) {
+    new (&value_) T(std::move(value));
+  }
+
+  // Copy constructor: copies the value from m if it has one.
+  Optional(const Optional& m) : has_value_(m.has_value_) {
+    if (has_value_)
+      new (&value_) T(m.value_);
+    else
+      PoisonValue();
+  }
+
+  // Move constructor: if m has a value, moves the value from m, leaving m
+  // still in a state where it has a value, but a moved-from one (the
+  // properties of which depends on T; the only general guarantee is that we
+  // can destroy m).
+  Optional(Optional&& m) : has_value_(m.has_value_) {
+    if (has_value_)
+      new (&value_) T(std::move(m.value_));
+    else
+      PoisonValue();
+  }
+
+  ~Optional() {
+    if (has_value_)
+      value_.~T();
+    else
+      UnpoisonValue();
+  }
+
+  // Copy assignment. Uses T's copy assignment if both sides have a value, T's
+  // copy constructor if only the right-hand side has a value.
+  Optional& operator=(const Optional& m) {
+    if (m.has_value_) {
+      if (has_value_) {
+        value_ = m.value_;  // T's copy assignment.
+      } else {
+        UnpoisonValue();
+        new (&value_) T(m.value_);  // T's copy constructor.
+        has_value_ = true;
+      }
+    } else {
+      reset();
+    }
+    return *this;
+  }
+
+  // Move assignment. Uses T's move assignment if both sides have a value, T's
+  // move constructor if only the right-hand side has a value. The state of m
+  // after it's been moved from is as for the move constructor.
+  Optional& operator=(Optional&& m) {
+    if (m.has_value_) {
+      if (has_value_) {
+        value_ = std::move(m.value_);  // T's move assignment.
+      } else {
+        UnpoisonValue();
+        new (&value_) T(std::move(m.value_));  // T's move constructor.
+        has_value_ = true;
+      }
+    } else {
+      reset();
+    }
+    return *this;
+  }
+
+  // Swap the values if both m1 and m2 have values; move the value if only one
+  // of them has one.
+  friend void swap(Optional& m1, Optional& m2) {
+    if (m1.has_value_) {
+      if (m2.has_value_) {
+        // Both have values: swap.
+        using std::swap;
+        swap(m1.value_, m2.value_);
+      } else {
+        // Only m1 has a value: move it to m2.
+        m2.UnpoisonValue();
+        new (&m2.value_) T(std::move(m1.value_));
+        m1.value_.~T();  // Destroy the moved-from value.
+        m1.has_value_ = false;
+        m2.has_value_ = true;
+        m1.PoisonValue();
+      }
+    } else if (m2.has_value_) {
+      // Only m2 has a value: move it to m1.
+      m1.UnpoisonValue();
+      new (&m1.value_) T(std::move(m2.value_));
+      m2.value_.~T();  // Destroy the moved-from value.
+      m1.has_value_ = true;
+      m2.has_value_ = false;
+      m2.PoisonValue();
+    }
+  }
+
+  // Destroy any contained value. Has no effect if we have no value.
+  void reset() {
+    if (!has_value_)
+      return;
+    value_.~T();
+    has_value_ = false;
+    PoisonValue();
+  }
+
+  template <class... Args>
+  void emplace(Args&&... args) {
+    if (has_value_)
+      value_.~T();
+    else
+      UnpoisonValue();
+    new (&value_) T(std::forward<Args>(args)...);
+    has_value_ = true;
+  }
+
+  // Conversion to bool to test if we have a value.
+  explicit operator bool() const { return has_value_; }
+  bool has_value() const { return has_value_; }
+
+  // Dereferencing. Only allowed if we have a value.
+  const T* operator->() const {
+    RTC_DCHECK(has_value_);
+    return &value_;
+  }
+  T* operator->() {
+    RTC_DCHECK(has_value_);
+    return &value_;
+  }
+  const T& operator*() const {
+    RTC_DCHECK(has_value_);
+    return value_;
+  }
+  T& operator*() {
+    RTC_DCHECK(has_value_);
+    return value_;
+  }
+  const T& value() const {
+    RTC_DCHECK(has_value_);
+    return value_;
+  }
+  T& value() {
+    RTC_DCHECK(has_value_);
+    return value_;
+  }
+
+  // Dereference with a default value in case we don't have a value.
+  const T& value_or(const T& default_val) const {
+    // The no-op call prevents the compiler from generating optimized code that
+    // reads value_ even if !has_value_, but only if FunctionThatDoesNothing is
+    // not completely inlined; see its declaration.).
+    return has_value_ ? *optional_internal::FunctionThatDoesNothing(&value_)
+                      : default_val;
+  }
+
+  // Dereference and move value.
+  T MoveValue() {
+    RTC_DCHECK(has_value_);
+    return std::move(value_);
+  }
+
+  // Equality tests. Two Optionals are equal if they contain equivalent values,
+  // or if they're both empty.
+  friend bool operator==(const Optional& m1, const Optional& m2) {
+    return m1.has_value_ && m2.has_value_ ? m1.value_ == m2.value_
+                                          : m1.has_value_ == m2.has_value_;
+  }
+  friend bool operator==(const Optional& opt, const T& value) {
+    return opt.has_value_ && opt.value_ == value;
+  }
+  friend bool operator==(const T& value, const Optional& opt) {
+    return opt.has_value_ && value == opt.value_;
+  }
+
+  friend bool operator!=(const Optional& m1, const Optional& m2) {
+    return m1.has_value_ && m2.has_value_ ? m1.value_ != m2.value_
+                                          : m1.has_value_ != m2.has_value_;
+  }
+  friend bool operator!=(const Optional& opt, const T& value) {
+    return !opt.has_value_ || opt.value_ != value;
+  }
+  friend bool operator!=(const T& value, const Optional& opt) {
+    return !opt.has_value_ || value != opt.value_;
+  }
+
+ private:
+  // Tell sanitizers that value_ shouldn't be touched.
+  void PoisonValue() {
+    rtc::AsanPoison(rtc::MakeArrayView(&value_, 1));
+    rtc::MsanMarkUninitialized(rtc::MakeArrayView(&value_, 1));
+  }
+
+  // Tell sanitizers that value_ is OK to touch again.
+  void UnpoisonValue() {
+    rtc::AsanUnpoison(rtc::MakeArrayView(&value_, 1));
+  }
+
+  bool has_value_;  // True iff value_ contains a live value.
+  union {
+    // empty_ exists only to make it possible to initialize the union, even when
+    // it doesn't contain any data. If the union goes uninitialized, it may
+    // trigger compiler warnings.
+    char empty_;
+    // By placing value_ in a union, we get to manage its construction and
+    // destruction manually: the Optional constructors won't automatically
+    // construct it, and the Optional destructor won't automatically destroy
+    // it. Basically, this just allocates a properly sized and aligned block of
+    // memory in which we can manually put a T with placement new.
+    T value_;
+  };
+};
+
+#ifdef UNIT_TEST
+namespace optional_internal {
+
+// Checks if there's a valid PrintTo(const T&, std::ostream*) call for T.
+template <typename T>
+struct HasPrintTo {
+ private:
+  struct No {};
+
+  template <typename T2>
+  static auto Test(const T2& obj)
+      -> decltype(PrintTo(obj, std::declval<std::ostream*>()));
+
+  template <typename>
+  static No Test(...);
+
+ public:
+  static constexpr bool value =
+      !std::is_same<decltype(Test<T>(std::declval<const T&>())), No>::value;
+};
+
+// Checks if there's a valid operator<<(std::ostream&, const T&) call for T.
+template <typename T>
+struct HasOstreamOperator {
+ private:
+  struct No {};
+
+  template <typename T2>
+  static auto Test(const T2& obj)
+      -> decltype(std::declval<std::ostream&>() << obj);
+
+  template <typename>
+  static No Test(...);
+
+ public:
+  static constexpr bool value =
+      !std::is_same<decltype(Test<T>(std::declval<const T&>())), No>::value;
+};
+
+// Prefer using PrintTo to print the object.
+template <typename T>
+typename std::enable_if<HasPrintTo<T>::value, void>::type OptionalPrintToHelper(
+    const T& value,
+    std::ostream* os) {
+  PrintTo(value, os);
+}
+
+// Fall back to operator<<(std::ostream&, ...) if it exists.
+template <typename T>
+typename std::enable_if<HasOstreamOperator<T>::value && !HasPrintTo<T>::value,
+                        void>::type
+OptionalPrintToHelper(const T& value, std::ostream* os) {
+  *os << value;
+}
+
+inline void OptionalPrintObjectBytes(const unsigned char* bytes,
+                                     size_t size,
+                                     std::ostream* os) {
+  *os << "<optional with " << size << "-byte object [";
+  for (size_t i = 0; i != size; ++i) {
+    *os << (i == 0 ? "" : ((i & 1) ? "-" : " "));
+    *os << std::hex << std::setw(2) << std::setfill('0')
+        << static_cast<int>(bytes[i]);
+  }
+  *os << "]>";
+}
+
+// As a final back-up, just print the contents of the objcets byte-wise.
+template <typename T>
+typename std::enable_if<!HasOstreamOperator<T>::value && !HasPrintTo<T>::value,
+                        void>::type
+OptionalPrintToHelper(const T& value, std::ostream* os) {
+  OptionalPrintObjectBytes(reinterpret_cast<const unsigned char*>(&value),
+                           sizeof(value), os);
+}
+
+}  // namespace optional_internal
+
+// PrintTo is used by gtest to print out the results of tests. We want to ensure
+// the object contained in an Optional can be printed out if it's set, while
+// avoiding touching the object's storage if it is undefined.
+template <typename T>
+void PrintTo(const rtc::Optional<T>& opt, std::ostream* os) {
+  if (opt) {
+    optional_internal::OptionalPrintToHelper(*opt, os);
+  } else {
+    *os << "<empty optional>";
+  }
+}
+
+#endif  // UNIT_TEST
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_OPTIONAL_H_
diff --git a/base/optional_unittest.cc b/base/optional_unittest.cc
new file mode 100644
index 0000000..1303552
--- /dev/null
+++ b/base/optional_unittest.cc
@@ -0,0 +1,831 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/optional.h"
+
+namespace rtc {
+
+namespace {
+
+struct MyUnprintableType {
+  int value;
+};
+
+struct MyPrintableType {
+  int value;
+};
+
+struct MyOstreamPrintableType {
+  int value;
+};
+
+void PrintTo(const MyPrintableType& mpt, std::ostream* os) {
+  *os << "The value is " << mpt.value;
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const MyPrintableType& mpt) {
+  os << mpt.value;
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const MyOstreamPrintableType& mpt) {
+  os << mpt.value;
+  return os;
+}
+
+// Class whose instances logs various method calls (constructor, destructor,
+// etc.). Each instance has a unique ID (a simple global sequence number) and
+// an origin ID. When a copy is made, the new object gets a fresh ID but copies
+// the origin ID from the original. When a new Logger is created from scratch,
+// it gets a fresh ID, and the origin ID is the same as the ID (default
+// constructor) or given as an argument (explicit constructor).
+class Logger {
+ public:
+  Logger() : id_(g_next_id++), origin_(id_) { Log("default constructor"); }
+  explicit Logger(int origin) : id_(g_next_id++), origin_(origin) {
+    Log("explicit constructor");
+  }
+  Logger(int origin, const Logger& pass_by_ref, Logger pass_by_value)
+      : id_(g_next_id++), origin_(origin) {
+    Log("multi parameter constructor");
+  }
+  Logger(const Logger& other) : id_(g_next_id++), origin_(other.origin_) {
+    LogFrom("copy constructor", other);
+  }
+  Logger(Logger&& other) : id_(g_next_id++), origin_(other.origin_) {
+    LogFrom("move constructor", other);
+  }
+  ~Logger() { Log("destructor"); }
+  Logger& operator=(const Logger& other) {
+    origin_ = other.origin_;
+    LogFrom("operator= copy", other);
+    return *this;
+  }
+  Logger& operator=(Logger&& other) {
+    origin_ = other.origin_;
+    LogFrom("operator= move", other);
+    return *this;
+  }
+  friend void swap(Logger& a, Logger& b) {
+    using std::swap;
+    swap(a.origin_, b.origin_);
+    Log2("swap", a, b);
+  }
+  friend bool operator==(const Logger& a, const Logger& b) {
+    Log2("operator==", a, b);
+    return a.origin_ == b.origin_;
+  }
+  friend bool operator!=(const Logger& a, const Logger& b) {
+    Log2("operator!=", a, b);
+    return a.origin_ != b.origin_;
+  }
+  void Foo() { Log("Foo()"); }
+  void Foo() const { Log("Foo() const"); }
+  static std::unique_ptr<std::vector<std::string>> Setup() {
+    std::unique_ptr<std::vector<std::string>> s(new std::vector<std::string>);
+    g_log = s.get();
+    g_next_id = 0;
+    return s;
+  }
+
+ private:
+  int id_;
+  int origin_;
+  static std::vector<std::string>* g_log;
+  static int g_next_id;
+  void Log(const char* msg) const {
+    std::ostringstream oss;
+    oss << id_ << ':' << origin_ << ". " << msg;
+    g_log->push_back(oss.str());
+  }
+  void LogFrom(const char* msg, const Logger& other) const {
+    std::ostringstream oss;
+    oss << id_ << ':' << origin_ << ". " << msg << " (from " << other.id_ << ':'
+        << other.origin_ << ")";
+    g_log->push_back(oss.str());
+  }
+  static void Log2(const char* msg, const Logger& a, const Logger& b) {
+    std::ostringstream oss;
+    oss << msg << ' ' << a.id_ << ':' << a.origin_ << ", " << b.id_ << ':'
+        << b.origin_;
+    g_log->push_back(oss.str());
+  }
+};
+
+std::vector<std::string>* Logger::g_log = nullptr;
+int Logger::g_next_id = 0;
+
+// Append all the other args to the vector pointed to by the first arg.
+template <typename T>
+void VectorAppend(std::vector<T>* v) {}
+template <typename T, typename... Ts>
+void VectorAppend(std::vector<T>* v, const T& e, Ts... es) {
+  v->push_back(e);
+  VectorAppend(v, es...);
+}
+
+// Create a vector of strings. Because we're not allowed to use
+// std::initializer_list.
+template <typename... Ts>
+std::vector<std::string> V(Ts... es) {
+  std::vector<std::string> strings;
+  VectorAppend(&strings, static_cast<std::string>(es)...);
+  return strings;
+}
+
+}  // namespace
+
+TEST(OptionalTest, TestConstructDefault) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    EXPECT_FALSE(x);
+    EXPECT_FALSE(x.has_value());
+  }
+  EXPECT_EQ(V(), *log);
+}
+
+TEST(OptionalTest, TestConstructCopyEmpty) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    EXPECT_FALSE(x);
+    EXPECT_FALSE(x.has_value());
+    auto y = x;
+    EXPECT_FALSE(y);
+    EXPECT_FALSE(y.has_value());
+  }
+  EXPECT_EQ(V(), *log);
+}
+
+TEST(OptionalTest, TestConstructCopyFull) {
+  auto log = Logger::Setup();
+  {
+    Logger a;
+    Optional<Logger> x(a);
+    EXPECT_TRUE(x);
+    EXPECT_TRUE(x.has_value());
+    log->push_back("---");
+    auto y = x;
+    EXPECT_TRUE(y);
+    EXPECT_TRUE(y.has_value());
+    log->push_back("---");
+  }
+  EXPECT_EQ(V("0:0. default constructor", "1:0. copy constructor (from 0:0)",
+              "---", "2:0. copy constructor (from 1:0)", "---",
+              "2:0. destructor", "1:0. destructor", "0:0. destructor"),
+            *log);
+}
+
+TEST(OptionalTest, TestConstructMoveEmpty) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    EXPECT_FALSE(x);
+    EXPECT_FALSE(x.has_value());
+    auto y = std::move(x);
+    EXPECT_FALSE(y);
+    EXPECT_FALSE(y.has_value());
+  }
+  EXPECT_EQ(V(), *log);
+}
+
+TEST(OptionalTest, TestConstructMoveFull) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    EXPECT_TRUE(x);
+    EXPECT_TRUE(x.has_value());
+    log->push_back("---");
+    auto y = std::move(x);
+    EXPECT_TRUE(x);
+    EXPECT_TRUE(x.has_value());
+    EXPECT_TRUE(y);
+    EXPECT_TRUE(y.has_value());
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "---", "2:17. move constructor (from 1:17)", "---",
+        "2:17. destructor", "1:17. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestCopyAssignToEmptyFromEmpty) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x, y;
+    x = y;
+  }
+  EXPECT_EQ(V(), *log);
+}
+
+TEST(OptionalTest, TestCopyAssignToFullFromEmpty) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    Optional<Logger> y;
+    log->push_back("---");
+    x = y;
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "---", "1:17. destructor", "---"),
+      *log);
+}
+
+TEST(OptionalTest, TestCopyAssignToEmptyFromFull) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    Optional<Logger> y(Logger(17));
+    log->push_back("---");
+    x = y;
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "---", "2:17. copy constructor (from 1:17)", "---",
+        "1:17. destructor", "2:17. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestCopyAssignToFullFromFull) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    Optional<Logger> y(Logger(42));
+    log->push_back("---");
+    x = y;
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "2:42. explicit constructor",
+        "3:42. move constructor (from 2:42)", "2:42. destructor", "---",
+        "1:42. operator= copy (from 3:42)", "---", "3:42. destructor",
+        "1:42. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestCopyAssignToEmptyFromT) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    Logger y(17);
+    log->push_back("---");
+    x = Optional<Logger>(y);
+    log->push_back("---");
+  }
+  EXPECT_EQ(V("0:17. explicit constructor", "---",
+              "1:17. copy constructor (from 0:17)",
+              "2:17. move constructor (from 1:17)", "1:17. destructor", "---",
+              "0:17. destructor", "2:17. destructor"),
+            *log);
+}
+
+TEST(OptionalTest, TestCopyAssignToFullFromT) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    Logger y(42);
+    log->push_back("---");
+    x = Optional<Logger>(y);
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "2:42. explicit constructor", "---",
+        "3:42. copy constructor (from 2:42)",
+        "1:42. operator= move (from 3:42)", "3:42. destructor", "---",
+        "2:42. destructor", "1:42. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestMoveAssignToEmptyFromEmpty) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x, y;
+    x = std::move(y);
+  }
+  EXPECT_EQ(V(), *log);
+}
+
+TEST(OptionalTest, TestMoveAssignToFullFromEmpty) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    Optional<Logger> y;
+    log->push_back("---");
+    x = std::move(y);
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "---", "1:17. destructor", "---"),
+      *log);
+}
+
+TEST(OptionalTest, TestMoveAssignToEmptyFromFull) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    Optional<Logger> y(Logger(17));
+    log->push_back("---");
+    x = std::move(y);
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "---", "2:17. move constructor (from 1:17)", "---",
+        "1:17. destructor", "2:17. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestMoveAssignToFullFromFull) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    Optional<Logger> y(Logger(42));
+    log->push_back("---");
+    x = std::move(y);
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "2:42. explicit constructor",
+        "3:42. move constructor (from 2:42)", "2:42. destructor", "---",
+        "1:42. operator= move (from 3:42)", "---", "3:42. destructor",
+        "1:42. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestMoveAssignToEmptyFromT) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    Logger y(17);
+    log->push_back("---");
+    x = Optional<Logger>(std::move(y));
+    log->push_back("---");
+  }
+  EXPECT_EQ(V("0:17. explicit constructor", "---",
+              "1:17. move constructor (from 0:17)",
+              "2:17. move constructor (from 1:17)", "1:17. destructor", "---",
+              "0:17. destructor", "2:17. destructor"),
+            *log);
+}
+
+TEST(OptionalTest, TestMoveAssignToFullFromT) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    Logger y(42);
+    log->push_back("---");
+    x = Optional<Logger>(std::move(y));
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "2:42. explicit constructor", "---",
+        "3:42. move constructor (from 2:42)",
+        "1:42. operator= move (from 3:42)", "3:42. destructor", "---",
+        "2:42. destructor", "1:42. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestResetEmpty) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    x.reset();
+  }
+  EXPECT_EQ(V(), *log);
+}
+
+TEST(OptionalTest, TestResetFull) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    log->push_back("---");
+    x.reset();
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
+        "0:17. destructor", "---", "1:17. destructor", "---"),
+      *log);
+}
+
+TEST(OptionalTest, TestEmplaceEmptyWithExplicit) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    log->push_back("---");
+    x.emplace(42);
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(V("---",
+              "0:42. explicit constructor",
+              "---",
+              "0:42. destructor"),
+            *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestEmplaceEmptyWithMultipleParameters) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    Logger ref(21);
+    Logger value(35);
+    log->push_back("---");
+    x.emplace(42, ref, std::move(value));
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(V("0:21. explicit constructor",
+              "1:35. explicit constructor",
+              "---",
+              "2:35. move constructor (from 1:35)",
+              "3:42. multi parameter constructor",
+              "2:35. destructor",
+              "---",
+              "1:35. destructor",
+              "0:21. destructor",
+              "3:42. destructor"),
+            *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestEmplaceEmptyWithCopy) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    Logger y(42);
+    log->push_back("---");
+    x.emplace(y);
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(V("0:42. explicit constructor",
+              "---",
+              "1:42. copy constructor (from 0:42)",
+              "---",
+              "0:42. destructor",
+              "1:42. destructor"),
+            *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestEmplaceEmptyWithMove) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x;
+    Logger y(42);
+    log->push_back("---");
+    x.emplace(std::move(y));
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(V("0:42. explicit constructor",
+              "---",
+              "1:42. move constructor (from 0:42)",
+              "---",
+              "0:42. destructor",
+              "1:42. destructor"),
+            *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestEmplaceFullWithExplicit) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    log->push_back("---");
+    x.emplace(42);
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(
+      V("0:17. explicit constructor",
+        "1:17. move constructor (from 0:17)",
+        "0:17. destructor",
+        "---",
+        "1:17. destructor",
+        "2:42. explicit constructor",
+        "---",
+        "2:42. destructor"),
+      *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestEmplaceFullWithMultipleParameters) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    Logger ref(21);
+    Logger value(35);
+    log->push_back("---");
+    x.emplace(42, ref, std::move(value));
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(V("0:17. explicit constructor",
+              "1:17. move constructor (from 0:17)",
+              "0:17. destructor",
+              "2:21. explicit constructor",
+              "3:35. explicit constructor",
+              "---",
+              "1:17. destructor",
+              "4:35. move constructor (from 3:35)",
+              "5:42. multi parameter constructor",
+              "4:35. destructor",
+              "---",
+              "3:35. destructor",
+              "2:21. destructor",
+              "5:42. destructor"),
+            *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestEmplaceFullWithCopy) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    Logger y(42);
+    log->push_back("---");
+    x.emplace(y);
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(V("0:17. explicit constructor",
+              "1:17. move constructor (from 0:17)",
+              "0:17. destructor",
+              "2:42. explicit constructor",
+              "---",
+              "1:17. destructor",
+              "3:42. copy constructor (from 2:42)",
+              "---",
+              "2:42. destructor",
+              "3:42. destructor"),
+           *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestEmplaceFullWithMove) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(17));
+    Logger y(42);
+    log->push_back("---");
+    x.emplace(std::move(y));
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(V("0:17. explicit constructor",
+              "1:17. move constructor (from 0:17)",
+              "0:17. destructor",
+              "2:42. explicit constructor",
+              "---",
+              "1:17. destructor",
+              "3:42. move constructor (from 2:42)",
+              "---",
+              "2:42. destructor",
+              "3:42. destructor"),
+            *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestDereference) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(42));
+    const auto& y = x;
+    log->push_back("---");
+    x->Foo();
+    y->Foo();
+    std::move(x)->Foo();
+    std::move(y)->Foo();
+    log->push_back("---");
+    (*x).Foo();
+    (*y).Foo();
+    (*std::move(x)).Foo();
+    (*std::move(y)).Foo();
+    log->push_back("---");
+    x.value().Foo();
+    y.value().Foo();
+    std::move(x).value().Foo();
+    std::move(y).value().Foo();
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(V("0:42. explicit constructor",
+              "1:42. move constructor (from 0:42)",
+              "0:42. destructor",
+              "---",
+              "1:42. Foo()",
+              "1:42. Foo() const",
+              "1:42. Foo()",
+              "1:42. Foo() const",
+              "---",
+              "1:42. Foo()",
+              "1:42. Foo() const",
+              "1:42. Foo()",
+              "1:42. Foo() const",
+              "---",
+              "1:42. Foo()",
+              "1:42. Foo() const",
+              "1:42. Foo()",
+              "1:42. Foo() const",
+              "---",
+              "1:42. destructor"),
+            *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestDereferenceWithDefault) {
+  auto log = Logger::Setup();
+  {
+    const Logger a(17), b(42);
+    Optional<Logger> x(a);
+    Optional<Logger> y;
+    log->push_back("-1-");
+    EXPECT_EQ(a, x.value_or(Logger(42)));
+    log->push_back("-2-");
+    EXPECT_EQ(b, y.value_or(Logger(42)));
+    log->push_back("-3-");
+    EXPECT_EQ(a, Optional<Logger>(Logger(17)).value_or(b));
+    log->push_back("-4-");
+    EXPECT_EQ(b, Optional<Logger>().value_or(b));
+    log->push_back("-5-");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:42. explicit constructor",
+        "2:17. copy constructor (from 0:17)", "-1-",
+        "3:42. explicit constructor", "operator== 0:17, 2:17",
+        "3:42. destructor", "-2-", "4:42. explicit constructor",
+        "operator== 1:42, 4:42", "4:42. destructor", "-3-",
+        "5:17. explicit constructor", "6:17. move constructor (from 5:17)",
+        "operator== 0:17, 6:17", "6:17. destructor", "5:17. destructor", "-4-",
+        "operator== 1:42, 1:42", "-5-", "2:17. destructor", "1:42. destructor",
+        "0:17. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestEquality) {
+  auto log = Logger::Setup();
+  {
+    Logger a(17), b(42);
+    Optional<Logger> ma1(a), ma2(a), mb(b), me1, me2;
+    log->push_back("---");
+    EXPECT_EQ(ma1, ma1);
+    EXPECT_EQ(ma1, ma2);
+    EXPECT_NE(ma1, mb);
+    EXPECT_NE(ma1, me1);
+    EXPECT_EQ(me1, me1);
+    EXPECT_EQ(me1, me2);
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:17. explicit constructor", "1:42. explicit constructor",
+        "2:17. copy constructor (from 0:17)",
+        "3:17. copy constructor (from 0:17)",
+        "4:42. copy constructor (from 1:42)", "---", "operator== 2:17, 2:17",
+        "operator== 2:17, 3:17", "operator!= 2:17, 4:42", "---",
+        "4:42. destructor", "3:17. destructor", "2:17. destructor",
+        "1:42. destructor", "0:17. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestEqualityWithObject) {
+  auto log = Logger::Setup();
+  {
+    Logger a(17), b(42);
+    Optional<Logger> ma(a), me;
+    // Using operator== and operator!= explicetly instead of EXPECT_EQ/EXPECT_NE
+    // macros because those operators are under test.
+    log->push_back("---");
+
+    EXPECT_TRUE(ma == a);
+    EXPECT_TRUE(a == ma);
+    EXPECT_FALSE(ma == b);
+    EXPECT_FALSE(b == ma);
+    EXPECT_FALSE(me == a);
+    EXPECT_FALSE(a == me);
+
+    EXPECT_FALSE(ma != a);
+    EXPECT_FALSE(a != ma);
+    EXPECT_TRUE(ma != b);
+    EXPECT_TRUE(b != ma);
+    EXPECT_TRUE(me != a);
+    EXPECT_TRUE(a != me);
+
+    log->push_back("---");
+  }
+  // clang-format off
+  EXPECT_EQ(V("0:17. explicit constructor",
+              "1:42. explicit constructor",
+              "2:17. copy constructor (from 0:17)",
+              "---",
+              "operator== 2:17, 0:17",
+              "operator== 0:17, 2:17",
+              "operator== 2:17, 1:42",
+              "operator== 1:42, 2:17",
+              // No operator should be called when comparing to empty.
+              "operator!= 2:17, 0:17",
+              "operator!= 0:17, 2:17",
+              "operator!= 2:17, 1:42",
+              "operator!= 1:42, 2:17",
+              // No operator should be called when comparing to empty.
+              "---",
+              "2:17. destructor",
+              "1:42. destructor",
+              "0:17. destructor"),
+            *log);
+  // clang-format on
+}
+
+TEST(OptionalTest, TestSwap) {
+  auto log = Logger::Setup();
+  {
+    Logger a(17), b(42);
+    Optional<Logger> x1(a), x2(b), y1(a), y2, z1, z2;
+    log->push_back("---");
+    swap(x1, x2);  // Swap full <-> full.
+    swap(y1, y2);  // Swap full <-> empty.
+    swap(z1, z2);  // Swap empty <-> empty.
+    log->push_back("---");
+  }
+  EXPECT_EQ(V("0:17. explicit constructor", "1:42. explicit constructor",
+              "2:17. copy constructor (from 0:17)",
+              "3:42. copy constructor (from 1:42)",
+              "4:17. copy constructor (from 0:17)", "---", "swap 2:42, 3:17",
+              "5:17. move constructor (from 4:17)", "4:17. destructor", "---",
+              "5:17. destructor", "3:17. destructor", "2:42. destructor",
+              "1:42. destructor", "0:17. destructor"),
+            *log);
+}
+
+TEST(OptionalTest, TestMoveValue) {
+  auto log = Logger::Setup();
+  {
+    Optional<Logger> x(Logger(42));
+    log->push_back("---");
+    Logger moved = x.MoveValue();
+    log->push_back("---");
+  }
+  EXPECT_EQ(
+      V("0:42. explicit constructor", "1:42. move constructor (from 0:42)",
+        "0:42. destructor", "---", "2:42. move constructor (from 1:42)", "---",
+        "2:42. destructor", "1:42. destructor"),
+      *log);
+}
+
+TEST(OptionalTest, TestPrintTo) {
+  constexpr char kEmptyOptionalMessage[] = "<empty optional>";
+  const Optional<MyUnprintableType> empty_unprintable;
+  const Optional<MyPrintableType> empty_printable;
+  const Optional<MyOstreamPrintableType> empty_ostream_printable;
+  EXPECT_EQ(kEmptyOptionalMessage, ::testing::PrintToString(empty_unprintable));
+  EXPECT_EQ(kEmptyOptionalMessage, ::testing::PrintToString(empty_printable));
+  EXPECT_EQ(kEmptyOptionalMessage,
+            ::testing::PrintToString(empty_ostream_printable));
+  EXPECT_NE("1", ::testing::PrintToString(Optional<MyUnprintableType>({1})));
+  EXPECT_NE("1", ::testing::PrintToString(Optional<MyPrintableType>({1})));
+  EXPECT_EQ("The value is 1",
+            ::testing::PrintToString(Optional<MyPrintableType>({1})));
+  EXPECT_EQ("1",
+            ::testing::PrintToString(Optional<MyOstreamPrintableType>({1})));
+}
+
+void UnusedFunctionWorkaround() {
+  // These are here to ensure we don't get warnings about ostream and PrintTo
+  // for MyPrintableType never getting called.
+  const MyPrintableType dont_warn{17};
+  const MyOstreamPrintableType dont_warn2{18};
+  std::stringstream sstr;
+  sstr << dont_warn;
+  PrintTo(dont_warn, &sstr);
+  sstr << dont_warn2;
+}
+
+}  // namespace rtc
diff --git a/base/optionsfile.cc b/base/optionsfile.cc
new file mode 100644
index 0000000..ae37877
--- /dev/null
+++ b/base/optionsfile.cc
@@ -0,0 +1,186 @@
+/*
+ *  Copyright 2008 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/optionsfile.h"
+
+#include <ctype.h>
+
+#include "webrtc/base/logging.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringencode.h"
+
+namespace rtc {
+
+OptionsFile::OptionsFile(const std::string &path) : path_(path) {
+}
+
+OptionsFile::~OptionsFile() = default;
+
+bool OptionsFile::Load() {
+  options_.clear();
+  // Open file.
+  FileStream stream;
+  int err;
+  if (!stream.Open(path_, "r", &err)) {
+    LOG_F(LS_WARNING) << "Could not open file, err=" << err;
+    // We do not consider this an error because we expect there to be no file
+    // until the user saves a setting.
+    return true;
+  }
+  // Read in all its data.
+  std::string line;
+  StreamResult res;
+  for (;;) {
+    res = stream.ReadLine(&line);
+    if (res != SR_SUCCESS) {
+      break;
+    }
+    size_t equals_pos = line.find('=');
+    if (equals_pos == std::string::npos) {
+      // We do not consider this an error. Instead we ignore the line and
+      // keep going.
+      LOG_F(LS_WARNING) << "Ignoring malformed line in " << path_;
+      continue;
+    }
+    std::string key(line, 0, equals_pos);
+    std::string value(line, equals_pos + 1, line.length() - (equals_pos + 1));
+    options_[key] = value;
+  }
+  if (res != SR_EOS) {
+    LOG_F(LS_ERROR) << "Error when reading from file";
+    return false;
+  } else {
+    return true;
+  }
+}
+
+bool OptionsFile::Save() {
+  // Open file.
+  FileStream stream;
+  int err;
+  if (!stream.Open(path_, "w", &err)) {
+    LOG_F(LS_ERROR) << "Could not open file, err=" << err;
+    return false;
+  }
+  // Write out all the data.
+  StreamResult res = SR_SUCCESS;
+  size_t written;
+  int error;
+  for (OptionsMap::const_iterator i = options_.begin(); i != options_.end();
+       ++i) {
+    res = stream.WriteAll(i->first.c_str(), i->first.length(), &written,
+        &error);
+    if (res != SR_SUCCESS) {
+      break;
+    }
+    res = stream.WriteAll("=", 1, &written, &error);
+    if (res != SR_SUCCESS) {
+      break;
+    }
+    res = stream.WriteAll(i->second.c_str(), i->second.length(), &written,
+        &error);
+    if (res != SR_SUCCESS) {
+      break;
+    }
+    res = stream.WriteAll("\n", 1, &written, &error);
+    if (res != SR_SUCCESS) {
+      break;
+    }
+  }
+  if (res != SR_SUCCESS) {
+    LOG_F(LS_ERROR) << "Unable to write to file";
+    return false;
+  } else {
+    return true;
+  }
+}
+
+bool OptionsFile::IsLegalName(const std::string &name) {
+  for (size_t pos = 0; pos < name.length(); ++pos) {
+    if (name[pos] == '\n' || name[pos] == '\\' || name[pos] == '=') {
+      // Illegal character.
+      LOG(LS_WARNING) << "Ignoring operation for illegal option " << name;
+      return false;
+    }
+  }
+  return true;
+}
+
+bool OptionsFile::IsLegalValue(const std::string &value) {
+  for (size_t pos = 0; pos < value.length(); ++pos) {
+    if (value[pos] == '\n' || value[pos] == '\\') {
+      // Illegal character.
+      LOG(LS_WARNING) << "Ignoring operation for illegal value " << value;
+      return false;
+    }
+  }
+  return true;
+}
+
+bool OptionsFile::GetStringValue(const std::string& option,
+                                 std::string *out_val) const {
+  LOG(LS_VERBOSE) << "OptionsFile::GetStringValue "
+                  << option;
+  if (!IsLegalName(option)) {
+    return false;
+  }
+  OptionsMap::const_iterator i = options_.find(option);
+  if (i == options_.end()) {
+    return false;
+  }
+  *out_val = i->second;
+  return true;
+}
+
+bool OptionsFile::GetIntValue(const std::string& option,
+                              int *out_val) const {
+  LOG(LS_VERBOSE) << "OptionsFile::GetIntValue "
+                  << option;
+  if (!IsLegalName(option)) {
+    return false;
+  }
+  OptionsMap::const_iterator i = options_.find(option);
+  if (i == options_.end()) {
+    return false;
+  }
+  return FromString(i->second, out_val);
+}
+
+bool OptionsFile::SetStringValue(const std::string& option,
+                                 const std::string& value) {
+  LOG(LS_VERBOSE) << "OptionsFile::SetStringValue "
+                  << option << ":" << value;
+  if (!IsLegalName(option) || !IsLegalValue(value)) {
+    return false;
+  }
+  options_[option] = value;
+  return true;
+}
+
+bool OptionsFile::SetIntValue(const std::string& option,
+                              int value) {
+  LOG(LS_VERBOSE) << "OptionsFile::SetIntValue "
+                  << option << ":" << value;
+  if (!IsLegalName(option)) {
+    return false;
+  }
+  return ToString(value, &options_[option]);
+}
+
+bool OptionsFile::RemoveValue(const std::string& option) {
+  LOG(LS_VERBOSE) << "OptionsFile::RemoveValue " << option;
+  if (!IsLegalName(option)) {
+    return false;
+  }
+  options_.erase(option);
+  return true;
+}
+
+}  // namespace rtc
diff --git a/base/optionsfile.h b/base/optionsfile.h
index e77fd8a..9eb484e 100644
--- a/base/optionsfile.h
+++ b/base/optionsfile.h
@@ -11,9 +11,40 @@
 #ifndef WEBRTC_BASE_OPTIONSFILE_H_
 #define WEBRTC_BASE_OPTIONSFILE_H_
 
+#include <map>
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/optionsfile.h"
+namespace rtc {
+
+// Implements storage of simple options in a text file on disk. This is
+// cross-platform, but it is intended mostly for Linux where there is no
+// first-class options storage system.
+class OptionsFile {
+ public:
+  OptionsFile(const std::string &path);
+  ~OptionsFile();
+
+  // Loads the file from disk, overwriting the in-memory values.
+  bool Load();
+  // Saves the contents in memory, overwriting the on-disk values.
+  bool Save();
+
+  bool GetStringValue(const std::string& option, std::string* out_val) const;
+  bool GetIntValue(const std::string& option, int* out_val) const;
+  bool SetStringValue(const std::string& option, const std::string& val);
+  bool SetIntValue(const std::string& option, int val);
+  bool RemoveValue(const std::string& option);
+
+ private:
+  typedef std::map<std::string, std::string> OptionsMap;
+
+  static bool IsLegalName(const std::string &name);
+  static bool IsLegalValue(const std::string &value);
+
+  std::string path_;
+  OptionsMap options_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_OPTIONSFILE_H_
diff --git a/base/optionsfile_unittest.cc b/base/optionsfile_unittest.cc
new file mode 100644
index 0000000..69bd719
--- /dev/null
+++ b/base/optionsfile_unittest.cc
@@ -0,0 +1,182 @@
+/*
+ *  Copyright 2008 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/optionsfile.h"
+#include "webrtc/base/pathutils.h"
+
+namespace rtc {
+
+static const std::string kTestOptionA = "test-option-a";
+static const std::string kTestOptionB = "test-option-b";
+static const std::string kTestString1 = "a string";
+static const std::string kTestString2 = "different string";
+static const std::string kOptionWithEquals = "foo=bar";
+static const std::string kOptionWithNewline = "foo\nbar";
+static const std::string kValueWithEquals = "baz=quux";
+static const std::string kValueWithNewline = "baz\nquux";
+static const std::string kEmptyString = "";
+static const char kOptionWithUtf8[] = {'O', 'p', 't', '\302', '\256', 'i', 'o',
+    'n', '\342', '\204', '\242', '\0'};  // Opt(R)io(TM).
+static const char kValueWithUtf8[] = {'V', 'a', 'l', '\302', '\256', 'v', 'e',
+    '\342', '\204', '\242', '\0'};  // Val(R)ue(TM).
+static int kTestInt1 = 12345;
+static int kTestInt2 = 67890;
+static int kNegInt = -634;
+static int kZero = 0;
+
+#if defined (WEBRTC_ANDROID)
+// Fails on Android: https://bugs.chromium.org/p/webrtc/issues/detail?id=4364.
+#define MAYBE_OptionsFileTest DISABLED_OptionsFileTest
+#else
+#define MAYBE_OptionsFileTest OptionsFileTest
+#endif
+
+class MAYBE_OptionsFileTest : public testing::Test {
+ public:
+  MAYBE_OptionsFileTest() {
+    Pathname dir;
+    RTC_CHECK(Filesystem::GetTemporaryFolder(dir, true, nullptr));
+    test_file_ = Filesystem::TempFilename(dir, ".testfile");
+    OpenStore();
+  }
+
+  ~MAYBE_OptionsFileTest() override {
+    remove(test_file_.c_str());
+  }
+
+ protected:
+  void OpenStore() {
+    store_.reset(new OptionsFile(test_file_));
+  }
+
+  std::unique_ptr<OptionsFile> store_;
+
+ private:
+  std::string test_file_;
+};
+
+TEST_F(MAYBE_OptionsFileTest, GetSetString) {
+  // Clear contents of the file on disk.
+  EXPECT_TRUE(store_->Save());
+  std::string out1, out2;
+  EXPECT_FALSE(store_->GetStringValue(kTestOptionA, &out1));
+  EXPECT_FALSE(store_->GetStringValue(kTestOptionB, &out2));
+  EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kTestString1));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->SetStringValue(kTestOptionB, kTestString2));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out1));
+  EXPECT_TRUE(store_->GetStringValue(kTestOptionB, &out2));
+  EXPECT_EQ(kTestString1, out1);
+  EXPECT_EQ(kTestString2, out2);
+  EXPECT_TRUE(store_->RemoveValue(kTestOptionA));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->RemoveValue(kTestOptionB));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_FALSE(store_->GetStringValue(kTestOptionA, &out1));
+  EXPECT_FALSE(store_->GetStringValue(kTestOptionB, &out2));
+}
+
+TEST_F(MAYBE_OptionsFileTest, GetSetInt) {
+  // Clear contents of the file on disk.
+  EXPECT_TRUE(store_->Save());
+  int out1, out2;
+  EXPECT_FALSE(store_->GetIntValue(kTestOptionA, &out1));
+  EXPECT_FALSE(store_->GetIntValue(kTestOptionB, &out2));
+  EXPECT_TRUE(store_->SetIntValue(kTestOptionA, kTestInt1));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->SetIntValue(kTestOptionB, kTestInt2));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->GetIntValue(kTestOptionA, &out1));
+  EXPECT_TRUE(store_->GetIntValue(kTestOptionB, &out2));
+  EXPECT_EQ(kTestInt1, out1);
+  EXPECT_EQ(kTestInt2, out2);
+  EXPECT_TRUE(store_->RemoveValue(kTestOptionA));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->RemoveValue(kTestOptionB));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_FALSE(store_->GetIntValue(kTestOptionA, &out1));
+  EXPECT_FALSE(store_->GetIntValue(kTestOptionB, &out2));
+  EXPECT_TRUE(store_->SetIntValue(kTestOptionA, kNegInt));
+  EXPECT_TRUE(store_->GetIntValue(kTestOptionA, &out1));
+  EXPECT_EQ(kNegInt, out1);
+  EXPECT_TRUE(store_->SetIntValue(kTestOptionA, kZero));
+  EXPECT_TRUE(store_->GetIntValue(kTestOptionA, &out1));
+  EXPECT_EQ(kZero, out1);
+}
+
+TEST_F(MAYBE_OptionsFileTest, Persist) {
+  // Clear contents of the file on disk.
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kTestString1));
+  EXPECT_TRUE(store_->SetIntValue(kTestOptionB, kNegInt));
+  EXPECT_TRUE(store_->Save());
+
+  // Load the saved contents from above.
+  OpenStore();
+  EXPECT_TRUE(store_->Load());
+  std::string out1;
+  int out2;
+  EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out1));
+  EXPECT_TRUE(store_->GetIntValue(kTestOptionB, &out2));
+  EXPECT_EQ(kTestString1, out1);
+  EXPECT_EQ(kNegInt, out2);
+}
+
+TEST_F(MAYBE_OptionsFileTest, SpecialCharacters) {
+  // Clear contents of the file on disk.
+  EXPECT_TRUE(store_->Save());
+  std::string out;
+  EXPECT_FALSE(store_->SetStringValue(kOptionWithEquals, kTestString1));
+  EXPECT_FALSE(store_->GetStringValue(kOptionWithEquals, &out));
+  EXPECT_FALSE(store_->SetStringValue(kOptionWithNewline, kTestString1));
+  EXPECT_FALSE(store_->GetStringValue(kOptionWithNewline, &out));
+  EXPECT_TRUE(store_->SetStringValue(kOptionWithUtf8, kValueWithUtf8));
+  EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kTestString1));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out));
+  EXPECT_EQ(kTestString1, out);
+  EXPECT_TRUE(store_->GetStringValue(kOptionWithUtf8, &out));
+  EXPECT_EQ(kValueWithUtf8, out);
+  EXPECT_FALSE(store_->SetStringValue(kTestOptionA, kValueWithNewline));
+  EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out));
+  EXPECT_EQ(kTestString1, out);
+  EXPECT_TRUE(store_->SetStringValue(kTestOptionA, kValueWithEquals));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->GetStringValue(kTestOptionA, &out));
+  EXPECT_EQ(kValueWithEquals, out);
+  EXPECT_TRUE(store_->SetStringValue(kEmptyString, kTestString2));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->GetStringValue(kEmptyString, &out));
+  EXPECT_EQ(kTestString2, out);
+  EXPECT_TRUE(store_->SetStringValue(kTestOptionB, kEmptyString));
+  EXPECT_TRUE(store_->Save());
+  EXPECT_TRUE(store_->Load());
+  EXPECT_TRUE(store_->GetStringValue(kTestOptionB, &out));
+  EXPECT_EQ(kEmptyString, out);
+}
+
+}  // namespace rtc
diff --git a/base/pathutils.cc b/base/pathutils.cc
new file mode 100644
index 0000000..3036774
--- /dev/null
+++ b/base/pathutils.cc
@@ -0,0 +1,189 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#include <shellapi.h>
+#include <shlobj.h>
+#include <tchar.h>
+#endif  // WEBRTC_WIN
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+
+static const char EMPTY_STR[] = "";
+
+// EXT_DELIM separates a file basename from extension
+const char EXT_DELIM = '.';
+
+// FOLDER_DELIMS separate folder segments and the filename
+const char* const FOLDER_DELIMS = "/\\";
+
+// DEFAULT_FOLDER_DELIM is the preferred delimiter for this platform
+#ifdef WEBRTC_WIN
+const char DEFAULT_FOLDER_DELIM = '\\';
+#else  // !WEBRTC_WIN
+const char DEFAULT_FOLDER_DELIM = '/';
+#endif  // !WEBRTC_WIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Pathname - parsing of pathnames into components, and vice versa
+///////////////////////////////////////////////////////////////////////////////
+
+bool Pathname::IsFolderDelimiter(char ch) {
+  return (nullptr != ::strchr(FOLDER_DELIMS, ch));
+}
+
+char Pathname::DefaultFolderDelimiter() {
+  return DEFAULT_FOLDER_DELIM;
+}
+
+Pathname::Pathname()
+    : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
+}
+
+Pathname::Pathname(const Pathname&) = default;
+Pathname::Pathname(Pathname&&) = default;
+
+Pathname::Pathname(const std::string& pathname)
+    : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
+  SetPathname(pathname);
+}
+
+Pathname::Pathname(const std::string& folder, const std::string& filename)
+    : folder_delimiter_(DEFAULT_FOLDER_DELIM) {
+  SetPathname(folder, filename);
+}
+
+Pathname& Pathname::operator=(const Pathname&) = default;
+Pathname& Pathname::operator=(Pathname&&) = default;
+
+void Pathname::Normalize() {
+  for (size_t i=0; i<folder_.length(); ++i) {
+    if (IsFolderDelimiter(folder_[i])) {
+      folder_[i] = folder_delimiter_;
+    }
+  }
+}
+
+void Pathname::clear() {
+  folder_.clear();
+  basename_.clear();
+  extension_.clear();
+}
+
+bool Pathname::empty() const {
+  return folder_.empty() && basename_.empty() && extension_.empty();
+}
+
+std::string Pathname::pathname() const {
+  std::string pathname(folder_);
+  pathname.append(basename_);
+  pathname.append(extension_);
+  if (pathname.empty()) {
+    // Instead of the empty pathname, return the current working directory.
+    pathname.push_back('.');
+    pathname.push_back(folder_delimiter_);
+  }
+  return pathname;
+}
+
+void Pathname::SetPathname(const std::string& pathname) {
+  std::string::size_type pos = pathname.find_last_of(FOLDER_DELIMS);
+  if (pos != std::string::npos) {
+    SetFolder(pathname.substr(0, pos + 1));
+    SetFilename(pathname.substr(pos + 1));
+  } else {
+    SetFolder(EMPTY_STR);
+    SetFilename(pathname);
+  }
+}
+
+void Pathname::SetPathname(const std::string& folder,
+                           const std::string& filename) {
+  SetFolder(folder);
+  SetFilename(filename);
+}
+
+std::string Pathname::folder() const {
+  return folder_;
+}
+
+std::string Pathname::parent_folder() const {
+  std::string::size_type pos = std::string::npos;
+  if (folder_.size() >= 2) {
+    pos = folder_.find_last_of(FOLDER_DELIMS, folder_.length() - 2);
+  }
+  if (pos != std::string::npos) {
+    return folder_.substr(0, pos + 1);
+  } else {
+    return EMPTY_STR;
+  }
+}
+
+void Pathname::SetFolder(const std::string& folder) {
+  folder_.assign(folder);
+  // Ensure folder ends in a path delimiter
+  if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) {
+    folder_.push_back(folder_delimiter_);
+  }
+}
+
+void Pathname::AppendFolder(const std::string& folder) {
+  folder_.append(folder);
+  // Ensure folder ends in a path delimiter
+  if (!folder_.empty() && !IsFolderDelimiter(folder_[folder_.length()-1])) {
+    folder_.push_back(folder_delimiter_);
+  }
+}
+
+bool Pathname::SetBasename(const std::string& basename) {
+  if(basename.find_first_of(FOLDER_DELIMS) != std::string::npos) {
+    return false;
+  }
+  basename_.assign(basename);
+  return true;
+}
+
+bool Pathname::SetExtension(const std::string& extension) {
+  if (extension.find_first_of(FOLDER_DELIMS) != std::string::npos ||
+    extension.find_first_of(EXT_DELIM, 1) != std::string::npos) {
+      return false;
+  }
+  extension_.assign(extension);
+  // Ensure extension begins with the extension delimiter
+  if (!extension_.empty() && (extension_[0] != EXT_DELIM)) {
+    extension_.insert(extension_.begin(), EXT_DELIM);
+  }
+  return true;
+}
+
+std::string Pathname::filename() const {
+  std::string filename(basename_);
+  filename.append(extension_);
+  return filename;
+}
+
+bool Pathname::SetFilename(const std::string& filename) {
+  std::string::size_type pos = filename.rfind(EXT_DELIM);
+  if ((pos == std::string::npos) || (pos == 0)) {
+    return SetExtension(EMPTY_STR) && SetBasename(filename);
+  } else {
+    return SetExtension(filename.substr(pos)) && SetBasename(filename.substr(0, pos));
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace rtc
diff --git a/base/pathutils.h b/base/pathutils.h
index b45ca04..ff09069 100644
--- a/base/pathutils.h
+++ b/base/pathutils.h
@@ -8,12 +8,86 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_PATHUTILS_H_
-#define WEBRTC_BASE_PATHUTILS_H_
+#ifndef WEBRTC_BASE_PATHUTILS_H__
+#define WEBRTC_BASE_PATHUTILS_H__
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/pathutils.h"
+#include "webrtc/base/checks.h"
 
-#endif // WEBRTC_BASE_PATHUTILS_H_
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// Pathname - parsing of pathnames into components, and vice versa.
+//
+// To establish consistent terminology, a filename never contains a folder
+// component.  A folder never contains a filename.  A pathname may include
+// a folder and/or filename component.  Here are some examples:
+//
+//   pathname()      /home/john/example.txt
+//   folder()        /home/john/
+//   filename()                 example.txt
+//   parent_folder() /home/
+//   folder_name()         john/
+//   basename()                 example
+//   extension()                       .txt
+//
+// Basename may begin, end, and/or include periods, but no folder delimiters.
+// If extension exists, it consists of a period followed by zero or more
+// non-period/non-delimiter characters, and basename is non-empty.
+///////////////////////////////////////////////////////////////////////////////
+
+class Pathname {
+public:
+  // Folder delimiters are slash and backslash
+  static bool IsFolderDelimiter(char ch);
+  static char DefaultFolderDelimiter();
+
+  Pathname();
+  Pathname(const Pathname&);
+  Pathname(Pathname&&);
+  Pathname(const std::string& pathname);
+  Pathname(const std::string& folder, const std::string& filename);
+
+  Pathname& operator=(const Pathname&);
+  Pathname& operator=(Pathname&&);
+
+  // Normalize changes all folder delimiters to folder_delimiter()
+  void Normalize();
+
+  // Reset to the empty pathname
+  void clear();
+
+  // Returns true if the pathname is empty.  Note: this->pathname().empty()
+  // is always false.
+  bool empty() const;
+
+  // Returns the folder and filename components.  If the pathname is empty,
+  // returns a string representing the current directory (as a relative path,
+  // i.e., ".").
+  std::string pathname() const;
+  void SetPathname(const std::string& pathname);
+  void SetPathname(const std::string& folder, const std::string& filename);
+
+  std::string folder() const;
+  std::string parent_folder() const;
+  // SetFolder and AppendFolder will append a folder delimiter, if needed.
+  void SetFolder(const std::string& folder);
+  void AppendFolder(const std::string& folder);
+
+  bool SetBasename(const std::string& basename);
+
+  // SetExtension will prefix a period, if needed.
+  bool SetExtension(const std::string& extension);
+
+  std::string filename() const;
+  bool SetFilename(const std::string& filename);
+
+private:
+  std::string folder_, basename_, extension_;
+  char folder_delimiter_;
+};
+
+}  // namespace rtc
+
+#endif // WEBRTC_BASE_PATHUTILS_H__
diff --git a/base/pathutils_unittest.cc b/base/pathutils_unittest.cc
new file mode 100644
index 0000000..b99bc16
--- /dev/null
+++ b/base/pathutils_unittest.cc
@@ -0,0 +1,45 @@
+/*
+ *  Copyright 2007 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/gunit.h"
+
+TEST(Pathname, ReturnsDotForEmptyPathname) {
+  const std::string kCWD =
+      std::string(".") + rtc::Pathname::DefaultFolderDelimiter();
+
+  rtc::Pathname path("/", "");
+  EXPECT_FALSE(path.empty());
+  EXPECT_FALSE(path.folder().empty());
+  EXPECT_TRUE (path.filename().empty());
+  EXPECT_FALSE(path.pathname().empty());
+  EXPECT_EQ(std::string("/"), path.pathname());
+
+  path.SetPathname("", "foo");
+  EXPECT_FALSE(path.empty());
+  EXPECT_TRUE (path.folder().empty());
+  EXPECT_FALSE(path.filename().empty());
+  EXPECT_FALSE(path.pathname().empty());
+  EXPECT_EQ(std::string("foo"), path.pathname());
+
+  path.SetPathname("", "");
+  EXPECT_TRUE (path.empty());
+  EXPECT_TRUE (path.folder().empty());
+  EXPECT_TRUE (path.filename().empty());
+  EXPECT_FALSE(path.pathname().empty());
+  EXPECT_EQ(kCWD, path.pathname());
+
+  path.SetPathname(kCWD, "");
+  EXPECT_FALSE(path.empty());
+  EXPECT_FALSE(path.folder().empty());
+  EXPECT_TRUE (path.filename().empty());
+  EXPECT_FALSE(path.pathname().empty());
+  EXPECT_EQ(kCWD, path.pathname());
+}
diff --git a/base/physicalsocketserver.cc b/base/physicalsocketserver.cc
new file mode 100644
index 0000000..655d397
--- /dev/null
+++ b/base/physicalsocketserver.cc
@@ -0,0 +1,1996 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/base/physicalsocketserver.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#ifdef MEMORY_SANITIZER
+#include <sanitizer/msan_interface.h>
+#endif
+
+#if defined(WEBRTC_POSIX)
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#if defined(WEBRTC_USE_EPOLL)
+// "poll" will be used to wait for the signal dispatcher.
+#include <poll.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include <signal.h>
+#endif
+
+#if defined(WEBRTC_WIN)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#undef SetPort
+#endif
+
+#include <algorithm>
+#include <map>
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/networkmonitor.h"
+#include "webrtc/base/nullsocketserver.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/base/win32socketinit.h"
+
+#if defined(WEBRTC_POSIX)
+#include <netinet/tcp.h>  // for TCP_NODELAY
+#define IP_MTU 14 // Until this is integrated from linux/in.h to netinet/in.h
+typedef void* SockOptArg;
+
+#endif  // WEBRTC_POSIX
+
+#if defined(WEBRTC_POSIX) && !defined(WEBRTC_MAC) && !defined(__native_client__)
+
+int64_t GetSocketRecvTimestamp(int socket) {
+  struct timeval tv_ioctl;
+  int ret = ioctl(socket, SIOCGSTAMP, &tv_ioctl);
+  if (ret != 0)
+    return -1;
+  int64_t timestamp =
+      rtc::kNumMicrosecsPerSec * static_cast<int64_t>(tv_ioctl.tv_sec) +
+      static_cast<int64_t>(tv_ioctl.tv_usec);
+  return timestamp;
+}
+
+#else
+
+int64_t GetSocketRecvTimestamp(int socket) {
+  return -1;
+}
+#endif
+
+#if defined(WEBRTC_WIN)
+typedef char* SockOptArg;
+#endif
+
+#if defined(WEBRTC_USE_EPOLL)
+// POLLRDHUP / EPOLLRDHUP are only defined starting with Linux 2.6.17.
+#if !defined(POLLRDHUP)
+#define POLLRDHUP 0x2000
+#endif
+#if !defined(EPOLLRDHUP)
+#define EPOLLRDHUP 0x2000
+#endif
+#endif
+
+namespace rtc {
+
+std::unique_ptr<SocketServer> SocketServer::CreateDefault() {
+#if defined(__native_client__)
+  return std::unique_ptr<SocketServer>(new rtc::NullSocketServer);
+#else
+  return std::unique_ptr<SocketServer>(new rtc::PhysicalSocketServer);
+#endif
+}
+
+#if defined(WEBRTC_WIN)
+// Standard MTUs, from RFC 1191
+const uint16_t PACKET_MAXIMUMS[] = {
+    65535,  // Theoretical maximum, Hyperchannel
+    32000,  // Nothing
+    17914,  // 16Mb IBM Token Ring
+    8166,   // IEEE 802.4
+    // 4464,   // IEEE 802.5 (4Mb max)
+    4352,   // FDDI
+    // 2048,   // Wideband Network
+    2002,   // IEEE 802.5 (4Mb recommended)
+    // 1536,   // Expermental Ethernet Networks
+    // 1500,   // Ethernet, Point-to-Point (default)
+    1492,   // IEEE 802.3
+    1006,   // SLIP, ARPANET
+    // 576,    // X.25 Networks
+    // 544,    // DEC IP Portal
+    // 512,    // NETBIOS
+    508,    // IEEE 802/Source-Rt Bridge, ARCNET
+    296,    // Point-to-Point (low delay)
+    68,     // Official minimum
+    0,      // End of list marker
+};
+
+static const int IP_HEADER_SIZE = 20u;
+static const int IPV6_HEADER_SIZE = 40u;
+static const int ICMP_HEADER_SIZE = 8u;
+static const int ICMP_PING_TIMEOUT_MILLIS = 10000u;
+#endif
+
+PhysicalSocket::PhysicalSocket(PhysicalSocketServer* ss, SOCKET s)
+  : ss_(ss), s_(s), error_(0),
+    state_((s == INVALID_SOCKET) ? CS_CLOSED : CS_CONNECTED),
+    resolver_(nullptr) {
+#if defined(WEBRTC_WIN)
+  // EnsureWinsockInit() ensures that winsock is initialized. The default
+  // version of this function doesn't do anything because winsock is
+  // initialized by constructor of a static object. If neccessary libjingle
+  // users can link it with a different version of this function by replacing
+  // win32socketinit.cc. See win32socketinit.cc for more details.
+  EnsureWinsockInit();
+#endif
+  if (s_ != INVALID_SOCKET) {
+    SetEnabledEvents(DE_READ | DE_WRITE);
+
+    int type = SOCK_STREAM;
+    socklen_t len = sizeof(type);
+    const int res =
+        getsockopt(s_, SOL_SOCKET, SO_TYPE, (SockOptArg)&type, &len);
+    RTC_DCHECK_EQ(0, res);
+    udp_ = (SOCK_DGRAM == type);
+  }
+}
+
+PhysicalSocket::~PhysicalSocket() {
+  Close();
+}
+
+bool PhysicalSocket::Create(int family, int type) {
+  Close();
+  s_ = ::socket(family, type, 0);
+  udp_ = (SOCK_DGRAM == type);
+  UpdateLastError();
+  if (udp_) {
+    SetEnabledEvents(DE_READ | DE_WRITE);
+  }
+  return s_ != INVALID_SOCKET;
+}
+
+SocketAddress PhysicalSocket::GetLocalAddress() const {
+  sockaddr_storage addr_storage = {0};
+  socklen_t addrlen = sizeof(addr_storage);
+  sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
+  int result = ::getsockname(s_, addr, &addrlen);
+  SocketAddress address;
+  if (result >= 0) {
+    SocketAddressFromSockAddrStorage(addr_storage, &address);
+  } else {
+    LOG(LS_WARNING) << "GetLocalAddress: unable to get local addr, socket="
+                    << s_;
+  }
+  return address;
+}
+
+SocketAddress PhysicalSocket::GetRemoteAddress() const {
+  sockaddr_storage addr_storage = {0};
+  socklen_t addrlen = sizeof(addr_storage);
+  sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
+  int result = ::getpeername(s_, addr, &addrlen);
+  SocketAddress address;
+  if (result >= 0) {
+    SocketAddressFromSockAddrStorage(addr_storage, &address);
+  } else {
+    LOG(LS_WARNING) << "GetRemoteAddress: unable to get remote addr, socket="
+                    << s_;
+  }
+  return address;
+}
+
+int PhysicalSocket::Bind(const SocketAddress& bind_addr) {
+  SocketAddress copied_bind_addr = bind_addr;
+  // If a network binder is available, use it to bind a socket to an interface
+  // instead of bind(), since this is more reliable on an OS with a weak host
+  // model.
+  if (ss_->network_binder() && !bind_addr.IsAnyIP()) {
+    NetworkBindingResult result =
+        ss_->network_binder()->BindSocketToNetwork(s_, bind_addr.ipaddr());
+    if (result == NetworkBindingResult::SUCCESS) {
+      // Since the network binder handled binding the socket to the desired
+      // network interface, we don't need to (and shouldn't) include an IP in
+      // the bind() call; bind() just needs to assign a port.
+      copied_bind_addr.SetIP(GetAnyIP(copied_bind_addr.ipaddr().family()));
+    } else if (result == NetworkBindingResult::NOT_IMPLEMENTED) {
+      LOG(LS_INFO) << "Can't bind socket to network because "
+                      "network binding is not implemented for this OS.";
+    } else {
+      if (bind_addr.IsLoopbackIP()) {
+        // If we couldn't bind to a loopback IP (which should only happen in
+        // test scenarios), continue on. This may be expected behavior.
+        LOG(LS_VERBOSE) << "Binding socket to loopback address "
+                        << bind_addr.ipaddr().ToString()
+                        << " failed; result: " << static_cast<int>(result);
+      } else {
+        LOG(LS_WARNING) << "Binding socket to network address "
+                        << bind_addr.ipaddr().ToString()
+                        << " failed; result: " << static_cast<int>(result);
+        // If a network binding was attempted and failed, we should stop here
+        // and not try to use the socket. Otherwise, we may end up sending
+        // packets with an invalid source address.
+        // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=7026
+        return -1;
+      }
+    }
+  }
+  sockaddr_storage addr_storage;
+  size_t len = copied_bind_addr.ToSockAddrStorage(&addr_storage);
+  sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
+  int err = ::bind(s_, addr, static_cast<int>(len));
+  UpdateLastError();
+#if !defined(NDEBUG)
+  if (0 == err) {
+    dbg_addr_ = "Bound @ ";
+    dbg_addr_.append(GetLocalAddress().ToString());
+  }
+#endif
+  return err;
+}
+
+int PhysicalSocket::Connect(const SocketAddress& addr) {
+  // TODO(pthatcher): Implicit creation is required to reconnect...
+  // ...but should we make it more explicit?
+  if (state_ != CS_CLOSED) {
+    SetError(EALREADY);
+    return SOCKET_ERROR;
+  }
+  if (addr.IsUnresolvedIP()) {
+    LOG(LS_VERBOSE) << "Resolving addr in PhysicalSocket::Connect";
+    resolver_ = new AsyncResolver();
+    resolver_->SignalDone.connect(this, &PhysicalSocket::OnResolveResult);
+    resolver_->Start(addr);
+    state_ = CS_CONNECTING;
+    return 0;
+  }
+
+  return DoConnect(addr);
+}
+
+int PhysicalSocket::DoConnect(const SocketAddress& connect_addr) {
+  if ((s_ == INVALID_SOCKET) &&
+      !Create(connect_addr.family(), SOCK_STREAM)) {
+    return SOCKET_ERROR;
+  }
+  sockaddr_storage addr_storage;
+  size_t len = connect_addr.ToSockAddrStorage(&addr_storage);
+  sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
+  int err = ::connect(s_, addr, static_cast<int>(len));
+  UpdateLastError();
+  uint8_t events = DE_READ | DE_WRITE;
+  if (err == 0) {
+    state_ = CS_CONNECTED;
+  } else if (IsBlockingError(GetError())) {
+    state_ = CS_CONNECTING;
+    events |= DE_CONNECT;
+  } else {
+    return SOCKET_ERROR;
+  }
+
+  EnableEvents(events);
+  return 0;
+}
+
+int PhysicalSocket::GetError() const {
+  CritScope cs(&crit_);
+  return error_;
+}
+
+void PhysicalSocket::SetError(int error) {
+  CritScope cs(&crit_);
+  error_ = error;
+}
+
+AsyncSocket::ConnState PhysicalSocket::GetState() const {
+  return state_;
+}
+
+int PhysicalSocket::GetOption(Option opt, int* value) {
+  int slevel;
+  int sopt;
+  if (TranslateOption(opt, &slevel, &sopt) == -1)
+    return -1;
+  socklen_t optlen = sizeof(*value);
+  int ret = ::getsockopt(s_, slevel, sopt, (SockOptArg)value, &optlen);
+  if (ret != -1 && opt == OPT_DONTFRAGMENT) {
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+    *value = (*value != IP_PMTUDISC_DONT) ? 1 : 0;
+#endif
+  }
+  return ret;
+}
+
+int PhysicalSocket::SetOption(Option opt, int value) {
+  int slevel;
+  int sopt;
+  if (TranslateOption(opt, &slevel, &sopt) == -1)
+    return -1;
+  if (opt == OPT_DONTFRAGMENT) {
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+    value = (value) ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
+#endif
+  }
+  return ::setsockopt(s_, slevel, sopt, (SockOptArg)&value, sizeof(value));
+}
+
+int PhysicalSocket::Send(const void* pv, size_t cb) {
+  int sent = DoSend(s_, reinterpret_cast<const char *>(pv),
+      static_cast<int>(cb),
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+      // Suppress SIGPIPE. Without this, attempting to send on a socket whose
+      // other end is closed will result in a SIGPIPE signal being raised to
+      // our process, which by default will terminate the process, which we
+      // don't want. By specifying this flag, we'll just get the error EPIPE
+      // instead and can handle the error gracefully.
+      MSG_NOSIGNAL
+#else
+      0
+#endif
+      );
+  UpdateLastError();
+  MaybeRemapSendError();
+  // We have seen minidumps where this may be false.
+  RTC_DCHECK(sent <= static_cast<int>(cb));
+  if ((sent > 0 && sent < static_cast<int>(cb)) ||
+      (sent < 0 && IsBlockingError(GetError()))) {
+    EnableEvents(DE_WRITE);
+  }
+  return sent;
+}
+
+int PhysicalSocket::SendTo(const void* buffer,
+                           size_t length,
+                           const SocketAddress& addr) {
+  sockaddr_storage saddr;
+  size_t len = addr.ToSockAddrStorage(&saddr);
+  int sent = DoSendTo(
+      s_, static_cast<const char *>(buffer), static_cast<int>(length),
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+      // Suppress SIGPIPE. See above for explanation.
+      MSG_NOSIGNAL,
+#else
+      0,
+#endif
+      reinterpret_cast<sockaddr*>(&saddr), static_cast<int>(len));
+  UpdateLastError();
+  MaybeRemapSendError();
+  // We have seen minidumps where this may be false.
+  RTC_DCHECK(sent <= static_cast<int>(length));
+  if ((sent > 0 && sent < static_cast<int>(length)) ||
+      (sent < 0 && IsBlockingError(GetError()))) {
+    EnableEvents(DE_WRITE);
+  }
+  return sent;
+}
+
+int PhysicalSocket::Recv(void* buffer, size_t length, int64_t* timestamp) {
+  int received = ::recv(s_, static_cast<char*>(buffer),
+                        static_cast<int>(length), 0);
+  if ((received == 0) && (length != 0)) {
+    // Note: on graceful shutdown, recv can return 0.  In this case, we
+    // pretend it is blocking, and then signal close, so that simplifying
+    // assumptions can be made about Recv.
+    LOG(LS_WARNING) << "EOF from socket; deferring close event";
+    // Must turn this back on so that the select() loop will notice the close
+    // event.
+    EnableEvents(DE_READ);
+    SetError(EWOULDBLOCK);
+    return SOCKET_ERROR;
+  }
+  if (timestamp) {
+    *timestamp = GetSocketRecvTimestamp(s_);
+  }
+  UpdateLastError();
+  int error = GetError();
+  bool success = (received >= 0) || IsBlockingError(error);
+  if (udp_ || success) {
+    EnableEvents(DE_READ);
+  }
+  if (!success) {
+    LOG_F(LS_VERBOSE) << "Error = " << error;
+  }
+  return received;
+}
+
+int PhysicalSocket::RecvFrom(void* buffer,
+                             size_t length,
+                             SocketAddress* out_addr,
+                             int64_t* timestamp) {
+  sockaddr_storage addr_storage;
+  socklen_t addr_len = sizeof(addr_storage);
+  sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
+  int received = ::recvfrom(s_, static_cast<char*>(buffer),
+                            static_cast<int>(length), 0, addr, &addr_len);
+  if (timestamp) {
+    *timestamp = GetSocketRecvTimestamp(s_);
+  }
+  UpdateLastError();
+  if ((received >= 0) && (out_addr != nullptr))
+    SocketAddressFromSockAddrStorage(addr_storage, out_addr);
+  int error = GetError();
+  bool success = (received >= 0) || IsBlockingError(error);
+  if (udp_ || success) {
+    EnableEvents(DE_READ);
+  }
+  if (!success) {
+    LOG_F(LS_VERBOSE) << "Error = " << error;
+  }
+  return received;
+}
+
+int PhysicalSocket::Listen(int backlog) {
+  int err = ::listen(s_, backlog);
+  UpdateLastError();
+  if (err == 0) {
+    state_ = CS_CONNECTING;
+    EnableEvents(DE_ACCEPT);
+#if !defined(NDEBUG)
+    dbg_addr_ = "Listening @ ";
+    dbg_addr_.append(GetLocalAddress().ToString());
+#endif
+  }
+  return err;
+}
+
+AsyncSocket* PhysicalSocket::Accept(SocketAddress* out_addr) {
+  // Always re-subscribe DE_ACCEPT to make sure new incoming connections will
+  // trigger an event even if DoAccept returns an error here.
+  EnableEvents(DE_ACCEPT);
+  sockaddr_storage addr_storage;
+  socklen_t addr_len = sizeof(addr_storage);
+  sockaddr* addr = reinterpret_cast<sockaddr*>(&addr_storage);
+  SOCKET s = DoAccept(s_, addr, &addr_len);
+  UpdateLastError();
+  if (s == INVALID_SOCKET)
+    return nullptr;
+  if (out_addr != nullptr)
+    SocketAddressFromSockAddrStorage(addr_storage, out_addr);
+  return ss_->WrapSocket(s);
+}
+
+int PhysicalSocket::Close() {
+  if (s_ == INVALID_SOCKET)
+    return 0;
+  int err = ::closesocket(s_);
+  UpdateLastError();
+  s_ = INVALID_SOCKET;
+  state_ = CS_CLOSED;
+  SetEnabledEvents(0);
+  if (resolver_) {
+    resolver_->Destroy(false);
+    resolver_ = nullptr;
+  }
+  return err;
+}
+
+SOCKET PhysicalSocket::DoAccept(SOCKET socket,
+                                sockaddr* addr,
+                                socklen_t* addrlen) {
+  return ::accept(socket, addr, addrlen);
+}
+
+int PhysicalSocket::DoSend(SOCKET socket, const char* buf, int len, int flags) {
+  return ::send(socket, buf, len, flags);
+}
+
+int PhysicalSocket::DoSendTo(SOCKET socket,
+                             const char* buf,
+                             int len,
+                             int flags,
+                             const struct sockaddr* dest_addr,
+                             socklen_t addrlen) {
+  return ::sendto(socket, buf, len, flags, dest_addr, addrlen);
+}
+
+void PhysicalSocket::OnResolveResult(AsyncResolverInterface* resolver) {
+  if (resolver != resolver_) {
+    return;
+  }
+
+  int error = resolver_->GetError();
+  if (error == 0) {
+    error = DoConnect(resolver_->address());
+  } else {
+    Close();
+  }
+
+  if (error) {
+    SetError(error);
+    SignalCloseEvent(this, error);
+  }
+}
+
+void PhysicalSocket::UpdateLastError() {
+  SetError(LAST_SYSTEM_ERROR);
+}
+
+void PhysicalSocket::MaybeRemapSendError() {
+#if defined(WEBRTC_MAC)
+  // https://developer.apple.com/library/mac/documentation/Darwin/
+  // Reference/ManPages/man2/sendto.2.html
+  // ENOBUFS - The output queue for a network interface is full.
+  // This generally indicates that the interface has stopped sending,
+  // but may be caused by transient congestion.
+  if (GetError() == ENOBUFS) {
+    SetError(EWOULDBLOCK);
+  }
+#endif
+}
+
+void PhysicalSocket::SetEnabledEvents(uint8_t events) {
+  enabled_events_ = events;
+}
+
+void PhysicalSocket::EnableEvents(uint8_t events) {
+  enabled_events_ |= events;
+}
+
+void PhysicalSocket::DisableEvents(uint8_t events) {
+  enabled_events_ &= ~events;
+}
+
+int PhysicalSocket::TranslateOption(Option opt, int* slevel, int* sopt) {
+  switch (opt) {
+    case OPT_DONTFRAGMENT:
+#if defined(WEBRTC_WIN)
+      *slevel = IPPROTO_IP;
+      *sopt = IP_DONTFRAGMENT;
+      break;
+#elif defined(WEBRTC_MAC) || defined(BSD) || defined(__native_client__)
+      LOG(LS_WARNING) << "Socket::OPT_DONTFRAGMENT not supported.";
+      return -1;
+#elif defined(WEBRTC_POSIX)
+      *slevel = IPPROTO_IP;
+      *sopt = IP_MTU_DISCOVER;
+      break;
+#endif
+    case OPT_RCVBUF:
+      *slevel = SOL_SOCKET;
+      *sopt = SO_RCVBUF;
+      break;
+    case OPT_SNDBUF:
+      *slevel = SOL_SOCKET;
+      *sopt = SO_SNDBUF;
+      break;
+    case OPT_NODELAY:
+      *slevel = IPPROTO_TCP;
+      *sopt = TCP_NODELAY;
+      break;
+    case OPT_DSCP:
+      LOG(LS_WARNING) << "Socket::OPT_DSCP not supported.";
+      return -1;
+    case OPT_RTP_SENDTIME_EXTN_ID:
+      return -1;  // No logging is necessary as this not a OS socket option.
+    default:
+      RTC_NOTREACHED();
+      return -1;
+  }
+  return 0;
+}
+
+SocketDispatcher::SocketDispatcher(PhysicalSocketServer *ss)
+#if defined(WEBRTC_WIN)
+  : PhysicalSocket(ss), id_(0), signal_close_(false)
+#else
+  : PhysicalSocket(ss)
+#endif
+{
+}
+
+SocketDispatcher::SocketDispatcher(SOCKET s, PhysicalSocketServer *ss)
+#if defined(WEBRTC_WIN)
+  : PhysicalSocket(ss, s), id_(0), signal_close_(false)
+#else
+  : PhysicalSocket(ss, s)
+#endif
+{
+}
+
+SocketDispatcher::~SocketDispatcher() {
+  Close();
+}
+
+bool SocketDispatcher::Initialize() {
+  RTC_DCHECK(s_ != INVALID_SOCKET);
+  // Must be a non-blocking
+#if defined(WEBRTC_WIN)
+  u_long argp = 1;
+  ioctlsocket(s_, FIONBIO, &argp);
+#elif defined(WEBRTC_POSIX)
+  fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) | O_NONBLOCK);
+#endif
+#if defined(WEBRTC_IOS)
+  // iOS may kill sockets when the app is moved to the background
+  // (specifically, if the app doesn't use the "voip" UIBackgroundMode). When
+  // we attempt to write to such a socket, SIGPIPE will be raised, which by
+  // default will terminate the process, which we don't want. By specifying
+  // this socket option, SIGPIPE will be disabled for the socket.
+  int value = 1;
+  ::setsockopt(s_, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));
+#endif
+  ss_->Add(this);
+  return true;
+}
+
+bool SocketDispatcher::Create(int type) {
+  return Create(AF_INET, type);
+}
+
+bool SocketDispatcher::Create(int family, int type) {
+  // Change the socket to be non-blocking.
+  if (!PhysicalSocket::Create(family, type))
+    return false;
+
+  if (!Initialize())
+    return false;
+
+#if defined(WEBRTC_WIN)
+  do { id_ = ++next_id_; } while (id_ == 0);
+#endif
+  return true;
+}
+
+#if defined(WEBRTC_WIN)
+
+WSAEVENT SocketDispatcher::GetWSAEvent() {
+  return WSA_INVALID_EVENT;
+}
+
+SOCKET SocketDispatcher::GetSocket() {
+  return s_;
+}
+
+bool SocketDispatcher::CheckSignalClose() {
+  if (!signal_close_)
+    return false;
+
+  char ch;
+  if (recv(s_, &ch, 1, MSG_PEEK) > 0)
+    return false;
+
+  state_ = CS_CLOSED;
+  signal_close_ = false;
+  SignalCloseEvent(this, signal_err_);
+  return true;
+}
+
+int SocketDispatcher::next_id_ = 0;
+
+#elif defined(WEBRTC_POSIX)
+
+int SocketDispatcher::GetDescriptor() {
+  return s_;
+}
+
+bool SocketDispatcher::IsDescriptorClosed() {
+  if (udp_) {
+    // The MSG_PEEK trick doesn't work for UDP, since (at least in some
+    // circumstances) it requires reading an entire UDP packet, which would be
+    // bad for performance here. So, just check whether |s_| has been closed,
+    // which should be sufficient.
+    return s_ == INVALID_SOCKET;
+  }
+  // We don't have a reliable way of distinguishing end-of-stream
+  // from readability.  So test on each readable call.  Is this
+  // inefficient?  Probably.
+  char ch;
+  ssize_t res = ::recv(s_, &ch, 1, MSG_PEEK);
+  if (res > 0) {
+    // Data available, so not closed.
+    return false;
+  } else if (res == 0) {
+    // EOF, so closed.
+    return true;
+  } else {  // error
+    switch (errno) {
+      // Returned if we've already closed s_.
+      case EBADF:
+      // Returned during ungraceful peer shutdown.
+      case ECONNRESET:
+        return true;
+      // The normal blocking error; don't log anything.
+      case EWOULDBLOCK:
+      // Interrupted system call.
+      case EINTR:
+        return false;
+      default:
+        // Assume that all other errors are just blocking errors, meaning the
+        // connection is still good but we just can't read from it right now.
+        // This should only happen when connecting (and at most once), because
+        // in all other cases this function is only called if the file
+        // descriptor is already known to be in the readable state. However,
+        // it's not necessary a problem if we spuriously interpret a
+        // "connection lost"-type error as a blocking error, because typically
+        // the next recv() will get EOF, so we'll still eventually notice that
+        // the socket is closed.
+        LOG_ERR(LS_WARNING) << "Assuming benign blocking error";
+        return false;
+    }
+  }
+}
+
+#endif // WEBRTC_POSIX
+
+uint32_t SocketDispatcher::GetRequestedEvents() {
+  return enabled_events();
+}
+
+void SocketDispatcher::OnPreEvent(uint32_t ff) {
+  if ((ff & DE_CONNECT) != 0)
+    state_ = CS_CONNECTED;
+
+#if defined(WEBRTC_WIN)
+  // We set CS_CLOSED from CheckSignalClose.
+#elif defined(WEBRTC_POSIX)
+  if ((ff & DE_CLOSE) != 0)
+    state_ = CS_CLOSED;
+#endif
+}
+
+#if defined(WEBRTC_WIN)
+
+void SocketDispatcher::OnEvent(uint32_t ff, int err) {
+  int cache_id = id_;
+  // Make sure we deliver connect/accept first. Otherwise, consumers may see
+  // something like a READ followed by a CONNECT, which would be odd.
+  if (((ff & DE_CONNECT) != 0) && (id_ == cache_id)) {
+    if (ff != DE_CONNECT)
+      LOG(LS_VERBOSE) << "Signalled with DE_CONNECT: " << ff;
+    DisableEvents(DE_CONNECT);
+#if !defined(NDEBUG)
+    dbg_addr_ = "Connected @ ";
+    dbg_addr_.append(GetRemoteAddress().ToString());
+#endif
+    SignalConnectEvent(this);
+  }
+  if (((ff & DE_ACCEPT) != 0) && (id_ == cache_id)) {
+    DisableEvents(DE_ACCEPT);
+    SignalReadEvent(this);
+  }
+  if ((ff & DE_READ) != 0) {
+    DisableEvents(DE_READ);
+    SignalReadEvent(this);
+  }
+  if (((ff & DE_WRITE) != 0) && (id_ == cache_id)) {
+    DisableEvents(DE_WRITE);
+    SignalWriteEvent(this);
+  }
+  if (((ff & DE_CLOSE) != 0) && (id_ == cache_id)) {
+    signal_close_ = true;
+    signal_err_ = err;
+  }
+}
+
+#elif defined(WEBRTC_POSIX)
+
+void SocketDispatcher::OnEvent(uint32_t ff, int err) {
+#if defined(WEBRTC_USE_EPOLL)
+  // Remember currently enabled events so we can combine multiple changes
+  // into one update call later.
+  // The signal handlers might re-enable events disabled here, so we can't
+  // keep a list of events to disable at the end of the method. This list
+  // would not be updated with the events enabled by the signal handlers.
+  StartBatchedEventUpdates();
+#endif
+  // Make sure we deliver connect/accept first. Otherwise, consumers may see
+  // something like a READ followed by a CONNECT, which would be odd.
+  if ((ff & DE_CONNECT) != 0) {
+    DisableEvents(DE_CONNECT);
+    SignalConnectEvent(this);
+  }
+  if ((ff & DE_ACCEPT) != 0) {
+    DisableEvents(DE_ACCEPT);
+    SignalReadEvent(this);
+  }
+  if ((ff & DE_READ) != 0) {
+    DisableEvents(DE_READ);
+    SignalReadEvent(this);
+  }
+  if ((ff & DE_WRITE) != 0) {
+    DisableEvents(DE_WRITE);
+    SignalWriteEvent(this);
+  }
+  if ((ff & DE_CLOSE) != 0) {
+    // The socket is now dead to us, so stop checking it.
+    SetEnabledEvents(0);
+    SignalCloseEvent(this, err);
+  }
+#if defined(WEBRTC_USE_EPOLL)
+  FinishBatchedEventUpdates();
+#endif
+}
+
+#endif // WEBRTC_POSIX
+
+#if defined(WEBRTC_USE_EPOLL)
+
+static int GetEpollEvents(uint32_t ff) {
+  int events = 0;
+  if (ff & (DE_READ | DE_ACCEPT)) {
+    events |= EPOLLIN;
+  }
+  if (ff & (DE_WRITE | DE_CONNECT)) {
+    events |= EPOLLOUT;
+  }
+  return events;
+}
+
+void SocketDispatcher::StartBatchedEventUpdates() {
+  RTC_DCHECK_EQ(saved_enabled_events_, -1);
+  saved_enabled_events_ = enabled_events();
+}
+
+void SocketDispatcher::FinishBatchedEventUpdates() {
+  RTC_DCHECK_NE(saved_enabled_events_, -1);
+  uint8_t old_events = static_cast<uint8_t>(saved_enabled_events_);
+  saved_enabled_events_ = -1;
+  MaybeUpdateDispatcher(old_events);
+}
+
+void SocketDispatcher::MaybeUpdateDispatcher(uint8_t old_events) {
+  if (GetEpollEvents(enabled_events()) != GetEpollEvents(old_events) &&
+      saved_enabled_events_ == -1) {
+    ss_->Update(this);
+  }
+}
+
+void SocketDispatcher::SetEnabledEvents(uint8_t events) {
+  uint8_t old_events = enabled_events();
+  PhysicalSocket::SetEnabledEvents(events);
+  MaybeUpdateDispatcher(old_events);
+}
+
+void SocketDispatcher::EnableEvents(uint8_t events) {
+  uint8_t old_events = enabled_events();
+  PhysicalSocket::EnableEvents(events);
+  MaybeUpdateDispatcher(old_events);
+}
+
+void SocketDispatcher::DisableEvents(uint8_t events) {
+  uint8_t old_events = enabled_events();
+  PhysicalSocket::DisableEvents(events);
+  MaybeUpdateDispatcher(old_events);
+}
+
+#endif  // WEBRTC_USE_EPOLL
+
+int SocketDispatcher::Close() {
+  if (s_ == INVALID_SOCKET)
+    return 0;
+
+#if defined(WEBRTC_WIN)
+  id_ = 0;
+  signal_close_ = false;
+#endif
+  ss_->Remove(this);
+  return PhysicalSocket::Close();
+}
+
+#if defined(WEBRTC_POSIX)
+class EventDispatcher : public Dispatcher {
+ public:
+  EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) {
+    if (pipe(afd_) < 0)
+      LOG(LERROR) << "pipe failed";
+    ss_->Add(this);
+  }
+
+  ~EventDispatcher() override {
+    ss_->Remove(this);
+    close(afd_[0]);
+    close(afd_[1]);
+  }
+
+  virtual void Signal() {
+    CritScope cs(&crit_);
+    if (!fSignaled_) {
+      const uint8_t b[1] = {0};
+      const ssize_t res = write(afd_[1], b, sizeof(b));
+      RTC_DCHECK_EQ(1, res);
+      fSignaled_ = true;
+    }
+  }
+
+  uint32_t GetRequestedEvents() override { return DE_READ; }
+
+  void OnPreEvent(uint32_t ff) override {
+    // It is not possible to perfectly emulate an auto-resetting event with
+    // pipes.  This simulates it by resetting before the event is handled.
+
+    CritScope cs(&crit_);
+    if (fSignaled_) {
+      uint8_t b[4];  // Allow for reading more than 1 byte, but expect 1.
+      const ssize_t res = read(afd_[0], b, sizeof(b));
+      RTC_DCHECK_EQ(1, res);
+      fSignaled_ = false;
+    }
+  }
+
+  void OnEvent(uint32_t ff, int err) override { RTC_NOTREACHED(); }
+
+  int GetDescriptor() override { return afd_[0]; }
+
+  bool IsDescriptorClosed() override { return false; }
+
+ private:
+  PhysicalSocketServer *ss_;
+  int afd_[2];
+  bool fSignaled_;
+  CriticalSection crit_;
+};
+
+// These two classes use the self-pipe trick to deliver POSIX signals to our
+// select loop. This is the only safe, reliable, cross-platform way to do
+// non-trivial things with a POSIX signal in an event-driven program (until
+// proper pselect() implementations become ubiquitous).
+
+class PosixSignalHandler {
+ public:
+  // POSIX only specifies 32 signals, but in principle the system might have
+  // more and the programmer might choose to use them, so we size our array
+  // for 128.
+  static const int kNumPosixSignals = 128;
+
+  // There is just a single global instance. (Signal handlers do not get any
+  // sort of user-defined void * parameter, so they can't access anything that
+  // isn't global.)
+  static PosixSignalHandler* Instance() {
+    RTC_DEFINE_STATIC_LOCAL(PosixSignalHandler, instance, ());
+    return &instance;
+  }
+
+  // Returns true if the given signal number is set.
+  bool IsSignalSet(int signum) const {
+    RTC_DCHECK(signum < static_cast<int>(arraysize(received_signal_)));
+    if (signum < static_cast<int>(arraysize(received_signal_))) {
+      return received_signal_[signum];
+    } else {
+      return false;
+    }
+  }
+
+  // Clears the given signal number.
+  void ClearSignal(int signum) {
+    RTC_DCHECK(signum < static_cast<int>(arraysize(received_signal_)));
+    if (signum < static_cast<int>(arraysize(received_signal_))) {
+      received_signal_[signum] = false;
+    }
+  }
+
+  // Returns the file descriptor to monitor for signal events.
+  int GetDescriptor() const {
+    return afd_[0];
+  }
+
+  // This is called directly from our real signal handler, so it must be
+  // signal-handler-safe. That means it cannot assume anything about the
+  // user-level state of the process, since the handler could be executed at any
+  // time on any thread.
+  void OnPosixSignalReceived(int signum) {
+    if (signum >= static_cast<int>(arraysize(received_signal_))) {
+      // We don't have space in our array for this.
+      return;
+    }
+    // Set a flag saying we've seen this signal.
+    received_signal_[signum] = true;
+    // Notify application code that we got a signal.
+    const uint8_t b[1] = {0};
+    if (-1 == write(afd_[1], b, sizeof(b))) {
+      // Nothing we can do here. If there's an error somehow then there's
+      // nothing we can safely do from a signal handler.
+      // No, we can't even safely log it.
+      // But, we still have to check the return value here. Otherwise,
+      // GCC 4.4.1 complains ignoring return value. Even (void) doesn't help.
+      return;
+    }
+  }
+
+ private:
+  PosixSignalHandler() {
+    if (pipe(afd_) < 0) {
+      LOG_ERR(LS_ERROR) << "pipe failed";
+      return;
+    }
+    if (fcntl(afd_[0], F_SETFL, O_NONBLOCK) < 0) {
+      LOG_ERR(LS_WARNING) << "fcntl #1 failed";
+    }
+    if (fcntl(afd_[1], F_SETFL, O_NONBLOCK) < 0) {
+      LOG_ERR(LS_WARNING) << "fcntl #2 failed";
+    }
+    memset(const_cast<void *>(static_cast<volatile void *>(received_signal_)),
+           0,
+           sizeof(received_signal_));
+  }
+
+  ~PosixSignalHandler() {
+    int fd1 = afd_[0];
+    int fd2 = afd_[1];
+    // We clobber the stored file descriptor numbers here or else in principle
+    // a signal that happens to be delivered during application termination
+    // could erroneously write a zero byte to an unrelated file handle in
+    // OnPosixSignalReceived() if some other file happens to be opened later
+    // during shutdown and happens to be given the same file descriptor number
+    // as our pipe had. Unfortunately even with this precaution there is still a
+    // race where that could occur if said signal happens to be handled
+    // concurrently with this code and happens to have already read the value of
+    // afd_[1] from memory before we clobber it, but that's unlikely.
+    afd_[0] = -1;
+    afd_[1] = -1;
+    close(fd1);
+    close(fd2);
+  }
+
+  int afd_[2];
+  // These are boolean flags that will be set in our signal handler and read
+  // and cleared from Wait(). There is a race involved in this, but it is
+  // benign. The signal handler sets the flag before signaling the pipe, so
+  // we'll never end up blocking in select() while a flag is still true.
+  // However, if two of the same signal arrive close to each other then it's
+  // possible that the second time the handler may set the flag while it's still
+  // true, meaning that signal will be missed. But the first occurrence of it
+  // will still be handled, so this isn't a problem.
+  // Volatile is not necessary here for correctness, but this data _is_ volatile
+  // so I've marked it as such.
+  volatile uint8_t received_signal_[kNumPosixSignals];
+};
+
+class PosixSignalDispatcher : public Dispatcher {
+ public:
+  PosixSignalDispatcher(PhysicalSocketServer *owner) : owner_(owner) {
+    owner_->Add(this);
+  }
+
+  ~PosixSignalDispatcher() override {
+    owner_->Remove(this);
+  }
+
+  uint32_t GetRequestedEvents() override { return DE_READ; }
+
+  void OnPreEvent(uint32_t ff) override {
+    // Events might get grouped if signals come very fast, so we read out up to
+    // 16 bytes to make sure we keep the pipe empty.
+    uint8_t b[16];
+    ssize_t ret = read(GetDescriptor(), b, sizeof(b));
+    if (ret < 0) {
+      LOG_ERR(LS_WARNING) << "Error in read()";
+    } else if (ret == 0) {
+      LOG(LS_WARNING) << "Should have read at least one byte";
+    }
+  }
+
+  void OnEvent(uint32_t ff, int err) override {
+    for (int signum = 0; signum < PosixSignalHandler::kNumPosixSignals;
+         ++signum) {
+      if (PosixSignalHandler::Instance()->IsSignalSet(signum)) {
+        PosixSignalHandler::Instance()->ClearSignal(signum);
+        HandlerMap::iterator i = handlers_.find(signum);
+        if (i == handlers_.end()) {
+          // This can happen if a signal is delivered to our process at around
+          // the same time as we unset our handler for it. It is not an error
+          // condition, but it's unusual enough to be worth logging.
+          LOG(LS_INFO) << "Received signal with no handler: " << signum;
+        } else {
+          // Otherwise, execute our handler.
+          (*i->second)(signum);
+        }
+      }
+    }
+  }
+
+  int GetDescriptor() override {
+    return PosixSignalHandler::Instance()->GetDescriptor();
+  }
+
+  bool IsDescriptorClosed() override { return false; }
+
+  void SetHandler(int signum, void (*handler)(int)) {
+    handlers_[signum] = handler;
+  }
+
+  void ClearHandler(int signum) {
+    handlers_.erase(signum);
+  }
+
+  bool HasHandlers() {
+    return !handlers_.empty();
+  }
+
+ private:
+  typedef std::map<int, void (*)(int)> HandlerMap;
+
+  HandlerMap handlers_;
+  // Our owner.
+  PhysicalSocketServer *owner_;
+};
+
+#endif // WEBRTC_POSIX
+
+#if defined(WEBRTC_WIN)
+static uint32_t FlagsToEvents(uint32_t events) {
+  uint32_t ffFD = FD_CLOSE;
+  if (events & DE_READ)
+    ffFD |= FD_READ;
+  if (events & DE_WRITE)
+    ffFD |= FD_WRITE;
+  if (events & DE_CONNECT)
+    ffFD |= FD_CONNECT;
+  if (events & DE_ACCEPT)
+    ffFD |= FD_ACCEPT;
+  return ffFD;
+}
+
+class EventDispatcher : public Dispatcher {
+ public:
+  EventDispatcher(PhysicalSocketServer *ss) : ss_(ss) {
+    hev_ = WSACreateEvent();
+    if (hev_) {
+      ss_->Add(this);
+    }
+  }
+
+  ~EventDispatcher() {
+    if (hev_ != nullptr) {
+      ss_->Remove(this);
+      WSACloseEvent(hev_);
+      hev_ = nullptr;
+    }
+  }
+
+  virtual void Signal() {
+    if (hev_ != nullptr)
+      WSASetEvent(hev_);
+  }
+
+  virtual uint32_t GetRequestedEvents() { return 0; }
+
+  virtual void OnPreEvent(uint32_t ff) { WSAResetEvent(hev_); }
+
+  virtual void OnEvent(uint32_t ff, int err) {}
+
+  virtual WSAEVENT GetWSAEvent() {
+    return hev_;
+  }
+
+  virtual SOCKET GetSocket() {
+    return INVALID_SOCKET;
+  }
+
+  virtual bool CheckSignalClose() { return false; }
+
+private:
+  PhysicalSocketServer* ss_;
+  WSAEVENT hev_;
+};
+#endif  // WEBRTC_WIN
+
+// Sets the value of a boolean value to false when signaled.
+class Signaler : public EventDispatcher {
+ public:
+  Signaler(PhysicalSocketServer* ss, bool* pf)
+      : EventDispatcher(ss), pf_(pf) {
+  }
+  ~Signaler() override { }
+
+  void OnEvent(uint32_t ff, int err) override {
+    if (pf_)
+      *pf_ = false;
+  }
+
+ private:
+  bool *pf_;
+};
+
+PhysicalSocketServer::PhysicalSocketServer()
+    : fWait_(false) {
+#if defined(WEBRTC_USE_EPOLL)
+  // Since Linux 2.6.8, the size argument is ignored, but must be greater than
+  // zero. Before that the size served as hint to the kernel for the amount of
+  // space to initially allocate in internal data structures.
+  epoll_fd_ = epoll_create(FD_SETSIZE);
+  if (epoll_fd_ == -1) {
+    // Not an error, will fall back to "select" below.
+    LOG_E(LS_WARNING, EN, errno) << "epoll_create";
+    epoll_fd_ = INVALID_SOCKET;
+  }
+#endif
+  signal_wakeup_ = new Signaler(this, &fWait_);
+#if defined(WEBRTC_WIN)
+  socket_ev_ = WSACreateEvent();
+#endif
+}
+
+PhysicalSocketServer::~PhysicalSocketServer() {
+#if defined(WEBRTC_WIN)
+  WSACloseEvent(socket_ev_);
+#endif
+#if defined(WEBRTC_POSIX)
+  signal_dispatcher_.reset();
+#endif
+  delete signal_wakeup_;
+#if defined(WEBRTC_USE_EPOLL)
+  if (epoll_fd_ != INVALID_SOCKET) {
+    close(epoll_fd_);
+  }
+#endif
+  RTC_DCHECK(dispatchers_.empty());
+}
+
+void PhysicalSocketServer::WakeUp() {
+  signal_wakeup_->Signal();
+}
+
+Socket* PhysicalSocketServer::CreateSocket(int type) {
+  return CreateSocket(AF_INET, type);
+}
+
+Socket* PhysicalSocketServer::CreateSocket(int family, int type) {
+  PhysicalSocket* socket = new PhysicalSocket(this);
+  if (socket->Create(family, type)) {
+    return socket;
+  } else {
+    delete socket;
+    return nullptr;
+  }
+}
+
+AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int type) {
+  return CreateAsyncSocket(AF_INET, type);
+}
+
+AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int family, int type) {
+  SocketDispatcher* dispatcher = new SocketDispatcher(this);
+  if (dispatcher->Create(family, type)) {
+    return dispatcher;
+  } else {
+    delete dispatcher;
+    return nullptr;
+  }
+}
+
+AsyncSocket* PhysicalSocketServer::WrapSocket(SOCKET s) {
+  SocketDispatcher* dispatcher = new SocketDispatcher(s, this);
+  if (dispatcher->Initialize()) {
+    return dispatcher;
+  } else {
+    delete dispatcher;
+    return nullptr;
+  }
+}
+
+void PhysicalSocketServer::Add(Dispatcher *pdispatcher) {
+  CritScope cs(&crit_);
+  if (processing_dispatchers_) {
+    // A dispatcher is being added while a "Wait" call is processing the
+    // list of socket events.
+    // Defer adding to "dispatchers_" set until processing is done to avoid
+    // invalidating the iterator in "Wait".
+    pending_remove_dispatchers_.erase(pdispatcher);
+    pending_add_dispatchers_.insert(pdispatcher);
+  } else {
+    dispatchers_.insert(pdispatcher);
+  }
+#if defined(WEBRTC_USE_EPOLL)
+  if (epoll_fd_ != INVALID_SOCKET) {
+    AddEpoll(pdispatcher);
+  }
+#endif  // WEBRTC_USE_EPOLL
+}
+
+void PhysicalSocketServer::Remove(Dispatcher *pdispatcher) {
+  CritScope cs(&crit_);
+  if (processing_dispatchers_) {
+    // A dispatcher is being removed while a "Wait" call is processing the
+    // list of socket events.
+    // Defer removal from "dispatchers_" set until processing is done to avoid
+    // invalidating the iterator in "Wait".
+    if (!pending_add_dispatchers_.erase(pdispatcher) &&
+        dispatchers_.find(pdispatcher) == dispatchers_.end()) {
+      LOG(LS_WARNING) << "PhysicalSocketServer asked to remove a unknown "
+                      << "dispatcher, potentially from a duplicate call to "
+                      << "Add.";
+      return;
+    }
+
+    pending_remove_dispatchers_.insert(pdispatcher);
+  } else if (!dispatchers_.erase(pdispatcher)) {
+    LOG(LS_WARNING) << "PhysicalSocketServer asked to remove a unknown "
+                    << "dispatcher, potentially from a duplicate call to Add.";
+    return;
+  }
+#if defined(WEBRTC_USE_EPOLL)
+  if (epoll_fd_ != INVALID_SOCKET) {
+    RemoveEpoll(pdispatcher);
+  }
+#endif  // WEBRTC_USE_EPOLL
+}
+
+void PhysicalSocketServer::Update(Dispatcher* pdispatcher) {
+#if defined(WEBRTC_USE_EPOLL)
+  if (epoll_fd_ == INVALID_SOCKET) {
+    return;
+  }
+
+  CritScope cs(&crit_);
+  if (dispatchers_.find(pdispatcher) == dispatchers_.end()) {
+    return;
+  }
+
+  UpdateEpoll(pdispatcher);
+#endif
+}
+
+void PhysicalSocketServer::AddRemovePendingDispatchers() {
+  if (!pending_add_dispatchers_.empty()) {
+    for (Dispatcher* pdispatcher : pending_add_dispatchers_) {
+      dispatchers_.insert(pdispatcher);
+    }
+    pending_add_dispatchers_.clear();
+  }
+
+  if (!pending_remove_dispatchers_.empty()) {
+    for (Dispatcher* pdispatcher : pending_remove_dispatchers_) {
+      dispatchers_.erase(pdispatcher);
+    }
+    pending_remove_dispatchers_.clear();
+  }
+}
+
+#if defined(WEBRTC_POSIX)
+
+bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) {
+#if defined(WEBRTC_USE_EPOLL)
+  // We don't keep a dedicated "epoll" descriptor containing only the non-IO
+  // (i.e. signaling) dispatcher, so "poll" will be used instead of the default
+  // "select" to support sockets larger than FD_SETSIZE.
+  if (!process_io) {
+    return WaitPoll(cmsWait, signal_wakeup_);
+  } else if (epoll_fd_ != INVALID_SOCKET) {
+    return WaitEpoll(cmsWait);
+  }
+#endif
+  return WaitSelect(cmsWait, process_io);
+}
+
+static void ProcessEvents(Dispatcher* dispatcher,
+                          bool readable,
+                          bool writable,
+                          bool check_error) {
+  int errcode = 0;
+  // TODO(pthatcher): Should we set errcode if getsockopt fails?
+  if (check_error) {
+    socklen_t len = sizeof(errcode);
+    ::getsockopt(dispatcher->GetDescriptor(), SOL_SOCKET, SO_ERROR, &errcode,
+                 &len);
+  }
+
+  uint32_t ff = 0;
+
+  // Check readable descriptors. If we're waiting on an accept, signal
+  // that. Otherwise we're waiting for data, check to see if we're
+  // readable or really closed.
+  // TODO(pthatcher): Only peek at TCP descriptors.
+  if (readable) {
+    if (dispatcher->GetRequestedEvents() & DE_ACCEPT) {
+      ff |= DE_ACCEPT;
+    } else if (errcode || dispatcher->IsDescriptorClosed()) {
+      ff |= DE_CLOSE;
+    } else {
+      ff |= DE_READ;
+    }
+  }
+
+  // Check writable descriptors. If we're waiting on a connect, detect
+  // success versus failure by the reaped error code.
+  if (writable) {
+    if (dispatcher->GetRequestedEvents() & DE_CONNECT) {
+      if (!errcode) {
+        ff |= DE_CONNECT;
+      } else {
+        ff |= DE_CLOSE;
+      }
+    } else {
+      ff |= DE_WRITE;
+    }
+  }
+
+  // Tell the descriptor about the event.
+  if (ff != 0) {
+    dispatcher->OnPreEvent(ff);
+    dispatcher->OnEvent(ff, errcode);
+  }
+}
+
+bool PhysicalSocketServer::WaitSelect(int cmsWait, bool process_io) {
+  // Calculate timing information
+
+  struct timeval* ptvWait = nullptr;
+  struct timeval tvWait;
+  struct timeval tvStop;
+  if (cmsWait != kForever) {
+    // Calculate wait timeval
+    tvWait.tv_sec = cmsWait / 1000;
+    tvWait.tv_usec = (cmsWait % 1000) * 1000;
+    ptvWait = &tvWait;
+
+    // Calculate when to return in a timeval
+    gettimeofday(&tvStop, nullptr);
+    tvStop.tv_sec += tvWait.tv_sec;
+    tvStop.tv_usec += tvWait.tv_usec;
+    if (tvStop.tv_usec >= 1000000) {
+      tvStop.tv_usec -= 1000000;
+      tvStop.tv_sec += 1;
+    }
+  }
+
+  // Zero all fd_sets. Don't need to do this inside the loop since
+  // select() zeros the descriptors not signaled
+
+  fd_set fdsRead;
+  FD_ZERO(&fdsRead);
+  fd_set fdsWrite;
+  FD_ZERO(&fdsWrite);
+  // Explicitly unpoison these FDs on MemorySanitizer which doesn't handle the
+  // inline assembly in FD_ZERO.
+  // http://crbug.com/344505
+#ifdef MEMORY_SANITIZER
+  __msan_unpoison(&fdsRead, sizeof(fdsRead));
+  __msan_unpoison(&fdsWrite, sizeof(fdsWrite));
+#endif
+
+  fWait_ = true;
+
+  while (fWait_) {
+    int fdmax = -1;
+    {
+      CritScope cr(&crit_);
+      // TODO(jbauch): Support re-entrant waiting.
+      RTC_DCHECK(!processing_dispatchers_);
+      for (Dispatcher* pdispatcher : dispatchers_) {
+        // Query dispatchers for read and write wait state
+        RTC_DCHECK(pdispatcher);
+        if (!process_io && (pdispatcher != signal_wakeup_))
+          continue;
+        int fd = pdispatcher->GetDescriptor();
+        // "select"ing a file descriptor that is equal to or larger than
+        // FD_SETSIZE will result in undefined behavior.
+        RTC_DCHECK_LT(fd, FD_SETSIZE);
+        if (fd > fdmax)
+          fdmax = fd;
+
+        uint32_t ff = pdispatcher->GetRequestedEvents();
+        if (ff & (DE_READ | DE_ACCEPT))
+          FD_SET(fd, &fdsRead);
+        if (ff & (DE_WRITE | DE_CONNECT))
+          FD_SET(fd, &fdsWrite);
+      }
+    }
+
+    // Wait then call handlers as appropriate
+    // < 0 means error
+    // 0 means timeout
+    // > 0 means count of descriptors ready
+    int n = select(fdmax + 1, &fdsRead, &fdsWrite, nullptr, ptvWait);
+
+    // If error, return error.
+    if (n < 0) {
+      if (errno != EINTR) {
+        LOG_E(LS_ERROR, EN, errno) << "select";
+        return false;
+      }
+      // Else ignore the error and keep going. If this EINTR was for one of the
+      // signals managed by this PhysicalSocketServer, the
+      // PosixSignalDeliveryDispatcher will be in the signaled state in the next
+      // iteration.
+    } else if (n == 0) {
+      // If timeout, return success
+      return true;
+    } else {
+      // We have signaled descriptors
+      CritScope cr(&crit_);
+      processing_dispatchers_ = true;
+      for (Dispatcher* pdispatcher : dispatchers_) {
+        int fd = pdispatcher->GetDescriptor();
+
+        bool readable = FD_ISSET(fd, &fdsRead);
+        if (readable) {
+          FD_CLR(fd, &fdsRead);
+        }
+
+        bool writable = FD_ISSET(fd, &fdsWrite);
+        if (writable) {
+          FD_CLR(fd, &fdsWrite);
+        }
+
+        // The error code can be signaled through reads or writes.
+        ProcessEvents(pdispatcher, readable, writable, readable || writable);
+      }
+
+      processing_dispatchers_ = false;
+      // Process deferred dispatchers that have been added/removed while the
+      // events were handled above.
+      AddRemovePendingDispatchers();
+    }
+
+    // Recalc the time remaining to wait. Doing it here means it doesn't get
+    // calced twice the first time through the loop
+    if (ptvWait) {
+      ptvWait->tv_sec = 0;
+      ptvWait->tv_usec = 0;
+      struct timeval tvT;
+      gettimeofday(&tvT, nullptr);
+      if ((tvStop.tv_sec > tvT.tv_sec)
+          || ((tvStop.tv_sec == tvT.tv_sec)
+              && (tvStop.tv_usec > tvT.tv_usec))) {
+        ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec;
+        ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec;
+        if (ptvWait->tv_usec < 0) {
+          RTC_DCHECK(ptvWait->tv_sec > 0);
+          ptvWait->tv_usec += 1000000;
+          ptvWait->tv_sec -= 1;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+#if defined(WEBRTC_USE_EPOLL)
+
+// Initial number of events to process with one call to "epoll_wait".
+static const size_t kInitialEpollEvents = 128;
+
+// Maximum number of events to process with one call to "epoll_wait".
+static const size_t kMaxEpollEvents = 8192;
+
+void PhysicalSocketServer::AddEpoll(Dispatcher* pdispatcher) {
+  RTC_DCHECK(epoll_fd_ != INVALID_SOCKET);
+  int fd = pdispatcher->GetDescriptor();
+  RTC_DCHECK(fd != INVALID_SOCKET);
+  if (fd == INVALID_SOCKET) {
+    return;
+  }
+
+  struct epoll_event event = {0};
+  event.events = GetEpollEvents(pdispatcher->GetRequestedEvents());
+  event.data.ptr = pdispatcher;
+  int err = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &event);
+  RTC_DCHECK_EQ(err, 0);
+  if (err == -1) {
+    LOG_E(LS_ERROR, EN, errno) << "epoll_ctl EPOLL_CTL_ADD";
+  }
+}
+
+void PhysicalSocketServer::RemoveEpoll(Dispatcher* pdispatcher) {
+  RTC_DCHECK(epoll_fd_ != INVALID_SOCKET);
+  int fd = pdispatcher->GetDescriptor();
+  RTC_DCHECK(fd != INVALID_SOCKET);
+  if (fd == INVALID_SOCKET) {
+    return;
+  }
+
+  struct epoll_event event = {0};
+  int err = epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, &event);
+  RTC_DCHECK(err == 0 || errno == ENOENT);
+  if (err == -1) {
+    if (errno == ENOENT) {
+      // Socket has already been closed.
+      LOG_E(LS_VERBOSE, EN, errno) << "epoll_ctl EPOLL_CTL_DEL";
+    } else {
+      LOG_E(LS_ERROR, EN, errno) << "epoll_ctl EPOLL_CTL_DEL";
+    }
+  }
+}
+
+void PhysicalSocketServer::UpdateEpoll(Dispatcher* pdispatcher) {
+  RTC_DCHECK(epoll_fd_ != INVALID_SOCKET);
+  int fd = pdispatcher->GetDescriptor();
+  RTC_DCHECK(fd != INVALID_SOCKET);
+  if (fd == INVALID_SOCKET) {
+    return;
+  }
+
+  struct epoll_event event = {0};
+  event.events = GetEpollEvents(pdispatcher->GetRequestedEvents());
+  event.data.ptr = pdispatcher;
+  int err = epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, fd, &event);
+  RTC_DCHECK_EQ(err, 0);
+  if (err == -1) {
+    LOG_E(LS_ERROR, EN, errno) << "epoll_ctl EPOLL_CTL_MOD";
+  }
+}
+
+bool PhysicalSocketServer::WaitEpoll(int cmsWait) {
+  RTC_DCHECK(epoll_fd_ != INVALID_SOCKET);
+  int64_t tvWait = -1;
+  int64_t tvStop = -1;
+  if (cmsWait != kForever) {
+    tvWait = cmsWait;
+    tvStop = TimeAfter(cmsWait);
+  }
+
+  if (epoll_events_.empty()) {
+    // The initial space to receive events is created only if epoll is used.
+    epoll_events_.resize(kInitialEpollEvents);
+  }
+
+  fWait_ = true;
+
+  while (fWait_) {
+    // Wait then call handlers as appropriate
+    // < 0 means error
+    // 0 means timeout
+    // > 0 means count of descriptors ready
+    int n = epoll_wait(epoll_fd_, &epoll_events_[0],
+                       static_cast<int>(epoll_events_.size()),
+                       static_cast<int>(tvWait));
+    if (n < 0) {
+      if (errno != EINTR) {
+        LOG_E(LS_ERROR, EN, errno) << "epoll";
+        return false;
+      }
+      // Else ignore the error and keep going. If this EINTR was for one of the
+      // signals managed by this PhysicalSocketServer, the
+      // PosixSignalDeliveryDispatcher will be in the signaled state in the next
+      // iteration.
+    } else if (n == 0) {
+      // If timeout, return success
+      return true;
+    } else {
+      // We have signaled descriptors
+      CritScope cr(&crit_);
+      for (int i = 0; i < n; ++i) {
+        const epoll_event& event = epoll_events_[i];
+        Dispatcher* pdispatcher = static_cast<Dispatcher*>(event.data.ptr);
+        if (dispatchers_.find(pdispatcher) == dispatchers_.end()) {
+          // The dispatcher for this socket no longer exists.
+          continue;
+        }
+
+        bool readable = (event.events & (EPOLLIN | EPOLLPRI));
+        bool writable = (event.events & EPOLLOUT);
+        bool check_error = (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP));
+
+        ProcessEvents(pdispatcher, readable, writable, check_error);
+      }
+    }
+
+    if (static_cast<size_t>(n) == epoll_events_.size() &&
+        epoll_events_.size() < kMaxEpollEvents) {
+      // We used the complete space to receive events, increase size for future
+      // iterations.
+      epoll_events_.resize(std::max(epoll_events_.size() * 2, kMaxEpollEvents));
+    }
+
+    if (cmsWait != kForever) {
+      tvWait = TimeDiff(tvStop, TimeMillis());
+      if (tvWait < 0) {
+        // Return success on timeout.
+        return true;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool PhysicalSocketServer::WaitPoll(int cmsWait, Dispatcher* dispatcher) {
+  RTC_DCHECK(dispatcher);
+  int64_t tvWait = -1;
+  int64_t tvStop = -1;
+  if (cmsWait != kForever) {
+    tvWait = cmsWait;
+    tvStop = TimeAfter(cmsWait);
+  }
+
+  fWait_ = true;
+
+  struct pollfd fds = {0};
+  int fd = dispatcher->GetDescriptor();
+  fds.fd = fd;
+
+  while (fWait_) {
+    uint32_t ff = dispatcher->GetRequestedEvents();
+    fds.events = 0;
+    if (ff & (DE_READ | DE_ACCEPT)) {
+      fds.events |= POLLIN;
+    }
+    if (ff & (DE_WRITE | DE_CONNECT)) {
+      fds.events |= POLLOUT;
+    }
+    fds.revents = 0;
+
+    // Wait then call handlers as appropriate
+    // < 0 means error
+    // 0 means timeout
+    // > 0 means count of descriptors ready
+    int n = poll(&fds, 1, static_cast<int>(tvWait));
+    if (n < 0) {
+      if (errno != EINTR) {
+        LOG_E(LS_ERROR, EN, errno) << "poll";
+        return false;
+      }
+      // Else ignore the error and keep going. If this EINTR was for one of the
+      // signals managed by this PhysicalSocketServer, the
+      // PosixSignalDeliveryDispatcher will be in the signaled state in the next
+      // iteration.
+    } else if (n == 0) {
+      // If timeout, return success
+      return true;
+    } else {
+      // We have signaled descriptors (should only be the passed dispatcher).
+      RTC_DCHECK_EQ(n, 1);
+      RTC_DCHECK_EQ(fds.fd, fd);
+
+      bool readable = (fds.revents & (POLLIN | POLLPRI));
+      bool writable = (fds.revents & POLLOUT);
+      bool check_error = (fds.revents & (POLLRDHUP | POLLERR | POLLHUP));
+
+      ProcessEvents(dispatcher, readable, writable, check_error);
+    }
+
+    if (cmsWait != kForever) {
+      tvWait = TimeDiff(tvStop, TimeMillis());
+      if (tvWait < 0) {
+        // Return success on timeout.
+        return true;
+      }
+    }
+  }
+
+  return true;
+}
+
+#endif  // WEBRTC_USE_EPOLL
+
+static void GlobalSignalHandler(int signum) {
+  PosixSignalHandler::Instance()->OnPosixSignalReceived(signum);
+}
+
+bool PhysicalSocketServer::SetPosixSignalHandler(int signum,
+                                                 void (*handler)(int)) {
+  // If handler is SIG_IGN or SIG_DFL then clear our user-level handler,
+  // otherwise set one.
+  if (handler == SIG_IGN || handler == SIG_DFL) {
+    if (!InstallSignal(signum, handler)) {
+      return false;
+    }
+    if (signal_dispatcher_) {
+      signal_dispatcher_->ClearHandler(signum);
+      if (!signal_dispatcher_->HasHandlers()) {
+        signal_dispatcher_.reset();
+      }
+    }
+  } else {
+    if (!signal_dispatcher_) {
+      signal_dispatcher_.reset(new PosixSignalDispatcher(this));
+    }
+    signal_dispatcher_->SetHandler(signum, handler);
+    if (!InstallSignal(signum, &GlobalSignalHandler)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+Dispatcher* PhysicalSocketServer::signal_dispatcher() {
+  return signal_dispatcher_.get();
+}
+
+bool PhysicalSocketServer::InstallSignal(int signum, void (*handler)(int)) {
+  struct sigaction act;
+  // It doesn't really matter what we set this mask to.
+  if (sigemptyset(&act.sa_mask) != 0) {
+    LOG_ERR(LS_ERROR) << "Couldn't set mask";
+    return false;
+  }
+  act.sa_handler = handler;
+#if !defined(__native_client__)
+  // Use SA_RESTART so that our syscalls don't get EINTR, since we don't need it
+  // and it's a nuisance. Though some syscalls still return EINTR and there's no
+  // real standard for which ones. :(
+  act.sa_flags = SA_RESTART;
+#else
+  act.sa_flags = 0;
+#endif
+  if (sigaction(signum, &act, nullptr) != 0) {
+    LOG_ERR(LS_ERROR) << "Couldn't set sigaction";
+    return false;
+  }
+  return true;
+}
+#endif  // WEBRTC_POSIX
+
+#if defined(WEBRTC_WIN)
+bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) {
+  int64_t cmsTotal = cmsWait;
+  int64_t cmsElapsed = 0;
+  int64_t msStart = Time();
+
+  fWait_ = true;
+  while (fWait_) {
+    std::vector<WSAEVENT> events;
+    std::vector<Dispatcher *> event_owners;
+
+    events.push_back(socket_ev_);
+
+    {
+      CritScope cr(&crit_);
+      // TODO(jbauch): Support re-entrant waiting.
+      RTC_DCHECK(!processing_dispatchers_);
+
+      // Calling "CheckSignalClose" might remove a closed dispatcher from the
+      // set. This must be deferred to prevent invalidating the iterator.
+      processing_dispatchers_ = true;
+      for (Dispatcher* disp : dispatchers_) {
+        if (!process_io && (disp != signal_wakeup_))
+          continue;
+        SOCKET s = disp->GetSocket();
+        if (disp->CheckSignalClose()) {
+          // We just signalled close, don't poll this socket
+        } else if (s != INVALID_SOCKET) {
+          WSAEventSelect(s,
+                         events[0],
+                         FlagsToEvents(disp->GetRequestedEvents()));
+        } else {
+          events.push_back(disp->GetWSAEvent());
+          event_owners.push_back(disp);
+        }
+      }
+
+      processing_dispatchers_ = false;
+      // Process deferred dispatchers that have been added/removed while the
+      // events were handled above.
+      AddRemovePendingDispatchers();
+    }
+
+    // Which is shorter, the delay wait or the asked wait?
+
+    int64_t cmsNext;
+    if (cmsWait == kForever) {
+      cmsNext = cmsWait;
+    } else {
+      cmsNext = std::max<int64_t>(0, cmsTotal - cmsElapsed);
+    }
+
+    // Wait for one of the events to signal
+    DWORD dw = WSAWaitForMultipleEvents(static_cast<DWORD>(events.size()),
+                                        &events[0],
+                                        false,
+                                        static_cast<DWORD>(cmsNext),
+                                        false);
+
+    if (dw == WSA_WAIT_FAILED) {
+      // Failed?
+      // TODO(pthatcher): need a better strategy than this!
+      WSAGetLastError();
+      RTC_NOTREACHED();
+      return false;
+    } else if (dw == WSA_WAIT_TIMEOUT) {
+      // Timeout?
+      return true;
+    } else {
+      // Figure out which one it is and call it
+      CritScope cr(&crit_);
+      int index = dw - WSA_WAIT_EVENT_0;
+      if (index > 0) {
+        --index; // The first event is the socket event
+        Dispatcher* disp = event_owners[index];
+        // The dispatcher could have been removed while waiting for events.
+        if (dispatchers_.find(disp) != dispatchers_.end()) {
+          disp->OnPreEvent(0);
+          disp->OnEvent(0, 0);
+        }
+      } else if (process_io) {
+        processing_dispatchers_ = true;
+        for (Dispatcher* disp : dispatchers_) {
+          SOCKET s = disp->GetSocket();
+          if (s == INVALID_SOCKET)
+            continue;
+
+          WSANETWORKEVENTS wsaEvents;
+          int err = WSAEnumNetworkEvents(s, events[0], &wsaEvents);
+          if (err == 0) {
+            {
+              if ((wsaEvents.lNetworkEvents & FD_READ) &&
+                  wsaEvents.iErrorCode[FD_READ_BIT] != 0) {
+                LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error "
+                             << wsaEvents.iErrorCode[FD_READ_BIT];
+              }
+              if ((wsaEvents.lNetworkEvents & FD_WRITE) &&
+                  wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) {
+                LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error "
+                             << wsaEvents.iErrorCode[FD_WRITE_BIT];
+              }
+              if ((wsaEvents.lNetworkEvents & FD_CONNECT) &&
+                  wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) {
+                LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error "
+                             << wsaEvents.iErrorCode[FD_CONNECT_BIT];
+              }
+              if ((wsaEvents.lNetworkEvents & FD_ACCEPT) &&
+                  wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) {
+                LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error "
+                             << wsaEvents.iErrorCode[FD_ACCEPT_BIT];
+              }
+              if ((wsaEvents.lNetworkEvents & FD_CLOSE) &&
+                  wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) {
+                LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error "
+                             << wsaEvents.iErrorCode[FD_CLOSE_BIT];
+              }
+            }
+            uint32_t ff = 0;
+            int errcode = 0;
+            if (wsaEvents.lNetworkEvents & FD_READ)
+              ff |= DE_READ;
+            if (wsaEvents.lNetworkEvents & FD_WRITE)
+              ff |= DE_WRITE;
+            if (wsaEvents.lNetworkEvents & FD_CONNECT) {
+              if (wsaEvents.iErrorCode[FD_CONNECT_BIT] == 0) {
+                ff |= DE_CONNECT;
+              } else {
+                ff |= DE_CLOSE;
+                errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT];
+              }
+            }
+            if (wsaEvents.lNetworkEvents & FD_ACCEPT)
+              ff |= DE_ACCEPT;
+            if (wsaEvents.lNetworkEvents & FD_CLOSE) {
+              ff |= DE_CLOSE;
+              errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT];
+            }
+            if (ff != 0) {
+              disp->OnPreEvent(ff);
+              disp->OnEvent(ff, errcode);
+            }
+          }
+        }
+
+        processing_dispatchers_ = false;
+        // Process deferred dispatchers that have been added/removed while the
+        // events were handled above.
+        AddRemovePendingDispatchers();
+      }
+
+      // Reset the network event until new activity occurs
+      WSAResetEvent(socket_ev_);
+    }
+
+    // Break?
+    if (!fWait_)
+      break;
+    cmsElapsed = TimeSince(msStart);
+    if ((cmsWait != kForever) && (cmsElapsed >= cmsWait)) {
+       break;
+    }
+  }
+
+  // Done
+  return true;
+}
+#endif  // WEBRTC_WIN
+
+}  // namespace rtc
diff --git a/base/physicalsocketserver.h b/base/physicalsocketserver.h
index 63e6dfa..dec37c2 100644
--- a/base/physicalsocketserver.h
+++ b/base/physicalsocketserver.h
@@ -8,12 +8,263 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_PHYSICALSOCKETSERVER_H_
-#define WEBRTC_BASE_PHYSICALSOCKETSERVER_H_
+#ifndef WEBRTC_BASE_PHYSICALSOCKETSERVER_H__
+#define WEBRTC_BASE_PHYSICALSOCKETSERVER_H__
 
+#if defined(WEBRTC_POSIX) && defined(WEBRTC_LINUX)
+#include <sys/epoll.h>
+#define WEBRTC_USE_EPOLL 1
+#endif
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/physicalsocketserver.h"
+#include <memory>
+#include <set>
+#include <vector>
 
-#endif // WEBRTC_BASE_PHYSICALSOCKETSERVER_H_
+#include "webrtc/base/nethelpers.h"
+#include "webrtc/base/socketserver.h"
+#include "webrtc/base/criticalsection.h"
+
+#if defined(WEBRTC_POSIX)
+typedef int SOCKET;
+#endif // WEBRTC_POSIX
+
+namespace rtc {
+
+// Event constants for the Dispatcher class.
+enum DispatcherEvent {
+  DE_READ    = 0x0001,
+  DE_WRITE   = 0x0002,
+  DE_CONNECT = 0x0004,
+  DE_CLOSE   = 0x0008,
+  DE_ACCEPT  = 0x0010,
+};
+
+class Signaler;
+#if defined(WEBRTC_POSIX)
+class PosixSignalDispatcher;
+#endif
+
+class Dispatcher {
+ public:
+  virtual ~Dispatcher() {}
+  virtual uint32_t GetRequestedEvents() = 0;
+  virtual void OnPreEvent(uint32_t ff) = 0;
+  virtual void OnEvent(uint32_t ff, int err) = 0;
+#if defined(WEBRTC_WIN)
+  virtual WSAEVENT GetWSAEvent() = 0;
+  virtual SOCKET GetSocket() = 0;
+  virtual bool CheckSignalClose() = 0;
+#elif defined(WEBRTC_POSIX)
+  virtual int GetDescriptor() = 0;
+  virtual bool IsDescriptorClosed() = 0;
+#endif
+};
+
+// A socket server that provides the real sockets of the underlying OS.
+class PhysicalSocketServer : public SocketServer {
+ public:
+  PhysicalSocketServer();
+  ~PhysicalSocketServer() override;
+
+  // SocketFactory:
+  Socket* CreateSocket(int type) override;
+  Socket* CreateSocket(int family, int type) override;
+
+  AsyncSocket* CreateAsyncSocket(int type) override;
+  AsyncSocket* CreateAsyncSocket(int family, int type) override;
+
+  // Internal Factory for Accept (virtual so it can be overwritten in tests).
+  virtual AsyncSocket* WrapSocket(SOCKET s);
+
+  // SocketServer:
+  bool Wait(int cms, bool process_io) override;
+  void WakeUp() override;
+
+  void Add(Dispatcher* dispatcher);
+  void Remove(Dispatcher* dispatcher);
+  void Update(Dispatcher* dispatcher);
+
+#if defined(WEBRTC_POSIX)
+  // Sets the function to be executed in response to the specified POSIX signal.
+  // The function is executed from inside Wait() using the "self-pipe trick"--
+  // regardless of which thread receives the signal--and hence can safely
+  // manipulate user-level data structures.
+  // "handler" may be SIG_IGN, SIG_DFL, or a user-specified function, just like
+  // with signal(2).
+  // Only one PhysicalSocketServer should have user-level signal handlers.
+  // Dispatching signals on multiple PhysicalSocketServers is not reliable.
+  // The signal mask is not modified. It is the caller's responsibily to
+  // maintain it as desired.
+  virtual bool SetPosixSignalHandler(int signum, void (*handler)(int));
+
+ protected:
+  Dispatcher* signal_dispatcher();
+#endif
+
+ private:
+  typedef std::set<Dispatcher*> DispatcherSet;
+
+  void AddRemovePendingDispatchers();
+
+#if defined(WEBRTC_POSIX)
+  bool WaitSelect(int cms, bool process_io);
+  static bool InstallSignal(int signum, void (*handler)(int));
+
+  std::unique_ptr<PosixSignalDispatcher> signal_dispatcher_;
+#endif  // WEBRTC_POSIX
+#if defined(WEBRTC_USE_EPOLL)
+  void AddEpoll(Dispatcher* dispatcher);
+  void RemoveEpoll(Dispatcher* dispatcher);
+  void UpdateEpoll(Dispatcher* dispatcher);
+  bool WaitEpoll(int cms);
+  bool WaitPoll(int cms, Dispatcher* dispatcher);
+
+  int epoll_fd_ = INVALID_SOCKET;
+  std::vector<struct epoll_event> epoll_events_;
+#endif  // WEBRTC_USE_EPOLL
+  DispatcherSet dispatchers_;
+  DispatcherSet pending_add_dispatchers_;
+  DispatcherSet pending_remove_dispatchers_;
+  bool processing_dispatchers_ = false;
+  Signaler* signal_wakeup_;
+  CriticalSection crit_;
+  bool fWait_;
+#if defined(WEBRTC_WIN)
+  WSAEVENT socket_ev_;
+#endif
+};
+
+class PhysicalSocket : public AsyncSocket, public sigslot::has_slots<> {
+ public:
+  PhysicalSocket(PhysicalSocketServer* ss, SOCKET s = INVALID_SOCKET);
+  ~PhysicalSocket() override;
+
+  // Creates the underlying OS socket (same as the "socket" function).
+  virtual bool Create(int family, int type);
+
+  SocketAddress GetLocalAddress() const override;
+  SocketAddress GetRemoteAddress() const override;
+
+  int Bind(const SocketAddress& bind_addr) override;
+  int Connect(const SocketAddress& addr) override;
+
+  int GetError() const override;
+  void SetError(int error) override;
+
+  ConnState GetState() const override;
+
+  int GetOption(Option opt, int* value) override;
+  int SetOption(Option opt, int value) override;
+
+  int Send(const void* pv, size_t cb) override;
+  int SendTo(const void* buffer,
+             size_t length,
+             const SocketAddress& addr) override;
+
+  int Recv(void* buffer, size_t length, int64_t* timestamp) override;
+  int RecvFrom(void* buffer,
+               size_t length,
+               SocketAddress* out_addr,
+               int64_t* timestamp) override;
+
+  int Listen(int backlog) override;
+  AsyncSocket* Accept(SocketAddress* out_addr) override;
+
+  int Close() override;
+
+  SocketServer* socketserver() { return ss_; }
+
+ protected:
+  int DoConnect(const SocketAddress& connect_addr);
+
+  // Make virtual so ::accept can be overwritten in tests.
+  virtual SOCKET DoAccept(SOCKET socket, sockaddr* addr, socklen_t* addrlen);
+
+  // Make virtual so ::send can be overwritten in tests.
+  virtual int DoSend(SOCKET socket, const char* buf, int len, int flags);
+
+  // Make virtual so ::sendto can be overwritten in tests.
+  virtual int DoSendTo(SOCKET socket, const char* buf, int len, int flags,
+                       const struct sockaddr* dest_addr, socklen_t addrlen);
+
+  void OnResolveResult(AsyncResolverInterface* resolver);
+
+  void UpdateLastError();
+  void MaybeRemapSendError();
+
+  uint8_t enabled_events() const { return enabled_events_; }
+  virtual void SetEnabledEvents(uint8_t events);
+  virtual void EnableEvents(uint8_t events);
+  virtual void DisableEvents(uint8_t events);
+
+  static int TranslateOption(Option opt, int* slevel, int* sopt);
+
+  PhysicalSocketServer* ss_;
+  SOCKET s_;
+  bool udp_;
+  CriticalSection crit_;
+  int error_ GUARDED_BY(crit_);
+  ConnState state_;
+  AsyncResolver* resolver_;
+
+#if !defined(NDEBUG)
+  std::string dbg_addr_;
+#endif
+
+ private:
+  uint8_t enabled_events_ = 0;
+};
+
+class SocketDispatcher : public Dispatcher, public PhysicalSocket {
+ public:
+  explicit SocketDispatcher(PhysicalSocketServer *ss);
+  SocketDispatcher(SOCKET s, PhysicalSocketServer *ss);
+  ~SocketDispatcher() override;
+
+  bool Initialize();
+
+  virtual bool Create(int type);
+  bool Create(int family, int type) override;
+
+#if defined(WEBRTC_WIN)
+  WSAEVENT GetWSAEvent() override;
+  SOCKET GetSocket() override;
+  bool CheckSignalClose() override;
+#elif defined(WEBRTC_POSIX)
+  int GetDescriptor() override;
+  bool IsDescriptorClosed() override;
+#endif
+
+  uint32_t GetRequestedEvents() override;
+  void OnPreEvent(uint32_t ff) override;
+  void OnEvent(uint32_t ff, int err) override;
+
+  int Close() override;
+
+#if defined(WEBRTC_USE_EPOLL)
+ protected:
+  void StartBatchedEventUpdates();
+  void FinishBatchedEventUpdates();
+
+  void SetEnabledEvents(uint8_t events) override;
+  void EnableEvents(uint8_t events) override;
+  void DisableEvents(uint8_t events) override;
+#endif
+
+ private:
+#if defined(WEBRTC_WIN)
+  static int next_id_;
+  int id_;
+  bool signal_close_;
+  int signal_err_;
+#endif // WEBRTC_WIN
+#if defined(WEBRTC_USE_EPOLL)
+  void MaybeUpdateDispatcher(uint8_t old_events);
+
+  int saved_enabled_events_ = -1;
+#endif
+};
+
+} // namespace rtc
+
+#endif // WEBRTC_BASE_PHYSICALSOCKETSERVER_H__
diff --git a/base/physicalsocketserver_unittest.cc b/base/physicalsocketserver_unittest.cc
new file mode 100644
index 0000000..20e66ce
--- /dev/null
+++ b/base/physicalsocketserver_unittest.cc
@@ -0,0 +1,624 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <signal.h>
+#include <stdarg.h>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/networkmonitor.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/socket_unittest.h"
+#include "webrtc/base/testutils.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+#define MAYBE_SKIP_IPV4                    \
+  if (!HasIPv4Enabled()) {                 \
+    LOG(LS_INFO) << "No IPv4... skipping"; \
+    return;                                \
+  }
+
+#define MAYBE_SKIP_IPV6                    \
+  if (!HasIPv6Enabled()) {                 \
+    LOG(LS_INFO) << "No IPv6... skipping"; \
+    return;                                \
+  }
+
+class PhysicalSocketTest;
+
+class FakeSocketDispatcher : public SocketDispatcher {
+ public:
+  explicit FakeSocketDispatcher(PhysicalSocketServer* ss)
+    : SocketDispatcher(ss) {
+  }
+
+  FakeSocketDispatcher(SOCKET s, PhysicalSocketServer* ss)
+    : SocketDispatcher(s, ss) {
+  }
+
+ protected:
+  SOCKET DoAccept(SOCKET socket, sockaddr* addr, socklen_t* addrlen) override;
+  int DoSend(SOCKET socket, const char* buf, int len, int flags) override;
+  int DoSendTo(SOCKET socket, const char* buf, int len, int flags,
+               const struct sockaddr* dest_addr, socklen_t addrlen) override;
+};
+
+class FakePhysicalSocketServer : public PhysicalSocketServer {
+ public:
+  explicit FakePhysicalSocketServer(PhysicalSocketTest* test)
+    : test_(test) {
+  }
+
+  AsyncSocket* CreateAsyncSocket(int type) override {
+    SocketDispatcher* dispatcher = new FakeSocketDispatcher(this);
+    if (!dispatcher->Create(type)) {
+      delete dispatcher;
+      return nullptr;
+    }
+    return dispatcher;
+  }
+
+  AsyncSocket* CreateAsyncSocket(int family, int type) override {
+    SocketDispatcher* dispatcher = new FakeSocketDispatcher(this);
+    if (!dispatcher->Create(family, type)) {
+      delete dispatcher;
+      return nullptr;
+    }
+    return dispatcher;
+  }
+
+  AsyncSocket* WrapSocket(SOCKET s) override {
+    SocketDispatcher* dispatcher = new FakeSocketDispatcher(s, this);
+    if (!dispatcher->Initialize()) {
+      delete dispatcher;
+      return nullptr;
+    }
+    return dispatcher;
+  }
+
+  PhysicalSocketTest* GetTest() const { return test_; }
+
+ private:
+  PhysicalSocketTest* test_;
+};
+
+class FakeNetworkBinder : public NetworkBinderInterface {
+ public:
+  NetworkBindingResult BindSocketToNetwork(int, const IPAddress&) override {
+    ++num_binds_;
+    return result_;
+  }
+
+  void set_result(NetworkBindingResult result) { result_ = result; }
+
+  int num_binds() { return num_binds_; }
+
+ private:
+  NetworkBindingResult result_ = NetworkBindingResult::SUCCESS;
+  int num_binds_ = 0;
+};
+
+class PhysicalSocketTest : public SocketTest {
+ public:
+  // Set flag to simluate failures when calling "::accept" on a AsyncSocket.
+  void SetFailAccept(bool fail) { fail_accept_ = fail; }
+  bool FailAccept() const { return fail_accept_; }
+
+  // Maximum size to ::send to a socket. Set to < 0 to disable limiting.
+  void SetMaxSendSize(int max_size) { max_send_size_ = max_size; }
+  int MaxSendSize() const { return max_send_size_; }
+
+ protected:
+  PhysicalSocketTest()
+    : server_(new FakePhysicalSocketServer(this)),
+      thread_(server_.get()),
+      fail_accept_(false),
+      max_send_size_(-1) {}
+
+  void ConnectInternalAcceptError(const IPAddress& loopback);
+  void WritableAfterPartialWrite(const IPAddress& loopback);
+
+  std::unique_ptr<FakePhysicalSocketServer> server_;
+  rtc::AutoSocketServerThread thread_;
+  bool fail_accept_;
+  int max_send_size_;
+};
+
+SOCKET FakeSocketDispatcher::DoAccept(SOCKET socket,
+                                      sockaddr* addr,
+                                      socklen_t* addrlen) {
+  FakePhysicalSocketServer* ss =
+      static_cast<FakePhysicalSocketServer*>(socketserver());
+  if (ss->GetTest()->FailAccept()) {
+    return INVALID_SOCKET;
+  }
+
+  return SocketDispatcher::DoAccept(socket, addr, addrlen);
+}
+
+int FakeSocketDispatcher::DoSend(SOCKET socket, const char* buf, int len,
+    int flags) {
+  FakePhysicalSocketServer* ss =
+      static_cast<FakePhysicalSocketServer*>(socketserver());
+  if (ss->GetTest()->MaxSendSize() >= 0) {
+    len = std::min(len, ss->GetTest()->MaxSendSize());
+  }
+
+  return SocketDispatcher::DoSend(socket, buf, len, flags);
+}
+
+int FakeSocketDispatcher::DoSendTo(SOCKET socket, const char* buf, int len,
+    int flags, const struct sockaddr* dest_addr, socklen_t addrlen) {
+  FakePhysicalSocketServer* ss =
+      static_cast<FakePhysicalSocketServer*>(socketserver());
+  if (ss->GetTest()->MaxSendSize() >= 0) {
+    len = std::min(len, ss->GetTest()->MaxSendSize());
+  }
+
+  return SocketDispatcher::DoSendTo(socket, buf, len, flags, dest_addr,
+      addrlen);
+}
+
+TEST_F(PhysicalSocketTest, TestConnectIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestConnectIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestConnectIPv6) {
+  SocketTest::TestConnectIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestConnectWithDnsLookupIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv6) {
+  SocketTest::TestConnectWithDnsLookupIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestConnectFailIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestConnectFailIPv4();
+}
+
+void PhysicalSocketTest::ConnectInternalAcceptError(const IPAddress& loopback) {
+  webrtc::testing::StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create two clients.
+  std::unique_ptr<AsyncSocket> client1(
+      server_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client1.get());
+  EXPECT_EQ(AsyncSocket::CS_CLOSED, client1->GetState());
+  EXPECT_PRED1(IsUnspecOrEmptyIP, client1->GetLocalAddress().ipaddr());
+
+  std::unique_ptr<AsyncSocket> client2(
+      server_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client2.get());
+  EXPECT_EQ(AsyncSocket::CS_CLOSED, client2->GetState());
+  EXPECT_PRED1(IsUnspecOrEmptyIP, client2->GetLocalAddress().ipaddr());
+
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      server_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+  EXPECT_EQ(AsyncSocket::CS_CONNECTING, server->GetState());
+
+  // Ensure no pending server connections, since we haven't done anything yet.
+  EXPECT_FALSE(sink.Check(server.get(), webrtc::testing::SSE_READ));
+  EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
+  EXPECT_TRUE(accept_addr.IsNil());
+
+  // Attempt first connect to listening socket.
+  EXPECT_EQ(0, client1->Connect(server->GetLocalAddress()));
+  EXPECT_FALSE(client1->GetLocalAddress().IsNil());
+  EXPECT_NE(server->GetLocalAddress(), client1->GetLocalAddress());
+
+  // Client is connecting, outcome not yet determined.
+  EXPECT_EQ(AsyncSocket::CS_CONNECTING, client1->GetState());
+  EXPECT_FALSE(sink.Check(client1.get(), webrtc::testing::SSE_OPEN));
+  EXPECT_FALSE(sink.Check(client1.get(), webrtc::testing::SSE_CLOSE));
+
+  // Server has pending connection, try to accept it (will fail).
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), webrtc::testing::SSE_READ)),
+                   kTimeout);
+  // Simulate "::accept" returning an error.
+  SetFailAccept(true);
+  std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+  EXPECT_FALSE(accepted);
+  ASSERT_TRUE(accept_addr.IsNil());
+
+  // Ensure no more pending server connections.
+  EXPECT_FALSE(sink.Check(server.get(), webrtc::testing::SSE_READ));
+  EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
+  EXPECT_TRUE(accept_addr.IsNil());
+
+  // Attempt second connect to listening socket.
+  EXPECT_EQ(0, client2->Connect(server->GetLocalAddress()));
+  EXPECT_FALSE(client2->GetLocalAddress().IsNil());
+  EXPECT_NE(server->GetLocalAddress(), client2->GetLocalAddress());
+
+  // Client is connecting, outcome not yet determined.
+  EXPECT_EQ(AsyncSocket::CS_CONNECTING, client2->GetState());
+  EXPECT_FALSE(sink.Check(client2.get(), webrtc::testing::SSE_OPEN));
+  EXPECT_FALSE(sink.Check(client2.get(), webrtc::testing::SSE_CLOSE));
+
+  // Server has pending connection, try to accept it (will succeed).
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), webrtc::testing::SSE_READ)),
+                   kTimeout);
+  SetFailAccept(false);
+  std::unique_ptr<AsyncSocket> accepted2(server->Accept(&accept_addr));
+  ASSERT_TRUE(accepted2);
+  EXPECT_FALSE(accept_addr.IsNil());
+  EXPECT_EQ(accepted2->GetRemoteAddress(), accept_addr);
+}
+
+TEST_F(PhysicalSocketTest, TestConnectAcceptErrorIPv4) {
+  MAYBE_SKIP_IPV4;
+  ConnectInternalAcceptError(kIPv4Loopback);
+}
+
+TEST_F(PhysicalSocketTest, TestConnectAcceptErrorIPv6) {
+  MAYBE_SKIP_IPV6;
+  ConnectInternalAcceptError(kIPv6Loopback);
+}
+
+void PhysicalSocketTest::WritableAfterPartialWrite(const IPAddress& loopback) {
+  // Simulate a really small maximum send size.
+  const int kMaxSendSize = 128;
+  SetMaxSendSize(kMaxSendSize);
+
+  // Run the default send/receive socket tests with a smaller amount of data
+  // to avoid long running times due to the small maximum send size.
+  const size_t kDataSize = 128 * 1024;
+  TcpInternal(loopback, kDataSize, kMaxSendSize);
+}
+
+// https://bugs.chromium.org/p/webrtc/issues/detail?id=6167
+#if defined(WEBRTC_WIN)
+#define MAYBE_TestWritableAfterPartialWriteIPv4 DISABLED_TestWritableAfterPartialWriteIPv4
+#else
+#define MAYBE_TestWritableAfterPartialWriteIPv4 TestWritableAfterPartialWriteIPv4
+#endif
+TEST_F(PhysicalSocketTest, MAYBE_TestWritableAfterPartialWriteIPv4) {
+  MAYBE_SKIP_IPV4;
+  WritableAfterPartialWrite(kIPv4Loopback);
+}
+
+// https://bugs.chromium.org/p/webrtc/issues/detail?id=6167
+#if defined(WEBRTC_WIN)
+#define MAYBE_TestWritableAfterPartialWriteIPv6 DISABLED_TestWritableAfterPartialWriteIPv6
+#else
+#define MAYBE_TestWritableAfterPartialWriteIPv6 TestWritableAfterPartialWriteIPv6
+#endif
+TEST_F(PhysicalSocketTest, MAYBE_TestWritableAfterPartialWriteIPv6) {
+  MAYBE_SKIP_IPV6;
+  WritableAfterPartialWrite(kIPv6Loopback);
+}
+
+TEST_F(PhysicalSocketTest, TestConnectFailIPv6) {
+  SocketTest::TestConnectFailIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestConnectWithDnsLookupFailIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv6) {
+  SocketTest::TestConnectWithDnsLookupFailIPv6();
+}
+
+
+TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestConnectWithClosedSocketIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv6) {
+  SocketTest::TestConnectWithClosedSocketIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestConnectWhileNotClosedIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv6) {
+  SocketTest::TestConnectWhileNotClosedIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestServerCloseDuringConnectIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv6) {
+  SocketTest::TestServerCloseDuringConnectIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestClientCloseDuringConnectIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv6) {
+  SocketTest::TestClientCloseDuringConnectIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestServerCloseIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestServerCloseIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestServerCloseIPv6) {
+  SocketTest::TestServerCloseIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestCloseInClosedCallbackIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv6) {
+  SocketTest::TestCloseInClosedCallbackIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestSocketServerWaitIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv6) {
+  SocketTest::TestSocketServerWaitIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestTcpIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestTcpIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestTcpIPv6) {
+  SocketTest::TestTcpIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestUdpIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestUdpIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestUdpIPv6) {
+  SocketTest::TestUdpIPv6();
+}
+
+// Disable for TSan v2, see
+// https://code.google.com/p/webrtc/issues/detail?id=3498 for details.
+// Also disable for MSan, see:
+// https://code.google.com/p/webrtc/issues/detail?id=4958
+// TODO(deadbeef): Enable again once test is reimplemented to be unflaky.
+// Also disable for ASan.
+// Disabled on Android: https://code.google.com/p/webrtc/issues/detail?id=4364
+// Disabled on Linux: https://bugs.chromium.org/p/webrtc/issues/detail?id=5233
+#if defined(THREAD_SANITIZER) || defined(MEMORY_SANITIZER) || \
+    defined(ADDRESS_SANITIZER) || defined(WEBRTC_ANDROID) ||  \
+    defined(WEBRTC_LINUX)
+#define MAYBE_TestUdpReadyToSendIPv4 DISABLED_TestUdpReadyToSendIPv4
+#else
+#define MAYBE_TestUdpReadyToSendIPv4 TestUdpReadyToSendIPv4
+#endif
+TEST_F(PhysicalSocketTest, MAYBE_TestUdpReadyToSendIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestUdpReadyToSendIPv4();
+}
+
+// https://bugs.chromium.org/p/webrtc/issues/detail?id=6167
+#if defined(WEBRTC_WIN)
+#define MAYBE_TestUdpReadyToSendIPv6 DISABLED_TestUdpReadyToSendIPv6
+#else
+#define MAYBE_TestUdpReadyToSendIPv6 TestUdpReadyToSendIPv6
+#endif
+TEST_F(PhysicalSocketTest, MAYBE_TestUdpReadyToSendIPv6) {
+  SocketTest::TestUdpReadyToSendIPv6();
+}
+
+TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestGetSetOptionsIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv6) {
+  SocketTest::TestGetSetOptionsIPv6();
+}
+
+#if defined(WEBRTC_POSIX)
+
+// We don't get recv timestamps on Mac.
+#if !defined(WEBRTC_MAC)
+TEST_F(PhysicalSocketTest, TestSocketRecvTimestampIPv4) {
+  MAYBE_SKIP_IPV4;
+  SocketTest::TestSocketRecvTimestampIPv4();
+}
+
+TEST_F(PhysicalSocketTest, TestSocketRecvTimestampIPv6) {
+  SocketTest::TestSocketRecvTimestampIPv6();
+}
+#endif
+
+// Verify that if the socket was unable to be bound to a real network interface
+// (not loopback), Bind will return an error.
+TEST_F(PhysicalSocketTest,
+       BindFailsIfNetworkBinderFailsForNonLoopbackInterface) {
+  MAYBE_SKIP_IPV4;
+  FakeNetworkBinder fake_network_binder;
+  server_->set_network_binder(&fake_network_binder);
+  std::unique_ptr<AsyncSocket> socket(
+      server_->CreateAsyncSocket(AF_INET, SOCK_DGRAM));
+  fake_network_binder.set_result(NetworkBindingResult::FAILURE);
+  EXPECT_EQ(-1, socket->Bind(SocketAddress("192.168.0.1", 0)));
+  server_->set_network_binder(nullptr);
+}
+
+// Network binder shouldn't be used if the socket is bound to the "any" IP.
+TEST_F(PhysicalSocketTest,
+       NetworkBinderIsNotUsedForAnyIp) {
+  MAYBE_SKIP_IPV4;
+  FakeNetworkBinder fake_network_binder;
+  server_->set_network_binder(&fake_network_binder);
+  std::unique_ptr<AsyncSocket> socket(
+      server_->CreateAsyncSocket(AF_INET, SOCK_DGRAM));
+  EXPECT_EQ(0, socket->Bind(SocketAddress("0.0.0.0", 0)));
+  EXPECT_EQ(0, fake_network_binder.num_binds());
+  server_->set_network_binder(nullptr);
+}
+
+// For a loopback interface, failures to bind to the interface should be
+// tolerated.
+TEST_F(PhysicalSocketTest,
+       BindSucceedsIfNetworkBinderFailsForLoopbackInterface) {
+  MAYBE_SKIP_IPV4;
+  FakeNetworkBinder fake_network_binder;
+  server_->set_network_binder(&fake_network_binder);
+  std::unique_ptr<AsyncSocket> socket(
+      server_->CreateAsyncSocket(AF_INET, SOCK_DGRAM));
+  fake_network_binder.set_result(NetworkBindingResult::FAILURE);
+  EXPECT_EQ(0, socket->Bind(SocketAddress(kIPv4Loopback, 0)));
+  server_->set_network_binder(nullptr);
+}
+
+class PosixSignalDeliveryTest : public testing::Test {
+ public:
+  static void RecordSignal(int signum) {
+    signals_received_.push_back(signum);
+    signaled_thread_ = Thread::Current();
+  }
+
+ protected:
+  void SetUp() {
+    ss_.reset(new PhysicalSocketServer());
+  }
+
+  void TearDown() {
+    ss_.reset(nullptr);
+    signals_received_.clear();
+    signaled_thread_ = nullptr;
+  }
+
+  bool ExpectSignal(int signum) {
+    if (signals_received_.empty()) {
+      LOG(LS_ERROR) << "ExpectSignal(): No signal received";
+      return false;
+    }
+    if (signals_received_[0] != signum) {
+      LOG(LS_ERROR) << "ExpectSignal(): Received signal " <<
+          signals_received_[0] << ", expected " << signum;
+      return false;
+    }
+    signals_received_.erase(signals_received_.begin());
+    return true;
+  }
+
+  bool ExpectNone() {
+    bool ret = signals_received_.empty();
+    if (!ret) {
+      LOG(LS_ERROR) << "ExpectNone(): Received signal " << signals_received_[0]
+          << ", expected none";
+    }
+    return ret;
+  }
+
+  static std::vector<int> signals_received_;
+  static Thread *signaled_thread_;
+
+  std::unique_ptr<PhysicalSocketServer> ss_;
+};
+
+std::vector<int> PosixSignalDeliveryTest::signals_received_;
+Thread* PosixSignalDeliveryTest::signaled_thread_ = nullptr;
+
+// Test receiving a synchronous signal while not in Wait() and then entering
+// Wait() afterwards.
+TEST_F(PosixSignalDeliveryTest, RaiseThenWait) {
+  ASSERT_TRUE(ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal));
+  raise(SIGTERM);
+  EXPECT_TRUE(ss_->Wait(0, true));
+  EXPECT_TRUE(ExpectSignal(SIGTERM));
+  EXPECT_TRUE(ExpectNone());
+}
+
+// Test that we can handle getting tons of repeated signals and that we see all
+// the different ones.
+TEST_F(PosixSignalDeliveryTest, InsanelyManySignals) {
+  ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal);
+  ss_->SetPosixSignalHandler(SIGINT, &RecordSignal);
+  for (int i = 0; i < 10000; ++i) {
+    raise(SIGTERM);
+  }
+  raise(SIGINT);
+  EXPECT_TRUE(ss_->Wait(0, true));
+  // Order will be lowest signal numbers first.
+  EXPECT_TRUE(ExpectSignal(SIGINT));
+  EXPECT_TRUE(ExpectSignal(SIGTERM));
+  EXPECT_TRUE(ExpectNone());
+}
+
+// Test that a signal during a Wait() call is detected.
+TEST_F(PosixSignalDeliveryTest, SignalDuringWait) {
+  ss_->SetPosixSignalHandler(SIGALRM, &RecordSignal);
+  alarm(1);
+  EXPECT_TRUE(ss_->Wait(1500, true));
+  EXPECT_TRUE(ExpectSignal(SIGALRM));
+  EXPECT_TRUE(ExpectNone());
+}
+
+class RaiseSigTermRunnable : public Runnable {
+  void Run(Thread *thread) {
+    thread->socketserver()->Wait(1000, false);
+
+    // Allow SIGTERM. This will be the only thread with it not masked so it will
+    // be delivered to us.
+    sigset_t mask;
+    sigemptyset(&mask);
+    pthread_sigmask(SIG_SETMASK, &mask, nullptr);
+
+    // Raise it.
+    raise(SIGTERM);
+  }
+};
+
+// Test that it works no matter what thread the kernel chooses to give the
+// signal to (since it's not guaranteed to be the one that Wait() runs on).
+TEST_F(PosixSignalDeliveryTest, SignalOnDifferentThread) {
+  ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal);
+  // Mask out SIGTERM so that it can't be delivered to this thread.
+  sigset_t mask;
+  sigemptyset(&mask);
+  sigaddset(&mask, SIGTERM);
+  EXPECT_EQ(0, pthread_sigmask(SIG_SETMASK, &mask, nullptr));
+  // Start a new thread that raises it. It will have to be delivered to that
+  // thread. Our implementation should safely handle it and dispatch
+  // RecordSignal() on this thread.
+  std::unique_ptr<Thread> thread(new Thread());
+  std::unique_ptr<RaiseSigTermRunnable> runnable(new RaiseSigTermRunnable());
+  thread->Start(runnable.get());
+  EXPECT_TRUE(ss_->Wait(1500, true));
+  EXPECT_TRUE(ExpectSignal(SIGTERM));
+  EXPECT_EQ(Thread::Current(), signaled_thread_);
+  EXPECT_TRUE(ExpectNone());
+}
+
+#endif
+
+}  // namespace rtc
diff --git a/base/platform_file.cc b/base/platform_file.cc
new file mode 100644
index 0000000..a00543c
--- /dev/null
+++ b/base/platform_file.cc
@@ -0,0 +1,81 @@
+/*
+ *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/platform_file.h"
+
+#if defined(WEBRTC_WIN)
+#include <io.h>
+#else
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+namespace rtc {
+
+#if defined(WEBRTC_WIN)
+const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
+
+FILE* FdopenPlatformFileForWriting(PlatformFile file) {
+  if (file == kInvalidPlatformFileValue)
+    return nullptr;
+  int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0);
+  if (fd < 0)
+    return nullptr;
+
+  return _fdopen(fd, "w");
+}
+
+bool ClosePlatformFile(PlatformFile file) {
+  return CloseHandle(file) != 0;
+}
+
+bool RemoveFile(const std::string& path) {
+  return ::DeleteFile(ToUtf16(path).c_str()) != 0;
+}
+
+PlatformFile OpenPlatformFile(const std::string& path) {
+  return ::CreateFile(ToUtf16(path).c_str(), GENERIC_READ | GENERIC_WRITE, 0,
+                      nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+}
+
+PlatformFile CreatePlatformFile(const std::string& path) {
+  return ::CreateFile(ToUtf16(path).c_str(), GENERIC_READ | GENERIC_WRITE, 0,
+                      nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+}
+
+#else  // defined(WEBRTC_WIN)
+
+const PlatformFile kInvalidPlatformFileValue = -1;
+
+FILE* FdopenPlatformFileForWriting(PlatformFile file) {
+  return fdopen(file, "w");
+}
+
+bool ClosePlatformFile(PlatformFile file) {
+  return close(file);
+}
+
+bool RemoveFile(const std::string& path) {
+  return ::unlink(path.c_str()) == 0;
+}
+
+PlatformFile OpenPlatformFile(const std::string& path) {
+  return ::open(path.c_str(), O_RDWR);
+}
+
+PlatformFile CreatePlatformFile(const std::string& path) {
+  return ::open(path.c_str(), O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+}
+
+#endif
+
+}  // namespace rtc
diff --git a/base/platform_file.h b/base/platform_file.h
index c7396ec..27accf0 100644
--- a/base/platform_file.h
+++ b/base/platform_file.h
@@ -11,9 +11,46 @@
 #ifndef WEBRTC_BASE_PLATFORM_FILE_H_
 #define WEBRTC_BASE_PLATFORM_FILE_H_
 
+#include <stdio.h>
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/platform_file.h"
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#endif
+
+namespace rtc {
+
+#if defined(WEBRTC_WIN)
+typedef HANDLE PlatformFile;
+#elif defined(WEBRTC_POSIX)
+typedef int PlatformFile;
+#else
+#error Unsupported platform
+#endif
+
+extern const PlatformFile kInvalidPlatformFileValue;
+
+// Associates a standard FILE stream with an existing PlatformFile.
+// Note that after this function has returned a valid FILE stream,
+// the PlatformFile should no longer be used.
+FILE* FdopenPlatformFileForWriting(PlatformFile file);
+
+// Closes a PlatformFile.
+// Don't use ClosePlatformFile to close a file opened with FdopenPlatformFile.
+// Use fclose instead.
+bool ClosePlatformFile(PlatformFile file);
+
+// Removes a file in the filesystem.
+bool RemoveFile(const std::string& path);
+
+// Opens a file for reading and writing. You might want to use base/file.h
+// instead.
+PlatformFile OpenPlatformFile(const std::string& path);
+
+// Creates a new file for reading and writing. If the file already exists it
+// will be overwritten. You might want to use base/file.h instead.
+PlatformFile CreatePlatformFile(const std::string& path);
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_PLATFORM_FILE_H_
diff --git a/base/platform_thread.cc b/base/platform_thread.cc
new file mode 100644
index 0000000..d3a1a03
--- /dev/null
+++ b/base/platform_thread.cc
@@ -0,0 +1,352 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/platform_thread.h"
+
+#include "webrtc/base/atomicops.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/base/trace_event.h"
+
+#if defined(WEBRTC_LINUX)
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#endif
+
+namespace rtc {
+
+PlatformThreadId CurrentThreadId() {
+  PlatformThreadId ret;
+#if defined(WEBRTC_WIN)
+  ret = GetCurrentThreadId();
+#elif defined(WEBRTC_POSIX)
+#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
+  ret = pthread_mach_thread_np(pthread_self());
+#elif defined(WEBRTC_LINUX)
+  ret =  syscall(__NR_gettid);
+#elif defined(WEBRTC_ANDROID)
+  ret = gettid();
+#else
+  // Default implementation for nacl and solaris.
+  ret = reinterpret_cast<pid_t>(pthread_self());
+#endif
+#endif  // defined(WEBRTC_POSIX)
+  RTC_DCHECK(ret);
+  return ret;
+}
+
+PlatformThreadRef CurrentThreadRef() {
+#if defined(WEBRTC_WIN)
+  return GetCurrentThreadId();
+#elif defined(WEBRTC_POSIX)
+  return pthread_self();
+#endif
+}
+
+bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) {
+#if defined(WEBRTC_WIN)
+  return a == b;
+#elif defined(WEBRTC_POSIX)
+  return pthread_equal(a, b);
+#endif
+}
+
+void SetCurrentThreadName(const char* name) {
+#if defined(WEBRTC_WIN)
+  struct {
+    DWORD dwType;
+    LPCSTR szName;
+    DWORD dwThreadID;
+    DWORD dwFlags;
+  } threadname_info = {0x1000, name, static_cast<DWORD>(-1), 0};
+
+  __try {
+    ::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(DWORD),
+                     reinterpret_cast<ULONG_PTR*>(&threadname_info));
+  } __except (EXCEPTION_EXECUTE_HANDLER) {
+  }
+#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
+  prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name));
+#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
+  pthread_setname_np(name);
+#endif
+}
+
+namespace {
+#if defined(WEBRTC_WIN)
+void CALLBACK RaiseFlag(ULONG_PTR param) {
+  *reinterpret_cast<bool*>(param) = true;
+}
+#else
+struct ThreadAttributes {
+  ThreadAttributes() { pthread_attr_init(&attr); }
+  ~ThreadAttributes() { pthread_attr_destroy(&attr); }
+  pthread_attr_t* operator&() { return &attr; }
+  pthread_attr_t attr;
+};
+#endif  // defined(WEBRTC_WIN)
+}
+
+PlatformThread::PlatformThread(ThreadRunFunctionDeprecated func,
+                               void* obj,
+                               const char* thread_name)
+    : run_function_deprecated_(func),
+      obj_(obj),
+      name_(thread_name ? thread_name : "webrtc") {
+  RTC_DCHECK(func);
+  RTC_DCHECK(name_.length() < 64);
+  spawned_thread_checker_.DetachFromThread();
+}
+
+PlatformThread::PlatformThread(ThreadRunFunction func,
+                               void* obj,
+                               const char* thread_name,
+                               ThreadPriority priority /*= kNormalPriority*/)
+    : run_function_(func), priority_(priority), obj_(obj), name_(thread_name) {
+  RTC_DCHECK(func);
+  RTC_DCHECK(!name_.empty());
+  // TODO(tommi): Consider lowering the limit to 15 (limit on Linux).
+  RTC_DCHECK(name_.length() < 64);
+  spawned_thread_checker_.DetachFromThread();
+}
+
+PlatformThread::~PlatformThread() {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(WEBRTC_WIN)
+  RTC_DCHECK(!thread_);
+  RTC_DCHECK(!thread_id_);
+#endif  // defined(WEBRTC_WIN)
+}
+
+#if defined(WEBRTC_WIN)
+DWORD WINAPI PlatformThread::StartThread(void* param) {
+  // The GetLastError() function only returns valid results when it is called
+  // after a Win32 API function that returns a "failed" result. A crash dump
+  // contains the result from GetLastError() and to make sure it does not
+  // falsely report a Windows error we call SetLastError here.
+  ::SetLastError(ERROR_SUCCESS);
+  static_cast<PlatformThread*>(param)->Run();
+  return 0;
+}
+#else
+void* PlatformThread::StartThread(void* param) {
+  static_cast<PlatformThread*>(param)->Run();
+  return 0;
+}
+#endif  // defined(WEBRTC_WIN)
+
+void PlatformThread::Start() {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  RTC_DCHECK(!thread_) << "Thread already started?";
+#if defined(WEBRTC_WIN)
+  stop_ = false;
+
+  // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION.
+  // Set the reserved stack stack size to 1M, which is the default on Windows
+  // and Linux.
+  thread_ = ::CreateThread(nullptr, 1024 * 1024, &StartThread, this,
+                           STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_);
+  RTC_CHECK(thread_) << "CreateThread failed";
+  RTC_DCHECK(thread_id_);
+#else
+  ThreadAttributes attr;
+  // Set the stack stack size to 1M.
+  pthread_attr_setstacksize(&attr, 1024 * 1024);
+  RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this));
+#endif  // defined(WEBRTC_WIN)
+}
+
+bool PlatformThread::IsRunning() const {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+#if defined(WEBRTC_WIN)
+  return thread_ != nullptr;
+#else
+  return thread_ != 0;
+#endif  // defined(WEBRTC_WIN)
+}
+
+PlatformThreadRef PlatformThread::GetThreadRef() const {
+#if defined(WEBRTC_WIN)
+  return thread_id_;
+#else
+  return thread_;
+#endif  // defined(WEBRTC_WIN)
+}
+
+void PlatformThread::Stop() {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  if (!IsRunning())
+    return;
+
+#if defined(WEBRTC_WIN)
+  // Set stop_ to |true| on the worker thread.
+  bool queued = QueueAPC(&RaiseFlag, reinterpret_cast<ULONG_PTR>(&stop_));
+  // Queuing the APC can fail if the thread is being terminated.
+  RTC_CHECK(queued || GetLastError() == ERROR_GEN_FAILURE);
+  WaitForSingleObject(thread_, INFINITE);
+  CloseHandle(thread_);
+  thread_ = nullptr;
+  thread_id_ = 0;
+#else
+  if (!run_function_)
+    RTC_CHECK_EQ(1, AtomicOps::Increment(&stop_flag_));
+  RTC_CHECK_EQ(0, pthread_join(thread_, nullptr));
+  if (!run_function_)
+    AtomicOps::ReleaseStore(&stop_flag_, 0);
+  thread_ = 0;
+#endif  // defined(WEBRTC_WIN)
+  spawned_thread_checker_.DetachFromThread();
+}
+
+// TODO(tommi): Deprecate the loop behavior in PlatformThread.
+// * Introduce a new callback type that returns void.
+// * Remove potential for a busy loop in PlatformThread.
+// * Delegate the responsibility for how to stop the thread, to the
+//   implementation that actually uses the thread.
+// All implementations will need to be aware of how the thread should be stopped
+// and encouraging a busy polling loop, can be costly in terms of power and cpu.
+void PlatformThread::Run() {
+  // Attach the worker thread checker to this thread.
+  RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread());
+  rtc::SetCurrentThreadName(name_.c_str());
+
+  if (run_function_) {
+    SetPriority(priority_);
+    run_function_(obj_);
+    return;
+  }
+
+// TODO(tommi): Delete the rest of this function when looping isn't supported.
+#if RTC_DCHECK_IS_ON
+  // These constants control the busy loop detection algorithm below.
+  // |kMaxLoopCount| controls the limit for how many times we allow the loop
+  // to run within a period, before DCHECKing.
+  // |kPeriodToMeasureMs| controls how long that period is.
+  static const int kMaxLoopCount = 1000;
+  static const int kPeriodToMeasureMs = 100;
+  int64_t loop_stamps[kMaxLoopCount] = {};
+  int64_t sequence_nr = 0;
+#endif
+
+  do {
+    TRACE_EVENT1("webrtc", "PlatformThread::Run", "name", name_.c_str());
+
+    // The interface contract of Start/Stop is that for a successful call to
+    // Start, there should be at least one call to the run function.  So we
+    // call the function before checking |stop_|.
+    if (!run_function_deprecated_(obj_))
+      break;
+#if RTC_DCHECK_IS_ON
+    auto id = sequence_nr % kMaxLoopCount;
+    loop_stamps[id] = rtc::TimeMillis();
+    if (sequence_nr > kMaxLoopCount) {
+      auto compare_id = (id + 1) % kMaxLoopCount;
+      auto diff = loop_stamps[id] - loop_stamps[compare_id];
+      RTC_DCHECK_GE(diff, 0);
+      if (diff < kPeriodToMeasureMs) {
+        RTC_NOTREACHED() << "This thread is too busy: " << name_ << " " << diff
+                         << "ms sequence=" << sequence_nr << " "
+                         << loop_stamps[id] << " vs " << loop_stamps[compare_id]
+                         << ", " << id << " vs " << compare_id;
+      }
+    }
+    ++sequence_nr;
+#endif
+#if defined(WEBRTC_WIN)
+    // Alertable sleep to permit RaiseFlag to run and update |stop_|.
+    SleepEx(0, true);
+  } while (!stop_);
+#else
+#if defined(WEBRTC_MAC)
+    sched_yield();
+#else
+    static const struct timespec ts_null = {0};
+    nanosleep(&ts_null, nullptr);
+#endif
+  } while (!AtomicOps::AcquireLoad(&stop_flag_));
+#endif  // defined(WEBRTC_WIN)
+}
+
+bool PlatformThread::SetPriority(ThreadPriority priority) {
+#if RTC_DCHECK_IS_ON
+  if (run_function_) {
+    // The non-deprecated way of how this function gets called, is that it must
+    // be called on the worker thread itself.
+    RTC_DCHECK(!thread_checker_.CalledOnValidThread());
+    RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread());
+  } else {
+    // In the case of deprecated use of this method, it must be called on the
+    // same thread as the PlatformThread object is constructed on.
+    RTC_DCHECK(thread_checker_.CalledOnValidThread());
+    RTC_DCHECK(IsRunning());
+  }
+#endif
+
+#if defined(WEBRTC_WIN)
+  return SetThreadPriority(thread_, priority) != FALSE;
+#elif defined(__native_client__)
+  // Setting thread priorities is not supported in NaCl.
+  return true;
+#elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX)
+  // TODO(tommi): Switch to the same mechanism as Chromium uses for changing
+  // thread priorities.
+  return true;
+#else
+#ifdef WEBRTC_THREAD_RR
+  const int policy = SCHED_RR;
+#else
+  const int policy = SCHED_FIFO;
+#endif
+  const int min_prio = sched_get_priority_min(policy);
+  const int max_prio = sched_get_priority_max(policy);
+  if (min_prio == -1 || max_prio == -1) {
+    return false;
+  }
+
+  if (max_prio - min_prio <= 2)
+    return false;
+
+  // Convert webrtc priority to system priorities:
+  sched_param param;
+  const int top_prio = max_prio - 1;
+  const int low_prio = min_prio + 1;
+  switch (priority) {
+    case kLowPriority:
+      param.sched_priority = low_prio;
+      break;
+    case kNormalPriority:
+      // The -1 ensures that the kHighPriority is always greater or equal to
+      // kNormalPriority.
+      param.sched_priority = (low_prio + top_prio - 1) / 2;
+      break;
+    case kHighPriority:
+      param.sched_priority = std::max(top_prio - 2, low_prio);
+      break;
+    case kHighestPriority:
+      param.sched_priority = std::max(top_prio - 1, low_prio);
+      break;
+    case kRealtimePriority:
+      param.sched_priority = top_prio;
+      break;
+  }
+  return pthread_setschedparam(thread_, policy, &param) == 0;
+#endif  // defined(WEBRTC_WIN)
+}
+
+#if defined(WEBRTC_WIN)
+bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  RTC_DCHECK(IsRunning());
+
+  return QueueUserAPC(function, thread_, data) != FALSE;
+}
+#endif
+
+}  // namespace rtc
diff --git a/base/platform_thread.h b/base/platform_thread.h
index 626d66f..ed26ca0 100644
--- a/base/platform_thread.h
+++ b/base/platform_thread.h
@@ -11,9 +11,114 @@
 #ifndef WEBRTC_BASE_PLATFORM_THREAD_H_
 #define WEBRTC_BASE_PLATFORM_THREAD_H_
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/platform_thread.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/platform_thread_types.h"
+#include "webrtc/base/thread_checker.h"
+
+namespace rtc {
+
+PlatformThreadId CurrentThreadId();
+PlatformThreadRef CurrentThreadRef();
+
+// Compares two thread identifiers for equality.
+bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b);
+
+// Sets the current thread name.
+void SetCurrentThreadName(const char* name);
+
+// Callback function that the spawned thread will enter once spawned.
+// A return value of false is interpreted as that the function has no
+// more work to do and that the thread can be released.
+typedef bool (*ThreadRunFunctionDeprecated)(void*);
+typedef void (*ThreadRunFunction)(void*);
+
+enum ThreadPriority {
+#ifdef WEBRTC_WIN
+  kLowPriority = THREAD_PRIORITY_BELOW_NORMAL,
+  kNormalPriority = THREAD_PRIORITY_NORMAL,
+  kHighPriority = THREAD_PRIORITY_ABOVE_NORMAL,
+  kHighestPriority = THREAD_PRIORITY_HIGHEST,
+  kRealtimePriority = THREAD_PRIORITY_TIME_CRITICAL
+#else
+  kLowPriority = 1,
+  kNormalPriority = 2,
+  kHighPriority = 3,
+  kHighestPriority = 4,
+  kRealtimePriority = 5
+#endif
+};
+
+// Represents a simple worker thread.  The implementation must be assumed
+// to be single threaded, meaning that all methods of the class, must be
+// called from the same thread, including instantiation.
+class PlatformThread {
+ public:
+  PlatformThread(ThreadRunFunctionDeprecated func,
+                 void* obj,
+                 const char* thread_name);
+  PlatformThread(ThreadRunFunction func,
+                 void* obj,
+                 const char* thread_name,
+                 ThreadPriority priority = kNormalPriority);
+  virtual ~PlatformThread();
+
+  const std::string& name() const { return name_; }
+
+  // Spawns a thread and tries to set thread priority according to the priority
+  // from when CreateThread was called.
+  void Start();
+
+  bool IsRunning() const;
+
+  // Returns an identifier for the worker thread that can be used to do
+  // thread checks.
+  PlatformThreadRef GetThreadRef() const;
+
+  // Stops (joins) the spawned thread.
+  void Stop();
+
+  // Set the priority of the thread. Must be called when thread is running.
+  // TODO(tommi): Make private and only allow public support via ctor.
+  bool SetPriority(ThreadPriority priority);
+
+ protected:
+#if defined(WEBRTC_WIN)
+  // Exposed to derived classes to allow for special cases specific to Windows.
+  bool QueueAPC(PAPCFUNC apc_function, ULONG_PTR data);
+#endif
+
+ private:
+  void Run();
+
+  ThreadRunFunctionDeprecated const run_function_deprecated_ = nullptr;
+  ThreadRunFunction const run_function_ = nullptr;
+  const ThreadPriority priority_ = kNormalPriority;
+  void* const obj_;
+  // TODO(pbos): Make sure call sites use string literals and update to a const
+  // char* instead of a std::string.
+  const std::string name_;
+  rtc::ThreadChecker thread_checker_;
+  rtc::ThreadChecker spawned_thread_checker_;
+#if defined(WEBRTC_WIN)
+  static DWORD WINAPI StartThread(void* param);
+
+  bool stop_ = false;
+  HANDLE thread_ = nullptr;
+  DWORD thread_id_ = 0;
+#else
+  static void* StartThread(void* param);
+
+  // An atomic flag that we use to stop the thread. Only modified on the
+  // controlling thread and checked on the worker thread.
+  volatile int stop_flag_ = 0;
+  pthread_t thread_ = 0;
+#endif  // defined(WEBRTC_WIN)
+  RTC_DISALLOW_COPY_AND_ASSIGN(PlatformThread);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_PLATFORM_THREAD_H_
diff --git a/base/platform_thread_types.h b/base/platform_thread_types.h
index f2dbd58..546fffd 100644
--- a/base/platform_thread_types.h
+++ b/base/platform_thread_types.h
@@ -11,9 +11,22 @@
 #ifndef WEBRTC_BASE_PLATFORM_THREAD_TYPES_H_
 #define WEBRTC_BASE_PLATFORM_THREAD_TYPES_H_
 
+#if defined(WEBRTC_WIN)
+#include <winsock2.h>
+#include <windows.h>
+#elif defined(WEBRTC_POSIX)
+#include <pthread.h>
+#include <unistd.h>
+#endif
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/platform_thread_types.h"
+namespace rtc {
+#if defined(WEBRTC_WIN)
+typedef DWORD PlatformThreadId;
+typedef DWORD PlatformThreadRef;
+#elif defined(WEBRTC_POSIX)
+typedef pid_t PlatformThreadId;
+typedef pthread_t PlatformThreadRef;
+#endif
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_PLATFORM_THREAD_TYPES_H_
diff --git a/base/platform_thread_unittest.cc b/base/platform_thread_unittest.cc
new file mode 100644
index 0000000..415b9eb
--- /dev/null
+++ b/base/platform_thread_unittest.cc
@@ -0,0 +1,128 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/platform_thread.h"
+
+#include "webrtc/system_wrappers/include/sleep.h"
+#include "webrtc/test/gtest.h"
+
+namespace rtc {
+namespace {
+// Function that does nothing, and reports success.
+bool NullRunFunctionDeprecated(void* obj) {
+  webrtc::SleepMs(2);  // Hand over timeslice, prevents busy looping.
+  return true;
+}
+
+bool TooBusyRunFunction(void* obj) {
+  // Indentionally busy looping.
+  return true;
+}
+
+void NullRunFunction(void* obj) {}
+
+// Function that sets a boolean.
+bool SetFlagRunFunctionDeprecated(void* obj) {
+  bool* obj_as_bool = static_cast<bool*>(obj);
+  *obj_as_bool = true;
+  webrtc::SleepMs(0);  // Hand over timeslice, prevents busy looping.
+  return true;
+}
+
+void SetFlagRunFunction(void* obj) {
+  bool* obj_as_bool = static_cast<bool*>(obj);
+  *obj_as_bool = true;
+}
+
+}  // namespace
+
+TEST(PlatformThreadTest, StartStopDeprecated) {
+  PlatformThread thread(&NullRunFunctionDeprecated, nullptr,
+                        "PlatformThreadTest");
+  EXPECT_TRUE(thread.name() == "PlatformThreadTest");
+  EXPECT_TRUE(thread.GetThreadRef() == 0);
+  thread.Start();
+  EXPECT_TRUE(thread.GetThreadRef() != 0);
+  thread.Stop();
+  EXPECT_TRUE(thread.GetThreadRef() == 0);
+}
+
+TEST(PlatformThreadTest, StartStop2Deprecated) {
+  PlatformThread thread1(&NullRunFunctionDeprecated, nullptr,
+                         "PlatformThreadTest1");
+  PlatformThread thread2(&NullRunFunctionDeprecated, nullptr,
+                         "PlatformThreadTest2");
+  EXPECT_TRUE(thread1.GetThreadRef() == thread2.GetThreadRef());
+  thread1.Start();
+  thread2.Start();
+  EXPECT_TRUE(thread1.GetThreadRef() != thread2.GetThreadRef());
+  thread2.Stop();
+  thread1.Stop();
+}
+
+TEST(PlatformThreadTest, RunFunctionIsCalledDeprecated) {
+  bool flag = false;
+  PlatformThread thread(&SetFlagRunFunctionDeprecated, &flag,
+                        "RunFunctionIsCalled");
+  thread.Start();
+
+  // At this point, the flag may be either true or false.
+  thread.Stop();
+
+  // We expect the thread to have run at least once.
+  EXPECT_TRUE(flag);
+}
+
+TEST(PlatformThreadTest, StartStop) {
+  PlatformThread thread(&NullRunFunction, nullptr, "PlatformThreadTest");
+  EXPECT_TRUE(thread.name() == "PlatformThreadTest");
+  EXPECT_TRUE(thread.GetThreadRef() == 0);
+  thread.Start();
+  EXPECT_TRUE(thread.GetThreadRef() != 0);
+  thread.Stop();
+  EXPECT_TRUE(thread.GetThreadRef() == 0);
+}
+
+TEST(PlatformThreadTest, StartStop2) {
+  PlatformThread thread1(&NullRunFunction, nullptr, "PlatformThreadTest1");
+  PlatformThread thread2(&NullRunFunction, nullptr, "PlatformThreadTest2");
+  EXPECT_TRUE(thread1.GetThreadRef() == thread2.GetThreadRef());
+  thread1.Start();
+  thread2.Start();
+  EXPECT_TRUE(thread1.GetThreadRef() != thread2.GetThreadRef());
+  thread2.Stop();
+  thread1.Stop();
+}
+
+TEST(PlatformThreadTest, RunFunctionIsCalled) {
+  bool flag = false;
+  PlatformThread thread(&SetFlagRunFunction, &flag, "RunFunctionIsCalled");
+  thread.Start();
+
+  // At this point, the flag may be either true or false.
+  thread.Stop();
+
+  // We expect the thread to have run at least once.
+  EXPECT_TRUE(flag);
+}
+
+// This test is disabled since it will cause a crash.
+// There might be a way to implement this as a death test, but it looks like
+// a death test requires an expression to be checked but does not allow a
+// flag to be raised that says "some thread will crash after this point".
+// TODO(tommi): Look into ways to enable the test by default.
+TEST(PlatformThreadTest, DISABLED_TooBusyDeprecated) {
+  PlatformThread thread(&TooBusyRunFunction, nullptr, "BusyThread");
+  thread.Start();
+  webrtc::SleepMs(1000);
+  thread.Stop();
+}
+
+}  // rtc
diff --git a/base/protobuf_utils.h b/base/protobuf_utils.h
index 3d2dd86..69f47cf 100644
--- a/base/protobuf_utils.h
+++ b/base/protobuf_utils.h
@@ -13,9 +13,24 @@
 #ifndef WEBRTC_BASE_PROTOBUF_UTILS_H_
 #define WEBRTC_BASE_PROTOBUF_UTILS_H_
 
+namespace webrtc {
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/protobuf_utils.h"
+using ProtoString = std::string;
+
+}  // namespace webrtc
+
+#if WEBRTC_ENABLE_PROTOBUF
+
+#include "third_party/protobuf/src/google/protobuf/message_lite.h"
+#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
+
+namespace webrtc {
+
+using google::protobuf::MessageLite;
+using google::protobuf::RepeatedPtrField;
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_ENABLE_PROTOBUF
 
 #endif  // WEBRTC_BASE_PROTOBUF_UTILS_H_
diff --git a/base/proxy_unittest.cc b/base/proxy_unittest.cc
new file mode 100644
index 0000000..f7668b5
--- /dev/null
+++ b/base/proxy_unittest.cc
@@ -0,0 +1,77 @@
+/*
+ *  Copyright 2009 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <string>
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/httpserver.h"
+#include "webrtc/base/proxyserver.h"
+#include "webrtc/base/socketadapters.h"
+#include "webrtc/base/testclient.h"
+#include "webrtc/base/testechoserver.h"
+#include "webrtc/base/virtualsocketserver.h"
+
+using rtc::Socket;
+using rtc::Thread;
+using rtc::SocketAddress;
+
+static const SocketAddress kSocksProxyIntAddr("1.2.3.4", 1080);
+static const SocketAddress kSocksProxyExtAddr("1.2.3.5", 0);
+static const SocketAddress kHttpsProxyIntAddr("1.2.3.4", 443);
+static const SocketAddress kHttpsProxyExtAddr("1.2.3.5", 0);
+static const SocketAddress kBogusProxyIntAddr("1.2.3.4", 999);
+
+// Sets up a virtual socket server and HTTPS/SOCKS5 proxy servers.
+class ProxyTest : public testing::Test {
+ public:
+  ProxyTest() : ss_(new rtc::VirtualSocketServer()), thread_(ss_.get()) {
+    socks_.reset(new rtc::SocksProxyServer(
+        ss_.get(), kSocksProxyIntAddr, ss_.get(), kSocksProxyExtAddr));
+    https_.reset(new rtc::HttpListenServer());
+    https_->Listen(kHttpsProxyIntAddr);
+  }
+  ~ProxyTest() {}
+
+  rtc::SocketServer* ss() { return ss_.get(); }
+
+ private:
+  std::unique_ptr<rtc::SocketServer> ss_;
+  rtc::AutoSocketServerThread thread_;
+  std::unique_ptr<rtc::SocksProxyServer> socks_;
+  // TODO: Make this a real HTTPS proxy server.
+  std::unique_ptr<rtc::HttpListenServer> https_;
+};
+
+// Tests whether we can use a SOCKS5 proxy to connect to a server.
+TEST_F(ProxyTest, TestSocks5Connect) {
+  rtc::AsyncSocket* socket =
+      ss()->CreateAsyncSocket(kSocksProxyIntAddr.family(), SOCK_STREAM);
+  rtc::AsyncSocksProxySocket* proxy_socket =
+      new rtc::AsyncSocksProxySocket(socket, kSocksProxyIntAddr,
+                                           "", rtc::CryptString());
+  // TODO: IPv6-ize these tests when proxy supports IPv6.
+
+  rtc::TestEchoServer server(Thread::Current(),
+                                   SocketAddress(INADDR_ANY, 0));
+
+  std::unique_ptr<rtc::AsyncTCPSocket> packet_socket(
+      rtc::AsyncTCPSocket::Create(proxy_socket, SocketAddress(INADDR_ANY, 0),
+                                  server.address()));
+  EXPECT_TRUE(packet_socket != nullptr);
+  rtc::TestClient client(std::move(packet_socket));
+
+  EXPECT_EQ(Socket::CS_CONNECTING, proxy_socket->GetState());
+  EXPECT_TRUE(client.CheckConnected());
+  EXPECT_EQ(Socket::CS_CONNECTED, proxy_socket->GetState());
+  EXPECT_EQ(server.address(), client.remote_address());
+  client.Send("foo", 3);
+  EXPECT_TRUE(client.CheckNextPacket("foo", 3, nullptr));
+  EXPECT_TRUE(client.CheckNoPacket());
+}
diff --git a/base/proxyinfo.cc b/base/proxyinfo.cc
new file mode 100644
index 0000000..76c7708
--- /dev/null
+++ b/base/proxyinfo.cc
@@ -0,0 +1,24 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/proxyinfo.h"
+
+namespace rtc {
+
+const char * ProxyToString(ProxyType proxy) {
+  const char * const PROXY_NAMES[] = { "none", "https", "socks5", "unknown" };
+  return PROXY_NAMES[proxy];
+}
+
+ProxyInfo::ProxyInfo() : type(PROXY_NONE), autodetect(false) {
+}
+ProxyInfo::~ProxyInfo() = default;
+
+} // namespace rtc
diff --git a/base/proxyinfo.h b/base/proxyinfo.h
index f0ae182..2251b13 100644
--- a/base/proxyinfo.h
+++ b/base/proxyinfo.h
@@ -8,12 +8,36 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_PROXYINFO_H_
-#define WEBRTC_BASE_PROXYINFO_H_
+#ifndef WEBRTC_BASE_PROXYINFO_H__
+#define WEBRTC_BASE_PROXYINFO_H__
 
+#include <string>
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/cryptstring.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/proxyinfo.h"
+namespace rtc {
 
-#endif // WEBRTC_BASE_PROXYINFO_H_
+enum ProxyType {
+  PROXY_NONE,
+  PROXY_HTTPS,
+  PROXY_SOCKS5,
+  PROXY_UNKNOWN
+};
+const char * ProxyToString(ProxyType proxy);
+
+struct ProxyInfo {
+  ProxyType type;
+  SocketAddress address;
+  std::string autoconfig_url;
+  bool autodetect;
+  std::string bypass_list;
+  std::string username;
+  CryptString password;
+
+  ProxyInfo();
+  ~ProxyInfo();
+};
+
+} // namespace rtc
+
+#endif // WEBRTC_BASE_PROXYINFO_H__
diff --git a/base/proxyserver.cc b/base/proxyserver.cc
new file mode 100644
index 0000000..713a986
--- /dev/null
+++ b/base/proxyserver.cc
@@ -0,0 +1,156 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/proxyserver.h"
+
+#include <algorithm>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/socketfactory.h"
+
+namespace rtc {
+
+// ProxyServer
+ProxyServer::ProxyServer(
+    SocketFactory* int_factory, const SocketAddress& int_addr,
+    SocketFactory* ext_factory, const SocketAddress& ext_ip)
+    : ext_factory_(ext_factory), ext_ip_(ext_ip.ipaddr(), 0),  // strip off port
+      server_socket_(int_factory->CreateAsyncSocket(int_addr.family(),
+                                                    SOCK_STREAM)) {
+  RTC_DCHECK(server_socket_.get() != nullptr);
+  RTC_DCHECK(int_addr.family() == AF_INET || int_addr.family() == AF_INET6);
+  server_socket_->Bind(int_addr);
+  server_socket_->Listen(5);
+  server_socket_->SignalReadEvent.connect(this, &ProxyServer::OnAcceptEvent);
+}
+
+ProxyServer::~ProxyServer() {
+  for (BindingList::iterator it = bindings_.begin();
+       it != bindings_.end(); ++it) {
+    delete (*it);
+  }
+}
+
+SocketAddress ProxyServer::GetServerAddress() {
+  return server_socket_->GetLocalAddress();
+}
+
+void ProxyServer::OnAcceptEvent(AsyncSocket* socket) {
+  RTC_DCHECK(socket != nullptr && socket == server_socket_.get());
+  AsyncSocket* int_socket = socket->Accept(nullptr);
+  AsyncProxyServerSocket* wrapped_socket = WrapSocket(int_socket);
+  AsyncSocket* ext_socket = ext_factory_->CreateAsyncSocket(ext_ip_.family(),
+                                                            SOCK_STREAM);
+  if (ext_socket) {
+    ext_socket->Bind(ext_ip_);
+    bindings_.push_back(new ProxyBinding(wrapped_socket, ext_socket));
+  } else {
+    LOG(LS_ERROR) << "Unable to create external socket on proxy accept event";
+  }
+}
+
+void ProxyServer::OnBindingDestroyed(ProxyBinding* binding) {
+  BindingList::iterator it =
+      std::find(bindings_.begin(), bindings_.end(), binding);
+  delete (*it);
+  bindings_.erase(it);
+}
+
+// ProxyBinding
+ProxyBinding::ProxyBinding(AsyncProxyServerSocket* int_socket,
+                           AsyncSocket* ext_socket)
+    : int_socket_(int_socket), ext_socket_(ext_socket), connected_(false),
+      out_buffer_(kBufferSize), in_buffer_(kBufferSize) {
+  int_socket_->SignalConnectRequest.connect(this,
+                                            &ProxyBinding::OnConnectRequest);
+  int_socket_->SignalReadEvent.connect(this, &ProxyBinding::OnInternalRead);
+  int_socket_->SignalWriteEvent.connect(this, &ProxyBinding::OnInternalWrite);
+  int_socket_->SignalCloseEvent.connect(this, &ProxyBinding::OnInternalClose);
+  ext_socket_->SignalConnectEvent.connect(this,
+                                          &ProxyBinding::OnExternalConnect);
+  ext_socket_->SignalReadEvent.connect(this, &ProxyBinding::OnExternalRead);
+  ext_socket_->SignalWriteEvent.connect(this, &ProxyBinding::OnExternalWrite);
+  ext_socket_->SignalCloseEvent.connect(this, &ProxyBinding::OnExternalClose);
+}
+
+ProxyBinding::~ProxyBinding() = default;
+
+void ProxyBinding::OnConnectRequest(AsyncProxyServerSocket* socket,
+                                   const SocketAddress& addr) {
+  RTC_DCHECK(!connected_ && ext_socket_.get() != nullptr);
+  ext_socket_->Connect(addr);
+  // TODO: handle errors here
+}
+
+void ProxyBinding::OnInternalRead(AsyncSocket* socket) {
+  Read(int_socket_.get(), &out_buffer_);
+  Write(ext_socket_.get(), &out_buffer_);
+}
+
+void ProxyBinding::OnInternalWrite(AsyncSocket* socket) {
+  Write(int_socket_.get(), &in_buffer_);
+}
+
+void ProxyBinding::OnInternalClose(AsyncSocket* socket, int err) {
+  Destroy();
+}
+
+void ProxyBinding::OnExternalConnect(AsyncSocket* socket) {
+  RTC_DCHECK(socket != nullptr);
+  connected_ = true;
+  int_socket_->SendConnectResult(0, socket->GetRemoteAddress());
+}
+
+void ProxyBinding::OnExternalRead(AsyncSocket* socket) {
+  Read(ext_socket_.get(), &in_buffer_);
+  Write(int_socket_.get(), &in_buffer_);
+}
+
+void ProxyBinding::OnExternalWrite(AsyncSocket* socket) {
+  Write(ext_socket_.get(), &out_buffer_);
+}
+
+void ProxyBinding::OnExternalClose(AsyncSocket* socket, int err) {
+  if (!connected_) {
+    int_socket_->SendConnectResult(err, SocketAddress());
+  }
+  Destroy();
+}
+
+void ProxyBinding::Read(AsyncSocket* socket, FifoBuffer* buffer) {
+  // Only read if the buffer is empty.
+  RTC_DCHECK(socket != nullptr);
+  size_t size;
+  int read;
+  if (buffer->GetBuffered(&size) && size == 0) {
+    void* p = buffer->GetWriteBuffer(&size);
+    read = socket->Recv(p, size, nullptr);
+    buffer->ConsumeWriteBuffer(std::max(read, 0));
+  }
+}
+
+void ProxyBinding::Write(AsyncSocket* socket, FifoBuffer* buffer) {
+  RTC_DCHECK(socket != nullptr);
+  size_t size;
+  int written;
+  const void* p = buffer->GetReadData(&size);
+  written = socket->Send(p, size);
+  buffer->ConsumeReadData(std::max(written, 0));
+}
+
+void ProxyBinding::Destroy() {
+  SignalDestroyed(this);
+}
+
+AsyncProxyServerSocket* SocksProxyServer::WrapSocket(AsyncSocket* socket) {
+  return new AsyncSocksProxyServerSocket(socket);
+}
+
+}  // namespace rtc
diff --git a/base/proxyserver.h b/base/proxyserver.h
index 1bf580a..86007c3 100644
--- a/base/proxyserver.h
+++ b/base/proxyserver.h
@@ -11,9 +11,90 @@
 #ifndef WEBRTC_BASE_PROXYSERVER_H_
 #define WEBRTC_BASE_PROXYSERVER_H_
 
+#include <list>
+#include <memory>
+#include "webrtc/base/asyncsocket.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/socketadapters.h"
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/stream.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/proxyserver.h"
+namespace rtc {
+
+class SocketFactory;
+
+// ProxyServer is a base class that allows for easy construction of proxy
+// servers. With its helper class ProxyBinding, it contains all the necessary
+// logic for receiving and bridging connections. The specific client-server
+// proxy protocol is implemented by an instance of the AsyncProxyServerSocket
+// class; children of ProxyServer implement WrapSocket appropriately to return
+// the correct protocol handler.
+
+class ProxyBinding : public sigslot::has_slots<> {
+ public:
+  ProxyBinding(AsyncProxyServerSocket* in_socket, AsyncSocket* out_socket);
+  ~ProxyBinding() override;
+  sigslot::signal1<ProxyBinding*> SignalDestroyed;
+
+ private:
+  void OnConnectRequest(AsyncProxyServerSocket* socket,
+                        const SocketAddress& addr);
+  void OnInternalRead(AsyncSocket* socket);
+  void OnInternalWrite(AsyncSocket* socket);
+  void OnInternalClose(AsyncSocket* socket, int err);
+  void OnExternalConnect(AsyncSocket* socket);
+  void OnExternalRead(AsyncSocket* socket);
+  void OnExternalWrite(AsyncSocket* socket);
+  void OnExternalClose(AsyncSocket* socket, int err);
+
+  static void Read(AsyncSocket* socket, FifoBuffer* buffer);
+  static void Write(AsyncSocket* socket, FifoBuffer* buffer);
+  void Destroy();
+
+  static const int kBufferSize = 4096;
+  std::unique_ptr<AsyncProxyServerSocket> int_socket_;
+  std::unique_ptr<AsyncSocket> ext_socket_;
+  bool connected_;
+  FifoBuffer out_buffer_;
+  FifoBuffer in_buffer_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(ProxyBinding);
+};
+
+class ProxyServer : public sigslot::has_slots<> {
+ public:
+  ProxyServer(SocketFactory* int_factory, const SocketAddress& int_addr,
+              SocketFactory* ext_factory, const SocketAddress& ext_ip);
+  ~ProxyServer() override;
+
+  // Returns the address to which the proxy server is bound
+  SocketAddress GetServerAddress();
+
+ protected:
+  void OnAcceptEvent(AsyncSocket* socket);
+  virtual AsyncProxyServerSocket* WrapSocket(AsyncSocket* socket) = 0;
+  void OnBindingDestroyed(ProxyBinding* binding);
+
+ private:
+  typedef std::list<ProxyBinding*> BindingList;
+  SocketFactory* ext_factory_;
+  SocketAddress ext_ip_;
+  std::unique_ptr<AsyncSocket> server_socket_;
+  BindingList bindings_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(ProxyServer);
+};
+
+// SocksProxyServer is a simple extension of ProxyServer to implement SOCKS.
+class SocksProxyServer : public ProxyServer {
+ public:
+  SocksProxyServer(SocketFactory* int_factory, const SocketAddress& int_addr,
+                   SocketFactory* ext_factory, const SocketAddress& ext_ip)
+      : ProxyServer(int_factory, int_addr, ext_factory, ext_ip) {
+  }
+ protected:
+  AsyncProxyServerSocket* WrapSocket(AsyncSocket* socket) override;
+  RTC_DISALLOW_COPY_AND_ASSIGN(SocksProxyServer);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_PROXYSERVER_H_
diff --git a/base/ptr_util.h b/base/ptr_util.h
index aa6f3b4..43895c7 100644
--- a/base/ptr_util.h
+++ b/base/ptr_util.h
@@ -13,9 +13,70 @@
 #ifndef WEBRTC_BASE_PTR_UTIL_H_
 #define WEBRTC_BASE_PTR_UTIL_H_
 
+#include <memory>
+#include <utility>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/ptr_util.h"
+namespace rtc {
+
+// Helper to transfer ownership of a raw pointer to a std::unique_ptr<T>.
+// Note that std::unique_ptr<T> has very different semantics from
+// std::unique_ptr<T[]>: do not use this helper for array allocations.
+template <typename T>
+std::unique_ptr<T> WrapUnique(T* ptr) {
+  return std::unique_ptr<T>(ptr);
+}
+
+namespace internal {
+
+template <typename T>
+struct MakeUniqueResult {
+  using Scalar = std::unique_ptr<T>;
+};
+
+template <typename T>
+struct MakeUniqueResult<T[]> {
+  using Array = std::unique_ptr<T[]>;
+};
+
+template <typename T, size_t N>
+struct MakeUniqueResult<T[N]> {
+  using Invalid = void;
+};
+
+}  // namespace internal
+
+// Helper to construct an object wrapped in a std::unique_ptr. This is an
+// implementation of C++14's std::make_unique that can be used in Chrome.
+//
+// MakeUnique<T>(args) should be preferred over WrapUnique(new T(args)): bare
+// calls to `new` should be treated with scrutiny.
+//
+// Usage:
+//   // ptr is a std::unique_ptr<std::string>
+//   auto ptr = MakeUnique<std::string>("hello world!");
+//
+//   // arr is a std::unique_ptr<int[]>
+//   auto arr = MakeUnique<int[]>(5);
+
+// Overload for non-array types. Arguments are forwarded to T's constructor.
+template <typename T, typename... Args>
+typename internal::MakeUniqueResult<T>::Scalar MakeUnique(Args&&... args) {
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+// Overload for array types of unknown bound, e.g. T[]. The array is allocated
+// with `new T[n]()` and value-initialized: note that this is distinct from
+// `new T[n]`, which default-initializes.
+template <typename T>
+typename internal::MakeUniqueResult<T>::Array MakeUnique(size_t size) {
+  return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
+}
+
+// Overload to reject array types of known bound, e.g. T[n].
+template <typename T, typename... Args>
+typename internal::MakeUniqueResult<T>::Invalid MakeUnique(Args&&... args) =
+    delete;
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_PTR_UTIL_H_
diff --git a/base/ptr_util_unittest.cc b/base/ptr_util_unittest.cc
new file mode 100644
index 0000000..22669c5
--- /dev/null
+++ b/base/ptr_util_unittest.cc
@@ -0,0 +1,69 @@
+/*
+ *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/ptr_util.h"
+
+#include <stddef.h>
+#include <string>
+
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+namespace {
+
+class DeleteCounter {
+ public:
+  DeleteCounter() { ++count_; }
+  ~DeleteCounter() { --count_; }
+
+  static size_t count() { return count_; }
+
+ private:
+  static size_t count_;
+};
+
+size_t DeleteCounter::count_ = 0;
+
+}  // namespace
+
+TEST(PtrUtilTest, WrapUnique) {
+  EXPECT_EQ(0u, DeleteCounter::count());
+  DeleteCounter* counter = new DeleteCounter;
+  EXPECT_EQ(1u, DeleteCounter::count());
+  std::unique_ptr<DeleteCounter> owned_counter = WrapUnique(counter);
+  EXPECT_EQ(1u, DeleteCounter::count());
+  owned_counter.reset();
+  EXPECT_EQ(0u, DeleteCounter::count());
+}
+
+TEST(PtrUtilTest, MakeUniqueScalar) {
+  auto s = MakeUnique<std::string>();
+  EXPECT_EQ("", *s);
+
+  auto s2 = MakeUnique<std::string>("test");
+  EXPECT_EQ("test", *s2);
+}
+
+TEST(PtrUtilTest, MakeUniqueScalarWithMoveOnlyType) {
+  using MoveOnly = std::unique_ptr<std::string>;
+  auto p = MakeUnique<MoveOnly>(MakeUnique<std::string>("test"));
+  EXPECT_EQ("test", **p);
+}
+
+TEST(PtrUtilTest, MakeUniqueArray) {
+  EXPECT_EQ(0u, DeleteCounter::count());
+  auto a = MakeUnique<DeleteCounter[]>(5);
+  EXPECT_EQ(5u, DeleteCounter::count());
+  a.reset();
+  EXPECT_EQ(0u, DeleteCounter::count());
+}
+
+}  // namespace rtc
diff --git a/base/race_checker.cc b/base/race_checker.cc
new file mode 100644
index 0000000..92bdd7b
--- /dev/null
+++ b/base/race_checker.cc
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/race_checker.h"
+
+namespace rtc {
+
+RaceChecker::RaceChecker() {}
+
+// Note that the implementation here is in itself racy, but we pretend it does
+// not matter because we want this useful in release builds without having to
+// pay the cost of using atomics. A race hitting the race checker is likely to
+// cause access_count_ to diverge from zero and therefore cause the ThreadRef
+// comparison to fail, signaling a race, although it may not be in the exact
+// spot where a race *first* appeared in the code we're trying to protect. There
+// is also a chance that an actual race is missed, however the probability of
+// that has been considered small enough to be an acceptable trade off.
+bool RaceChecker::Acquire() const {
+  const PlatformThreadRef current_thread = CurrentThreadRef();
+  // Set new accessing thread if this is a new use.
+  if (access_count_++ == 0)
+    accessing_thread_ = current_thread;
+  // If this is being used concurrently this check will fail for the second
+  // thread entering since it won't set the thread. Recursive use of checked
+  // methods are OK since the accessing thread remains the same.
+  const PlatformThreadRef accessing_thread = accessing_thread_;
+  return IsThreadRefEqual(accessing_thread, current_thread);
+}
+
+void RaceChecker::Release() const {
+  --access_count_;
+}
+
+namespace internal {
+RaceCheckerScope::RaceCheckerScope(const RaceChecker* race_checker)
+    : race_checker_(race_checker), race_check_ok_(race_checker->Acquire()) {}
+
+bool RaceCheckerScope::RaceDetected() const {
+  return !race_check_ok_;
+}
+
+RaceCheckerScope::~RaceCheckerScope() {
+  race_checker_->Release();
+}
+
+}  // namespace internal
+}  // namespace rtc
diff --git a/base/race_checker.h b/base/race_checker.h
index 474fdb5..a6ba771 100644
--- a/base/race_checker.h
+++ b/base/race_checker.h
@@ -11,9 +11,68 @@
 #ifndef WEBRTC_BASE_RACE_CHECKER_H_
 #define WEBRTC_BASE_RACE_CHECKER_H_
 
+#include "webrtc/base/checks.h"
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/thread_annotations.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/race_checker.h"
+namespace rtc {
+
+namespace internal {
+class RaceCheckerScope;
+}  // namespace internal
+
+// Best-effort race-checking implementation. This primitive uses no
+// synchronization at all to be as-fast-as-possible in the non-racy case.
+class LOCKABLE RaceChecker {
+ public:
+  friend class internal::RaceCheckerScope;
+  RaceChecker();
+
+ private:
+  bool Acquire() const EXCLUSIVE_LOCK_FUNCTION();
+  void Release() const UNLOCK_FUNCTION();
+
+  // Volatile to prevent code being optimized away in Acquire()/Release().
+  mutable volatile int access_count_ = 0;
+  mutable volatile PlatformThreadRef accessing_thread_;
+};
+
+namespace internal {
+class SCOPED_LOCKABLE RaceCheckerScope {
+ public:
+  explicit RaceCheckerScope(const RaceChecker* race_checker)
+      EXCLUSIVE_LOCK_FUNCTION(race_checker);
+
+  bool RaceDetected() const;
+  ~RaceCheckerScope() UNLOCK_FUNCTION();
+
+ private:
+  const RaceChecker* const race_checker_;
+  const bool race_check_ok_;
+};
+
+class SCOPED_LOCKABLE RaceCheckerScopeDoNothing {
+ public:
+  explicit RaceCheckerScopeDoNothing(const RaceChecker* race_checker)
+      EXCLUSIVE_LOCK_FUNCTION(race_checker) {}
+
+  ~RaceCheckerScopeDoNothing() UNLOCK_FUNCTION() {}
+};
+
+}  // namespace internal
+}  // namespace rtc
+
+#define RTC_CHECK_RUNS_SERIALIZED(x)               \
+  rtc::internal::RaceCheckerScope race_checker(x); \
+  RTC_CHECK(!race_checker.RaceDetected())
+
+#if RTC_DCHECK_IS_ON
+#define RTC_DCHECK_RUNS_SERIALIZED(x)              \
+  rtc::internal::RaceCheckerScope race_checker(x); \
+  RTC_DCHECK(!race_checker.RaceDetected())
+#else
+#define RTC_DCHECK_RUNS_SERIALIZED(x) \
+  rtc::internal::RaceCheckerScopeDoNothing race_checker(x)
+#endif
 
 #endif  // WEBRTC_BASE_RACE_CHECKER_H_
diff --git a/base/random.cc b/base/random.cc
new file mode 100644
index 0000000..14a9faf
--- /dev/null
+++ b/base/random.cc
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/base/random.h"
+
+#include <math.h>
+
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+Random::Random(uint64_t seed) {
+  RTC_DCHECK(seed != 0x0ull);
+  state_ = seed;
+}
+
+uint32_t Random::Rand(uint32_t t) {
+  // Casting the output to 32 bits will give an almost uniform number.
+  // Pr[x=0] = (2^32-1) / (2^64-1)
+  // Pr[x=k] = 2^32 / (2^64-1) for k!=0
+  // Uniform would be Pr[x=k] = 2^32 / 2^64 for all 32-bit integers k.
+  uint32_t x = NextOutput();
+  // If x / 2^32 is uniform on [0,1), then x / 2^32 * (t+1) is uniform on
+  // the interval [0,t+1), so the integer part is uniform on [0,t].
+  uint64_t result = x * (static_cast<uint64_t>(t) + 1);
+  result >>= 32;
+  return result;
+}
+
+uint32_t Random::Rand(uint32_t low, uint32_t high) {
+  RTC_DCHECK(low <= high);
+  return Rand(high - low) + low;
+}
+
+int32_t Random::Rand(int32_t low, int32_t high) {
+  RTC_DCHECK(low <= high);
+  // We rely on subtraction (and addition) to be the same for signed and
+  // unsigned numbers in two-complement representation. Thus, although
+  // high - low might be negative as an int, it is the correct difference
+  // when interpreted as an unsigned.
+  return Rand(high - low) + low;
+}
+
+template <>
+float Random::Rand<float>() {
+  double result = NextOutput() - 1;
+  result = result / 0xFFFFFFFFFFFFFFFEull;
+  return static_cast<float>(result);
+}
+
+template <>
+double Random::Rand<double>() {
+  double result = NextOutput() - 1;
+  result = result / 0xFFFFFFFFFFFFFFFEull;
+  return result;
+}
+
+template <>
+bool Random::Rand<bool>() {
+  return Rand(0, 1) == 1;
+}
+
+double Random::Gaussian(double mean, double standard_deviation) {
+  // Creating a Normal distribution variable from two independent uniform
+  // variables based on the Box-Muller transform, which is defined on the
+  // interval (0, 1]. Note that we rely on NextOutput to generate integers
+  // in the range [1, 2^64-1]. Normally this behavior is a bit frustrating,
+  // but here it is exactly what we need.
+  const double kPi = 3.14159265358979323846;
+  double u1 = static_cast<double>(NextOutput()) / 0xFFFFFFFFFFFFFFFFull;
+  double u2 = static_cast<double>(NextOutput()) / 0xFFFFFFFFFFFFFFFFull;
+  return mean + standard_deviation * sqrt(-2 * log(u1)) * cos(2 * kPi * u2);
+}
+
+double Random::Exponential(double lambda) {
+  double uniform = Rand<double>();
+  return -log(uniform) / lambda;
+}
+
+}  // namespace webrtc
diff --git a/base/random.h b/base/random.h
index 12a4902..cb7b9eb 100644
--- a/base/random.h
+++ b/base/random.h
@@ -11,9 +11,83 @@
 #ifndef WEBRTC_BASE_RANDOM_H_
 #define WEBRTC_BASE_RANDOM_H_
 
+#include <limits>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/random.h"
+#include "webrtc/typedefs.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+class Random {
+ public:
+  // TODO(tommi): Change this so that the seed can be initialized internally,
+  // e.g. by offering two ways of constructing or offer a static method that
+  // returns a seed that's suitable for initialization.
+  // The problem now is that callers are calling clock_->TimeInMicroseconds()
+  // which calls TickTime::Now().Ticks(), which can return a very low value on
+  // Mac and can result in a seed of 0 after conversion to microseconds.
+  // Besides the quality of the random seed being poor, this also requires
+  // the client to take on extra dependencies to generate a seed.
+  // If we go for a static seed generator in Random, we can use something from
+  // webrtc/base and make sure that it works the same way across platforms.
+  // See also discussion here: https://codereview.webrtc.org/1623543002/
+  explicit Random(uint64_t seed);
+
+  // Return pseudo-random integer of the specified type.
+  // We need to limit the size to 32 bits to keep the output close to uniform.
+  template <typename T>
+  T Rand() {
+    static_assert(std::numeric_limits<T>::is_integer &&
+                      std::numeric_limits<T>::radix == 2 &&
+                      std::numeric_limits<T>::digits <= 32,
+                  "Rand is only supported for built-in integer types that are "
+                  "32 bits or smaller.");
+    return static_cast<T>(NextOutput());
+  }
+
+  // Uniformly distributed pseudo-random number in the interval [0, t].
+  uint32_t Rand(uint32_t t);
+
+  // Uniformly distributed pseudo-random number in the interval [low, high].
+  uint32_t Rand(uint32_t low, uint32_t high);
+
+  // Uniformly distributed pseudo-random number in the interval [low, high].
+  int32_t Rand(int32_t low, int32_t high);
+
+  // Normal Distribution.
+  double Gaussian(double mean, double standard_deviation);
+
+  // Exponential Distribution.
+  double Exponential(double lambda);
+
+ private:
+  // Outputs a nonzero 64-bit random number.
+  uint64_t NextOutput() {
+    state_ ^= state_ >> 12;
+    state_ ^= state_ << 25;
+    state_ ^= state_ >> 27;
+    RTC_DCHECK(state_ != 0x0ULL);
+    return state_ * 2685821657736338717ull;
+  }
+
+  uint64_t state_;
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Random);
+};
+
+// Return pseudo-random number in the interval [0.0, 1.0).
+template <>
+float Random::Rand<float>();
+
+// Return pseudo-random number in the interval [0.0, 1.0).
+template <>
+double Random::Rand<double>();
+
+// Return pseudo-random boolean value.
+template <>
+bool Random::Rand<bool>();
+
+}  // namespace webrtc
 
 #endif  // WEBRTC_BASE_RANDOM_H_
diff --git a/base/random_unittest.cc b/base/random_unittest.cc
new file mode 100644
index 0000000..704e81f
--- /dev/null
+++ b/base/random_unittest.cc
@@ -0,0 +1,309 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <math.h>
+
+#include <limits>
+#include <vector>
+
+#include "webrtc/base/mathutils.h"  // unsigned difference
+#include "webrtc/base/random.h"
+#include "webrtc/test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+// Computes the positive remainder of x/n.
+template <typename T>
+T fdiv_remainder(T x, T n) {
+  RTC_CHECK_GE(n, 0);
+  T remainder = x % n;
+  if (remainder < 0)
+    remainder += n;
+  return remainder;
+}
+}  // namespace
+
+// Sample a number of random integers of type T. Divide them into buckets
+// based on the remainder when dividing by bucket_count and check that each
+// bucket gets roughly the expected number of elements.
+template <typename T>
+void UniformBucketTest(T bucket_count, int samples, Random* prng) {
+  std::vector<int> buckets(bucket_count, 0);
+
+  uint64_t total_values = 1ull << (std::numeric_limits<T>::digits +
+                                   std::numeric_limits<T>::is_signed);
+  T upper_limit =
+      std::numeric_limits<T>::max() -
+      static_cast<T>(total_values % static_cast<uint64_t>(bucket_count));
+  ASSERT_GT(upper_limit, std::numeric_limits<T>::max() / 2);
+
+  for (int i = 0; i < samples; i++) {
+    T sample;
+    do {
+      // We exclude a few numbers from the range so that it is divisible by
+      // the number of buckets. If we are unlucky and hit one of the excluded
+      // numbers we just resample. Note that if the number of buckets is a
+      // power of 2, then we don't have to exclude anything.
+      sample = prng->Rand<T>();
+    } while (sample > upper_limit);
+    buckets[fdiv_remainder(sample, bucket_count)]++;
+  }
+
+  for (T i = 0; i < bucket_count; i++) {
+    // Expect the result to be within 3 standard deviations of the mean.
+    EXPECT_NEAR(buckets[i], samples / bucket_count,
+                3 * sqrt(samples / bucket_count));
+  }
+}
+
+TEST(RandomNumberGeneratorTest, BucketTestSignedChar) {
+  Random prng(7297352569824ull);
+  UniformBucketTest<signed char>(64, 640000, &prng);
+  UniformBucketTest<signed char>(11, 440000, &prng);
+  UniformBucketTest<signed char>(3, 270000, &prng);
+}
+
+TEST(RandomNumberGeneratorTest, BucketTestUnsignedChar) {
+  Random prng(7297352569824ull);
+  UniformBucketTest<unsigned char>(64, 640000, &prng);
+  UniformBucketTest<unsigned char>(11, 440000, &prng);
+  UniformBucketTest<unsigned char>(3, 270000, &prng);
+}
+
+TEST(RandomNumberGeneratorTest, BucketTestSignedShort) {
+  Random prng(7297352569824ull);
+  UniformBucketTest<int16_t>(64, 640000, &prng);
+  UniformBucketTest<int16_t>(11, 440000, &prng);
+  UniformBucketTest<int16_t>(3, 270000, &prng);
+}
+
+TEST(RandomNumberGeneratorTest, BucketTestUnsignedShort) {
+  Random prng(7297352569824ull);
+  UniformBucketTest<uint16_t>(64, 640000, &prng);
+  UniformBucketTest<uint16_t>(11, 440000, &prng);
+  UniformBucketTest<uint16_t>(3, 270000, &prng);
+}
+
+TEST(RandomNumberGeneratorTest, BucketTestSignedInt) {
+  Random prng(7297352569824ull);
+  UniformBucketTest<signed int>(64, 640000, &prng);
+  UniformBucketTest<signed int>(11, 440000, &prng);
+  UniformBucketTest<signed int>(3, 270000, &prng);
+}
+
+TEST(RandomNumberGeneratorTest, BucketTestUnsignedInt) {
+  Random prng(7297352569824ull);
+  UniformBucketTest<unsigned int>(64, 640000, &prng);
+  UniformBucketTest<unsigned int>(11, 440000, &prng);
+  UniformBucketTest<unsigned int>(3, 270000, &prng);
+}
+
+// The range of the random numbers is divided into bucket_count intervals
+// of consecutive numbers. Check that approximately equally many numbers
+// from each inteval are generated.
+void BucketTestSignedInterval(unsigned int bucket_count,
+                              unsigned int samples,
+                              int32_t low,
+                              int32_t high,
+                              int sigma_level,
+                              Random* prng) {
+  std::vector<unsigned int> buckets(bucket_count, 0);
+
+  ASSERT_GE(high, low);
+  ASSERT_GE(bucket_count, 2u);
+  uint32_t interval = unsigned_difference<int32_t>(high, low) + 1;
+  uint32_t numbers_per_bucket;
+  if (interval == 0) {
+    // The computation high - low + 1 should be 2^32 but overflowed
+    // Hence, bucket_count must be a power of 2
+    ASSERT_EQ(bucket_count & (bucket_count - 1), 0u);
+    numbers_per_bucket = (0x80000000u / bucket_count) * 2;
+  } else {
+    ASSERT_EQ(interval % bucket_count, 0u);
+    numbers_per_bucket = interval / bucket_count;
+  }
+
+  for (unsigned int i = 0; i < samples; i++) {
+    int32_t sample = prng->Rand(low, high);
+    EXPECT_LE(low, sample);
+    EXPECT_GE(high, sample);
+    buckets[unsigned_difference<int32_t>(sample, low) / numbers_per_bucket]++;
+  }
+
+  for (unsigned int i = 0; i < bucket_count; i++) {
+    // Expect the result to be within 3 standard deviations of the mean,
+    // or more generally, within sigma_level standard deviations of the mean.
+    double mean = static_cast<double>(samples) / bucket_count;
+    EXPECT_NEAR(buckets[i], mean, sigma_level * sqrt(mean));
+  }
+}
+
+// The range of the random numbers is divided into bucket_count intervals
+// of consecutive numbers. Check that approximately equally many numbers
+// from each inteval are generated.
+void BucketTestUnsignedInterval(unsigned int bucket_count,
+                                unsigned int samples,
+                                uint32_t low,
+                                uint32_t high,
+                                int sigma_level,
+                                Random* prng) {
+  std::vector<unsigned int> buckets(bucket_count, 0);
+
+  ASSERT_GE(high, low);
+  ASSERT_GE(bucket_count, 2u);
+  uint32_t interval = high - low + 1;
+  uint32_t numbers_per_bucket;
+  if (interval == 0) {
+    // The computation high - low + 1 should be 2^32 but overflowed
+    // Hence, bucket_count must be a power of 2
+    ASSERT_EQ(bucket_count & (bucket_count - 1), 0u);
+    numbers_per_bucket = (0x80000000u / bucket_count) * 2;
+  } else {
+    ASSERT_EQ(interval % bucket_count, 0u);
+    numbers_per_bucket = interval / bucket_count;
+  }
+
+  for (unsigned int i = 0; i < samples; i++) {
+    uint32_t sample = prng->Rand(low, high);
+    EXPECT_LE(low, sample);
+    EXPECT_GE(high, sample);
+    buckets[(sample - low) / numbers_per_bucket]++;
+  }
+
+  for (unsigned int i = 0; i < bucket_count; i++) {
+    // Expect the result to be within 3 standard deviations of the mean,
+    // or more generally, within sigma_level standard deviations of the mean.
+    double mean = static_cast<double>(samples) / bucket_count;
+    EXPECT_NEAR(buckets[i], mean, sigma_level * sqrt(mean));
+  }
+}
+
+TEST(RandomNumberGeneratorTest, UniformUnsignedInterval) {
+  Random prng(299792458ull);
+  BucketTestUnsignedInterval(2, 100000, 0, 1, 3, &prng);
+  BucketTestUnsignedInterval(7, 100000, 1, 14, 3, &prng);
+  BucketTestUnsignedInterval(11, 100000, 1000, 1010, 3, &prng);
+  BucketTestUnsignedInterval(100, 100000, 0, 99, 3, &prng);
+  BucketTestUnsignedInterval(2, 100000, 0, 4294967295, 3, &prng);
+  BucketTestUnsignedInterval(17, 100000, 455, 2147484110, 3, &prng);
+  // 99.7% of all samples will be within 3 standard deviations of the mean,
+  // but since we test 1000 buckets we allow an interval of 4 sigma.
+  BucketTestUnsignedInterval(1000, 1000000, 0, 2147483999, 4, &prng);
+}
+
+// Disabled for UBSan: https://bugs.chromium.org/p/webrtc/issues/detail?id=5491
+#ifdef UNDEFINED_SANITIZER
+#define MAYBE_UniformSignedInterval DISABLED_UniformSignedInterval
+#else
+#define MAYBE_UniformSignedInterval UniformSignedInterval
+#endif
+TEST(RandomNumberGeneratorTest, MAYBE_UniformSignedInterval) {
+  Random prng(66260695729ull);
+  BucketTestSignedInterval(2, 100000, 0, 1, 3, &prng);
+  BucketTestSignedInterval(7, 100000, -2, 4, 3, &prng);
+  BucketTestSignedInterval(11, 100000, 1000, 1010, 3, &prng);
+  BucketTestSignedInterval(100, 100000, 0, 99, 3, &prng);
+  BucketTestSignedInterval(2, 100000, std::numeric_limits<int32_t>::min(),
+                           std::numeric_limits<int32_t>::max(), 3, &prng);
+  BucketTestSignedInterval(17, 100000, -1073741826, 1073741829, 3, &prng);
+  // 99.7% of all samples will be within 3 standard deviations of the mean,
+  // but since we test 1000 buckets we allow an interval of 4 sigma.
+  BucketTestSignedInterval(1000, 1000000, -352, 2147483647, 4, &prng);
+}
+
+// The range of the random numbers is divided into bucket_count intervals
+// of consecutive numbers. Check that approximately equally many numbers
+// from each inteval are generated.
+void BucketTestFloat(unsigned int bucket_count,
+                     unsigned int samples,
+                     int sigma_level,
+                     Random* prng) {
+  ASSERT_GE(bucket_count, 2u);
+  std::vector<unsigned int> buckets(bucket_count, 0);
+
+  for (unsigned int i = 0; i < samples; i++) {
+    uint32_t sample = bucket_count * prng->Rand<float>();
+    EXPECT_LE(0u, sample);
+    EXPECT_GE(bucket_count - 1, sample);
+    buckets[sample]++;
+  }
+
+  for (unsigned int i = 0; i < bucket_count; i++) {
+    // Expect the result to be within 3 standard deviations of the mean,
+    // or more generally, within sigma_level standard deviations of the mean.
+    double mean = static_cast<double>(samples) / bucket_count;
+    EXPECT_NEAR(buckets[i], mean, sigma_level * sqrt(mean));
+  }
+}
+
+TEST(RandomNumberGeneratorTest, UniformFloatInterval) {
+  Random prng(1380648813ull);
+  BucketTestFloat(100, 100000, 3, &prng);
+  // 99.7% of all samples will be within 3 standard deviations of the mean,
+  // but since we test 1000 buckets we allow an interval of 4 sigma.
+  // BucketTestSignedInterval(1000, 1000000, -352, 2147483647, 4, &prng);
+}
+
+TEST(RandomNumberGeneratorTest, SignedHasSameBitPattern) {
+  Random prng_signed(66738480ull), prng_unsigned(66738480ull);
+
+  for (int i = 0; i < 1000; i++) {
+    signed int s = prng_signed.Rand<signed int>();
+    unsigned int u = prng_unsigned.Rand<unsigned int>();
+    EXPECT_EQ(u, static_cast<unsigned int>(s));
+  }
+
+  for (int i = 0; i < 1000; i++) {
+    int16_t s = prng_signed.Rand<int16_t>();
+    uint16_t u = prng_unsigned.Rand<uint16_t>();
+    EXPECT_EQ(u, static_cast<uint16_t>(s));
+  }
+
+  for (int i = 0; i < 1000; i++) {
+    signed char s = prng_signed.Rand<signed char>();
+    unsigned char u = prng_unsigned.Rand<unsigned char>();
+    EXPECT_EQ(u, static_cast<unsigned char>(s));
+  }
+}
+
+TEST(RandomNumberGeneratorTest, Gaussian) {
+  const int kN = 100000;
+  const int kBuckets = 100;
+  const double kMean = 49;
+  const double kStddev = 10;
+
+  Random prng(1256637061);
+
+  std::vector<unsigned int> buckets(kBuckets, 0);
+  for (int i = 0; i < kN; i++) {
+    int index = prng.Gaussian(kMean, kStddev) + 0.5;
+    if (index >= 0 && index < kBuckets) {
+      buckets[index]++;
+    }
+  }
+
+  const double kPi = 3.14159265358979323846;
+  const double kScale = 1 / (kStddev * sqrt(2.0 * kPi));
+  const double kDiv = -2.0 * kStddev * kStddev;
+  for (int n = 0; n < kBuckets; ++n) {
+    // Use Simpsons rule to estimate the probability that a random gaussian
+    // sample is in the interval [n-0.5, n+0.5].
+    double f_left = kScale * exp((n - kMean - 0.5) * (n - kMean - 0.5) / kDiv);
+    double f_mid = kScale * exp((n - kMean) * (n - kMean) / kDiv);
+    double f_right = kScale * exp((n - kMean + 0.5) * (n - kMean + 0.5) / kDiv);
+    double normal_dist = (f_left + 4 * f_mid + f_right) / 6;
+    // Expect the number of samples to be within 3 standard deviations
+    // (rounded up) of the expected number of samples in the bucket.
+    EXPECT_NEAR(buckets[n], kN * normal_dist, 3 * sqrt(kN * normal_dist) + 1);
+  }
+}
+
+}  // namespace webrtc
diff --git a/base/rate_limiter.cc b/base/rate_limiter.cc
new file mode 100644
index 0000000..9215fa0
--- /dev/null
+++ b/base/rate_limiter.cc
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/rate_limiter.h"
+#include "webrtc/system_wrappers/include/clock.h"
+
+namespace webrtc {
+
+RateLimiter::RateLimiter(const Clock* clock, int64_t max_window_ms)
+    : clock_(clock),
+      current_rate_(max_window_ms, RateStatistics::kBpsScale),
+      window_size_ms_(max_window_ms),
+      max_rate_bps_(std::numeric_limits<uint32_t>::max()) {}
+
+RateLimiter::~RateLimiter() {}
+
+// Usage note: This class is intended be usable in a scenario where different
+// threads may call each of the the different method. For instance, a network
+// thread trying to send data calling TryUseRate(), the bandwidth estimator
+// calling SetMaxRate() and a timed maintenance thread periodically updating
+// the RTT.
+bool RateLimiter::TryUseRate(size_t packet_size_bytes) {
+  rtc::CritScope cs(&lock_);
+  int64_t now_ms = clock_->TimeInMilliseconds();
+  rtc::Optional<uint32_t> current_rate = current_rate_.Rate(now_ms);
+  if (current_rate) {
+    // If there is a current rate, check if adding bytes would cause maximum
+    // bitrate target to be exceeded. If there is NOT a valid current rate,
+    // allow allocating rate even if target is exceeded. This prevents
+    // problems
+    // at very low rates, where for instance retransmissions would never be
+    // allowed due to too high bitrate caused by a single packet.
+
+    size_t bitrate_addition_bps =
+        (packet_size_bytes * 8 * 1000) / window_size_ms_;
+    if (*current_rate + bitrate_addition_bps > max_rate_bps_)
+      return false;
+  }
+
+  current_rate_.Update(packet_size_bytes, now_ms);
+  return true;
+}
+
+void RateLimiter::SetMaxRate(uint32_t max_rate_bps) {
+  rtc::CritScope cs(&lock_);
+  max_rate_bps_ = max_rate_bps;
+}
+
+// Set the window size over which to measure the current bitrate.
+// For retransmissions, this is typically the RTT.
+bool RateLimiter::SetWindowSize(int64_t window_size_ms) {
+  rtc::CritScope cs(&lock_);
+  window_size_ms_ = window_size_ms;
+  return current_rate_.SetWindowSize(window_size_ms,
+                                     clock_->TimeInMilliseconds());
+}
+
+}  // namespace webrtc
diff --git a/base/rate_limiter.h b/base/rate_limiter.h
index 0cba5fb..ceeccfc 100644
--- a/base/rate_limiter.h
+++ b/base/rate_limiter.h
@@ -11,9 +11,46 @@
 #ifndef WEBRTC_BASE_RATE_LIMITER_H_
 #define WEBRTC_BASE_RATE_LIMITER_H_
 
+#include <limits>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/rate_limiter.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/rate_statistics.h"
+
+namespace webrtc {
+
+class Clock;
+
+// Class used to limit a bitrate, making sure the average does not exceed a
+// maximum as measured over a sliding window. This class is thread safe; all
+// methods will acquire (the same) lock befeore executing.
+class RateLimiter {
+ public:
+  RateLimiter(const Clock* clock, int64_t max_window_ms);
+  ~RateLimiter();
+
+  // Try to use rate to send bytes. Returns true on success and if so updates
+  // current rate.
+  bool TryUseRate(size_t packet_size_bytes);
+
+  // Set the maximum bitrate, in bps, that this limiter allows to send.
+  void SetMaxRate(uint32_t max_rate_bps);
+
+  // Set the window size over which to measure the current bitrate.
+  // For example, irt retransmissions, this is typically the RTT.
+  // Returns true on success and false if window_size_ms is out of range.
+  bool SetWindowSize(int64_t window_size_ms);
+
+ private:
+  const Clock* const clock_;
+  rtc::CriticalSection lock_;
+  RateStatistics current_rate_ GUARDED_BY(lock_);
+  int64_t window_size_ms_ GUARDED_BY(lock_);
+  uint32_t max_rate_bps_ GUARDED_BY(lock_);
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RateLimiter);
+};
+
+}  // namespace webrtc
 
 #endif  // WEBRTC_BASE_RATE_LIMITER_H_
diff --git a/base/rate_limiter_unittest.cc b/base/rate_limiter_unittest.cc
new file mode 100644
index 0000000..6d92567
--- /dev/null
+++ b/base/rate_limiter_unittest.cc
@@ -0,0 +1,203 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+#include <memory>
+
+#include "webrtc/base/event.h"
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/rate_limiter.h"
+#include "webrtc/base/task_queue.h"
+#include "webrtc/system_wrappers/include/clock.h"
+#include "webrtc/test/gtest.h"
+
+namespace webrtc {
+
+class RateLimitTest : public ::testing::Test {
+ public:
+  RateLimitTest()
+      : clock_(0), rate_limiter(new RateLimiter(&clock_, kWindowSizeMs)) {}
+  ~RateLimitTest() override {}
+
+  void SetUp() override { rate_limiter->SetMaxRate(kMaxRateBps); }
+
+ protected:
+  static constexpr int64_t kWindowSizeMs = 1000;
+  static constexpr uint32_t kMaxRateBps = 100000;
+  // Bytes needed to completely saturate the rate limiter.
+  static constexpr size_t kRateFillingBytes =
+      (kMaxRateBps * kWindowSizeMs) / (8 * 1000);
+  SimulatedClock clock_;
+  std::unique_ptr<RateLimiter> rate_limiter;
+};
+
+TEST_F(RateLimitTest, IncreasingMaxRate) {
+  // Fill rate, extend window to full size.
+  EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
+  clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
+  EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
+
+  // All rate consumed.
+  EXPECT_FALSE(rate_limiter->TryUseRate(1));
+
+  // Double the available rate and fill that too.
+  rate_limiter->SetMaxRate(kMaxRateBps * 2);
+  EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes));
+
+  // All rate consumed again.
+  EXPECT_FALSE(rate_limiter->TryUseRate(1));
+}
+
+TEST_F(RateLimitTest, DecreasingMaxRate) {
+  // Fill rate, extend window to full size.
+  EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
+  clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
+  EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
+
+  // All rate consumed.
+  EXPECT_FALSE(rate_limiter->TryUseRate(1));
+
+  // Halve the available rate and move window so half of the data falls out.
+  rate_limiter->SetMaxRate(kMaxRateBps / 2);
+  clock_.AdvanceTimeMilliseconds(1);
+
+  // All rate still consumed.
+  EXPECT_FALSE(rate_limiter->TryUseRate(1));
+}
+
+TEST_F(RateLimitTest, ChangingWindowSize) {
+  // Fill rate, extend window to full size.
+  EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
+  clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
+  EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
+
+  // All rate consumed.
+  EXPECT_FALSE(rate_limiter->TryUseRate(1));
+
+  // Decrease window size so half of the data falls out.
+  rate_limiter->SetWindowSize(kWindowSizeMs / 2);
+  // Average rate should still be the same, so rate is still all consumed.
+  EXPECT_FALSE(rate_limiter->TryUseRate(1));
+
+  // Increase window size again. Now the rate is only half used (removed data
+  // points don't come back to life).
+  rate_limiter->SetWindowSize(kWindowSizeMs);
+  EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
+
+  // All rate consumed again.
+  EXPECT_FALSE(rate_limiter->TryUseRate(1));
+}
+
+TEST_F(RateLimitTest, SingleUsageAlwaysOk) {
+  // Using more bytes than can fit in a window is OK for a single packet.
+  EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes + 1));
+}
+
+TEST_F(RateLimitTest, WindowSizeLimits) {
+  EXPECT_TRUE(rate_limiter->SetWindowSize(1));
+  EXPECT_FALSE(rate_limiter->SetWindowSize(0));
+  EXPECT_TRUE(rate_limiter->SetWindowSize(kWindowSizeMs));
+  EXPECT_FALSE(rate_limiter->SetWindowSize(kWindowSizeMs + 1));
+}
+
+static const int64_t kMaxTimeoutMs = 30000;
+
+class ThreadTask {
+ public:
+  explicit ThreadTask(RateLimiter* rate_limiter)
+      : rate_limiter_(rate_limiter),
+        start_signal_(false, false),
+        end_signal_(false, false) {}
+  virtual ~ThreadTask() {}
+
+  void Run() {
+    start_signal_.Wait(kMaxTimeoutMs);
+    DoRun();
+    end_signal_.Set();
+  }
+
+  virtual void DoRun() = 0;
+
+  RateLimiter* const rate_limiter_;
+  rtc::Event start_signal_;
+  rtc::Event end_signal_;
+};
+
+void RunTask(void* thread_task) {
+  reinterpret_cast<ThreadTask*>(thread_task)->Run();
+}
+
+TEST_F(RateLimitTest, MultiThreadedUsage) {
+  // Simple sanity test, with different threads calling the various methods.
+  // Runs a few simple tasks, each on its own thread, but coordinated with
+  // events so that they run in a serialized order. Intended to catch data
+  // races when run with tsan et al.
+
+  // Half window size, double rate -> same amount of bytes needed to fill rate.
+
+  class SetWindowSizeTask : public ThreadTask {
+   public:
+    explicit SetWindowSizeTask(RateLimiter* rate_limiter)
+        : ThreadTask(rate_limiter) {}
+    ~SetWindowSizeTask() override {}
+
+    void DoRun() override {
+      EXPECT_TRUE(rate_limiter_->SetWindowSize(kWindowSizeMs / 2));
+    }
+  } set_window_size_task(rate_limiter.get());
+  rtc::PlatformThread thread1(RunTask, &set_window_size_task, "Thread1");
+  thread1.Start();
+
+  class SetMaxRateTask : public ThreadTask {
+   public:
+    explicit SetMaxRateTask(RateLimiter* rate_limiter)
+        : ThreadTask(rate_limiter) {}
+    ~SetMaxRateTask() override {}
+
+    void DoRun() override { rate_limiter_->SetMaxRate(kMaxRateBps * 2); }
+  } set_max_rate_task(rate_limiter.get());
+  rtc::PlatformThread thread2(RunTask, &set_max_rate_task, "Thread2");
+  thread2.Start();
+
+  class UseRateTask : public ThreadTask {
+   public:
+    UseRateTask(RateLimiter* rate_limiter, SimulatedClock* clock)
+        : ThreadTask(rate_limiter), clock_(clock) {}
+    ~UseRateTask() override {}
+
+    void DoRun() override {
+      EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
+      clock_->AdvanceTimeMilliseconds((kWindowSizeMs / 2) - 1);
+      EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
+    }
+
+    SimulatedClock* const clock_;
+  } use_rate_task(rate_limiter.get(), &clock_);
+  rtc::PlatformThread thread3(RunTask, &use_rate_task, "Thread3");
+  thread3.Start();
+
+  set_window_size_task.start_signal_.Set();
+  EXPECT_TRUE(set_window_size_task.end_signal_.Wait(kMaxTimeoutMs));
+
+  set_max_rate_task.start_signal_.Set();
+  EXPECT_TRUE(set_max_rate_task.end_signal_.Wait(kMaxTimeoutMs));
+
+  use_rate_task.start_signal_.Set();
+  EXPECT_TRUE(use_rate_task.end_signal_.Wait(kMaxTimeoutMs));
+
+  // All rate consumed.
+  EXPECT_FALSE(rate_limiter->TryUseRate(1));
+
+  thread1.Stop();
+  thread2.Stop();
+  thread3.Stop();
+}
+
+}  // namespace webrtc
diff --git a/base/rate_statistics.cc b/base/rate_statistics.cc
new file mode 100644
index 0000000..3122dbb
--- /dev/null
+++ b/base/rate_statistics.cc
@@ -0,0 +1,121 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/rate_statistics.h"
+
+#include <algorithm>
+
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+RateStatistics::RateStatistics(int64_t window_size_ms, float scale)
+    : buckets_(new Bucket[window_size_ms]()),
+      accumulated_count_(0),
+      num_samples_(0),
+      oldest_time_(-window_size_ms),
+      oldest_index_(0),
+      scale_(scale),
+      max_window_size_ms_(window_size_ms),
+      current_window_size_ms_(max_window_size_ms_) {}
+
+RateStatistics::~RateStatistics() {}
+
+void RateStatistics::Reset() {
+  accumulated_count_ = 0;
+  num_samples_ = 0;
+  oldest_time_ = -max_window_size_ms_;
+  oldest_index_ = 0;
+  current_window_size_ms_ = max_window_size_ms_;
+  for (int64_t i = 0; i < max_window_size_ms_; i++)
+    buckets_[i] = Bucket();
+}
+
+void RateStatistics::Update(size_t count, int64_t now_ms) {
+  if (now_ms < oldest_time_) {
+    // Too old data is ignored.
+    return;
+  }
+
+  EraseOld(now_ms);
+
+  // First ever sample, reset window to start now.
+  if (!IsInitialized())
+    oldest_time_ = now_ms;
+
+  uint32_t now_offset = static_cast<uint32_t>(now_ms - oldest_time_);
+  RTC_DCHECK_LT(now_offset, max_window_size_ms_);
+  uint32_t index = oldest_index_ + now_offset;
+  if (index >= max_window_size_ms_)
+    index -= max_window_size_ms_;
+  buckets_[index].sum += count;
+  ++buckets_[index].samples;
+  accumulated_count_ += count;
+  ++num_samples_;
+}
+
+rtc::Optional<uint32_t> RateStatistics::Rate(int64_t now_ms) const {
+  // Yeah, this const_cast ain't pretty, but the alternative is to declare most
+  // of the members as mutable...
+  const_cast<RateStatistics*>(this)->EraseOld(now_ms);
+
+  // If window is a single bucket or there is only one sample in a data set that
+  // has not grown to the full window size, treat this as rate unavailable.
+  int64_t active_window_size = now_ms - oldest_time_ + 1;
+  if (num_samples_ == 0 || active_window_size <= 1 ||
+      (num_samples_ <= 1 && active_window_size < current_window_size_ms_)) {
+    return rtc::Optional<uint32_t>();
+  }
+
+  float scale = scale_ / active_window_size;
+  return rtc::Optional<uint32_t>(
+      static_cast<uint32_t>(accumulated_count_ * scale + 0.5f));
+}
+
+void RateStatistics::EraseOld(int64_t now_ms) {
+  if (!IsInitialized())
+    return;
+
+  // New oldest time that is included in data set.
+  int64_t new_oldest_time = now_ms - current_window_size_ms_ + 1;
+
+  // New oldest time is older than the current one, no need to cull data.
+  if (new_oldest_time <= oldest_time_)
+    return;
+
+  // Loop over buckets and remove too old data points.
+  while (num_samples_ > 0 && oldest_time_ < new_oldest_time) {
+    const Bucket& oldest_bucket = buckets_[oldest_index_];
+    RTC_DCHECK_GE(accumulated_count_, oldest_bucket.sum);
+    RTC_DCHECK_GE(num_samples_, oldest_bucket.samples);
+    accumulated_count_ -= oldest_bucket.sum;
+    num_samples_ -= oldest_bucket.samples;
+    buckets_[oldest_index_] = Bucket();
+    if (++oldest_index_ >= max_window_size_ms_)
+      oldest_index_ = 0;
+    ++oldest_time_;
+  }
+  oldest_time_ = new_oldest_time;
+}
+
+bool RateStatistics::SetWindowSize(int64_t window_size_ms, int64_t now_ms) {
+  if (window_size_ms <= 0 || window_size_ms > max_window_size_ms_)
+    return false;
+
+  current_window_size_ms_ = window_size_ms;
+  EraseOld(now_ms);
+  return true;
+}
+
+bool RateStatistics::IsInitialized() const {
+  return oldest_time_ != -max_window_size_ms_;
+}
+
+}  // namespace webrtc
diff --git a/base/rate_statistics.h b/base/rate_statistics.h
index 1a17500..8a90a46 100644
--- a/base/rate_statistics.h
+++ b/base/rate_statistics.h
@@ -11,9 +11,74 @@
 #ifndef WEBRTC_BASE_RATE_STATISTICS_H_
 #define WEBRTC_BASE_RATE_STATISTICS_H_
 
+#include <memory>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/rate_statistics.h"
+#include "webrtc/base/optional.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class RateStatistics {
+ public:
+  static constexpr float kBpsScale = 8000.0f;
+
+  // max_window_size_ms = Maximum window size in ms for the rate estimation.
+  //                      Initial window size is set to this, but may be changed
+  //                      to something lower by calling SetWindowSize().
+  // scale = coefficient to convert counts/ms to desired unit
+  //         ex: kBpsScale (8000) for bits/s if count represents bytes.
+  RateStatistics(int64_t max_window_size_ms, float scale);
+  ~RateStatistics();
+
+  // Reset instance to original state.
+  void Reset();
+
+  // Update rate with a new data point, moving averaging window as needed.
+  void Update(size_t count, int64_t now_ms);
+
+  // Note that despite this being a const method, it still updates the internal
+  // state (moves averaging window), but it doesn't make any alterations that
+  // are observable from the other methods, as long as supplied timestamps are
+  // from a monotonic clock. Ie, it doesn't matter if this call moves the
+  // window, since any subsequent call to Update or Rate would still have moved
+  // the window as much or more.
+  rtc::Optional<uint32_t> Rate(int64_t now_ms) const;
+
+  // Update the size of the averaging window. The maximum allowed value for
+  // window_size_ms is max_window_size_ms as supplied in the constructor.
+  bool SetWindowSize(int64_t window_size_ms, int64_t now_ms);
+
+ private:
+  void EraseOld(int64_t now_ms);
+  bool IsInitialized() const;
+
+  // Counters are kept in buckets (circular buffer), with one bucket
+  // per millisecond.
+  struct Bucket {
+    size_t sum;      // Sum of all samples in this bucket.
+    size_t samples;  // Number of samples in this bucket.
+  };
+  std::unique_ptr<Bucket[]> buckets_;
+
+  // Total count recorded in buckets.
+  size_t accumulated_count_;
+
+  // The total number of samples in the buckets.
+  size_t num_samples_;
+
+  // Oldest time recorded in buckets.
+  int64_t oldest_time_;
+
+  // Bucket index of oldest counter recorded in buckets.
+  uint32_t oldest_index_;
+
+  // To convert counts/ms to desired units
+  const float scale_;
+
+  // The window sizes, in ms, over which the rate is calculated.
+  const int64_t max_window_size_ms_;
+  int64_t current_window_size_ms_;
+};
+}  // namespace webrtc
 
 #endif  // WEBRTC_BASE_RATE_STATISTICS_H_
diff --git a/base/rate_statistics_unittest.cc b/base/rate_statistics_unittest.cc
new file mode 100644
index 0000000..06136f5
--- /dev/null
+++ b/base/rate_statistics_unittest.cc
@@ -0,0 +1,280 @@
+/*
+ *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+
+#include "webrtc/base/rate_statistics.h"
+#include "webrtc/test/gtest.h"
+
+namespace {
+
+using webrtc::RateStatistics;
+
+const int64_t kWindowMs = 500;
+
+class RateStatisticsTest : public ::testing::Test {
+ protected:
+  RateStatisticsTest() : stats_(kWindowMs, 8000) {}
+  RateStatistics stats_;
+};
+
+TEST_F(RateStatisticsTest, TestStrictMode) {
+  int64_t now_ms = 0;
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  const uint32_t kPacketSize = 1500u;
+  const uint32_t kExpectedRateBps = kPacketSize * 1000 * 8;
+
+  // Single data point is not enough for valid estimate.
+  stats_.Update(kPacketSize, now_ms++);
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  // Expecting 1200 kbps since the window is initially kept small and grows as
+  // we have more data.
+  stats_.Update(kPacketSize, now_ms);
+  EXPECT_EQ(kExpectedRateBps, *stats_.Rate(now_ms));
+
+  stats_.Reset();
+  // Expecting 0 after init.
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  const int kInterval = 10;
+  for (int i = 0; i < 100000; ++i) {
+    if (i % kInterval == 0)
+      stats_.Update(kPacketSize, now_ms);
+
+    // Approximately 1200 kbps expected. Not exact since when packets
+    // are removed we will jump 10 ms to the next packet.
+    if (i > kInterval) {
+      rtc::Optional<uint32_t> rate = stats_.Rate(now_ms);
+      EXPECT_TRUE(static_cast<bool>(rate));
+      uint32_t samples = i / kInterval + 1;
+      uint64_t total_bits = samples * kPacketSize * 8;
+      uint32_t rate_bps = static_cast<uint32_t>((1000 * total_bits) / (i + 1));
+      EXPECT_NEAR(rate_bps, *rate, 22000u);
+    }
+    now_ms += 1;
+  }
+  now_ms += kWindowMs;
+  // The window is 2 seconds. If nothing has been received for that time
+  // the estimate should be 0.
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+}
+
+TEST_F(RateStatisticsTest, IncreasingThenDecreasingBitrate) {
+  int64_t now_ms = 0;
+  stats_.Reset();
+  // Expecting 0 after init.
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  stats_.Update(1000, ++now_ms);
+  const uint32_t kExpectedBitrate = 8000000;
+  // 1000 bytes per millisecond until plateau is reached.
+  int prev_error = kExpectedBitrate;
+  rtc::Optional<uint32_t> bitrate;
+  while (++now_ms < 10000) {
+    stats_.Update(1000, now_ms);
+    bitrate = stats_.Rate(now_ms);
+    EXPECT_TRUE(static_cast<bool>(bitrate));
+    int error = kExpectedBitrate - *bitrate;
+    error = std::abs(error);
+    // Expect the estimation error to decrease as the window is extended.
+    EXPECT_LE(error, prev_error + 1);
+    prev_error = error;
+  }
+  // Window filled, expect to be close to 8000000.
+  EXPECT_EQ(kExpectedBitrate, *bitrate);
+
+  // 1000 bytes per millisecond until 10-second mark, 8000 kbps expected.
+  while (++now_ms < 10000) {
+    stats_.Update(1000, now_ms);
+    bitrate = stats_.Rate(now_ms);
+    EXPECT_EQ(kExpectedBitrate, *bitrate);
+  }
+
+  // Zero bytes per millisecond until 0 is reached.
+  while (++now_ms < 20000) {
+    stats_.Update(0, now_ms);
+    rtc::Optional<uint32_t> new_bitrate = stats_.Rate(now_ms);
+    if (static_cast<bool>(new_bitrate) && *new_bitrate != *bitrate) {
+      // New bitrate must be lower than previous one.
+      EXPECT_LT(*new_bitrate, *bitrate);
+    } else {
+      // 0 kbps expected.
+      EXPECT_EQ(0u, *new_bitrate);
+      break;
+    }
+    bitrate = new_bitrate;
+  }
+
+  // Zero bytes per millisecond until 20-second mark, 0 kbps expected.
+  while (++now_ms < 20000) {
+    stats_.Update(0, now_ms);
+    EXPECT_EQ(0u, *stats_.Rate(now_ms));
+  }
+}
+
+TEST_F(RateStatisticsTest, ResetAfterSilence) {
+  int64_t now_ms = 0;
+  stats_.Reset();
+  // Expecting 0 after init.
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  const uint32_t kExpectedBitrate = 8000000;
+  // 1000 bytes per millisecond until the window has been filled.
+  int prev_error = kExpectedBitrate;
+  rtc::Optional<uint32_t> bitrate;
+  while (++now_ms < 10000) {
+    stats_.Update(1000, now_ms);
+    bitrate = stats_.Rate(now_ms);
+    if (bitrate) {
+      int error = kExpectedBitrate - *bitrate;
+      error = std::abs(error);
+      // Expect the estimation error to decrease as the window is extended.
+      EXPECT_LE(error, prev_error + 1);
+      prev_error = error;
+    }
+  }
+  // Window filled, expect to be close to 8000000.
+  EXPECT_EQ(kExpectedBitrate, *bitrate);
+
+  now_ms += kWindowMs + 1;
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+  stats_.Update(1000, now_ms);
+  ++now_ms;
+  stats_.Update(1000, now_ms);
+  // We expect two samples of 1000 bytes, and that the bitrate is measured over
+  // 500 ms, i.e. 2 * 8 * 1000 / 0.500 = 32000.
+  EXPECT_EQ(32000u, *stats_.Rate(now_ms));
+
+  // Reset, add the same samples again.
+  stats_.Reset();
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+  stats_.Update(1000, now_ms);
+  ++now_ms;
+  stats_.Update(1000, now_ms);
+  // We expect two samples of 1000 bytes, and that the bitrate is measured over
+  // 2 ms (window size has been reset) i.e. 2 * 8 * 1000 / 0.002 = 8000000.
+  EXPECT_EQ(kExpectedBitrate, *stats_.Rate(now_ms));
+}
+
+TEST_F(RateStatisticsTest, HandlesChangingWindowSize) {
+  int64_t now_ms = 0;
+  stats_.Reset();
+
+  // Sanity test window size.
+  EXPECT_TRUE(stats_.SetWindowSize(kWindowMs, now_ms));
+  EXPECT_FALSE(stats_.SetWindowSize(kWindowMs + 1, now_ms));
+  EXPECT_FALSE(stats_.SetWindowSize(0, now_ms));
+  EXPECT_TRUE(stats_.SetWindowSize(1, now_ms));
+  EXPECT_TRUE(stats_.SetWindowSize(kWindowMs, now_ms));
+
+  // Fill the buffer at a rate of 1 byte / millisecond (8 kbps).
+  const int kBatchSize = 10;
+  for (int i = 0; i <= kWindowMs; i += kBatchSize)
+    stats_.Update(kBatchSize, now_ms += kBatchSize);
+  EXPECT_EQ(static_cast<uint32_t>(8000), *stats_.Rate(now_ms));
+
+  // Halve the window size, rate should stay the same.
+  EXPECT_TRUE(stats_.SetWindowSize(kWindowMs / 2, now_ms));
+  EXPECT_EQ(static_cast<uint32_t>(8000), *stats_.Rate(now_ms));
+
+  // Double the window size again, rate should stay the same. (As the window
+  // won't actually expand until new bit and bobs fall into it.
+  EXPECT_TRUE(stats_.SetWindowSize(kWindowMs, now_ms));
+  EXPECT_EQ(static_cast<uint32_t>(8000), *stats_.Rate(now_ms));
+
+  // Fill the now empty half with bits it twice the rate.
+  for (int i = 0; i < kWindowMs / 2; i += kBatchSize)
+    stats_.Update(kBatchSize * 2, now_ms += kBatchSize);
+
+  // Rate should have increase be 50%.
+  EXPECT_EQ(static_cast<uint32_t>((8000 * 3) / 2), *stats_.Rate(now_ms));
+}
+
+TEST_F(RateStatisticsTest, RespectsWindowSizeEdges) {
+  int64_t now_ms = 0;
+  stats_.Reset();
+  // Expecting 0 after init.
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  // One byte per ms, using one big sample.
+  stats_.Update(kWindowMs, now_ms);
+  now_ms += kWindowMs - 2;
+  // Shouldn't work! (Only one sample, not full window size.)
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  // Window size should be full, and the single data point should be accepted.
+  ++now_ms;
+  rtc::Optional<uint32_t> bitrate = stats_.Rate(now_ms);
+  EXPECT_TRUE(static_cast<bool>(bitrate));
+  EXPECT_EQ(1000 * 8u, *bitrate);
+
+  // Add another, now we have twice the bitrate.
+  stats_.Update(kWindowMs, now_ms);
+  bitrate = stats_.Rate(now_ms);
+  EXPECT_TRUE(static_cast<bool>(bitrate));
+  EXPECT_EQ(2 * 1000 * 8u, *bitrate);
+
+  // Now that first sample should drop out...
+  now_ms += 1;
+  bitrate = stats_.Rate(now_ms);
+  EXPECT_TRUE(static_cast<bool>(bitrate));
+  EXPECT_EQ(1000 * 8u, *bitrate);
+}
+
+TEST_F(RateStatisticsTest, HandlesZeroCounts) {
+  int64_t now_ms = 0;
+  stats_.Reset();
+  // Expecting 0 after init.
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  stats_.Update(kWindowMs, now_ms);
+  now_ms += kWindowMs - 1;
+  stats_.Update(0, now_ms);
+  rtc::Optional<uint32_t> bitrate = stats_.Rate(now_ms);
+  EXPECT_TRUE(static_cast<bool>(bitrate));
+  EXPECT_EQ(1000 * 8u, *bitrate);
+
+  // Move window along so first data point falls out.
+  ++now_ms;
+  bitrate = stats_.Rate(now_ms);
+  EXPECT_TRUE(static_cast<bool>(bitrate));
+  EXPECT_EQ(0u, *bitrate);
+
+  // Move window so last data point falls out.
+  now_ms += kWindowMs;
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+}
+
+TEST_F(RateStatisticsTest, HandlesQuietPeriods) {
+  int64_t now_ms = 0;
+  stats_.Reset();
+  // Expecting 0 after init.
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  stats_.Update(0, now_ms);
+  now_ms += kWindowMs - 1;
+  rtc::Optional<uint32_t> bitrate = stats_.Rate(now_ms);
+  EXPECT_TRUE(static_cast<bool>(bitrate));
+  EXPECT_EQ(0u, *bitrate);
+
+  // Move window along so first data point falls out.
+  ++now_ms;
+  EXPECT_FALSE(static_cast<bool>(stats_.Rate(now_ms)));
+
+  // Move window a long way out.
+  now_ms += 2 * kWindowMs;
+  stats_.Update(0, now_ms);
+  bitrate = stats_.Rate(now_ms);
+  EXPECT_TRUE(static_cast<bool>(bitrate));
+  EXPECT_EQ(0u, *bitrate);
+}
+}  // namespace
diff --git a/base/ratelimiter.cc b/base/ratelimiter.cc
new file mode 100644
index 0000000..c4a251d
--- /dev/null
+++ b/base/ratelimiter.cc
@@ -0,0 +1,29 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/ratelimiter.h"
+
+namespace rtc {
+
+bool RateLimiter::CanUse(size_t desired, double time) {
+  return ((time > period_end_ && desired <= max_per_period_) ||
+          (used_in_period_ + desired) <= max_per_period_);
+}
+
+void RateLimiter::Use(size_t used, double time) {
+  if (time > period_end_) {
+    period_start_ = time;
+    period_end_ = time + period_length_;
+    used_in_period_ = 0;
+  }
+  used_in_period_ += used;
+}
+
+}  // namespace rtc
diff --git a/base/ratelimiter.h b/base/ratelimiter.h
index 0e372db..1423e99 100644
--- a/base/ratelimiter.h
+++ b/base/ratelimiter.h
@@ -11,9 +11,52 @@
 #ifndef WEBRTC_BASE_RATELIMITER_H_
 #define WEBRTC_BASE_RATELIMITER_H_
 
+#include <stddef.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/ratelimiter.h"
+namespace rtc {
+
+// Limits the rate of use to a certain maximum quantity per period of
+// time.  Use, for example, for simple bandwidth throttling.
+//
+// It's implemented like a diet plan: You have so many calories per
+// day.  If you hit the limit, you can't eat any more until the next
+// day.
+class RateLimiter {
+ public:
+  // For example, 100kb per second.
+  RateLimiter(size_t max, double period)
+      : max_per_period_(max),
+        period_length_(period),
+        used_in_period_(0),
+        period_start_(0.0),
+        period_end_(period) {
+  }
+  virtual ~RateLimiter() {}
+
+  // Returns true if if the desired quantity is available in the
+  // current period (< (max - used)).  Once the given time passes the
+  // end of the period, used is set to zero and more use is available.
+  bool CanUse(size_t desired, double time);
+  // Increment the quantity used this period.  If past the end of a
+  // period, a new period is started.
+  void Use(size_t used, double time);
+
+  size_t used_in_period() const {
+    return used_in_period_;
+  }
+
+  size_t max_per_period() const {
+    return max_per_period_;
+  }
+
+ private:
+  size_t max_per_period_;
+  double period_length_;
+  size_t used_in_period_;
+  double period_start_;
+  double period_end_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_RATELIMITER_H_
diff --git a/base/ratelimiter_unittest.cc b/base/ratelimiter_unittest.cc
new file mode 100644
index 0000000..b54a751
--- /dev/null
+++ b/base/ratelimiter_unittest.cc
@@ -0,0 +1,59 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/ratelimiter.h"
+
+namespace rtc {
+
+TEST(RateLimiterTest, TestCanUse) {
+  // Diet: Can eat 2,000 calories per day.
+  RateLimiter limiter = RateLimiter(2000, 1.0);
+
+  double monday = 1.0;
+  double tuesday = 2.0;
+  double thursday = 4.0;
+
+  EXPECT_TRUE(limiter.CanUse(0, monday));
+  EXPECT_TRUE(limiter.CanUse(1000, monday));
+  EXPECT_TRUE(limiter.CanUse(1999, monday));
+  EXPECT_TRUE(limiter.CanUse(2000, monday));
+  EXPECT_FALSE(limiter.CanUse(2001, monday));
+
+  limiter.Use(1000, monday);
+
+  EXPECT_TRUE(limiter.CanUse(0, monday));
+  EXPECT_TRUE(limiter.CanUse(999, monday));
+  EXPECT_TRUE(limiter.CanUse(1000, monday));
+  EXPECT_FALSE(limiter.CanUse(1001, monday));
+
+  limiter.Use(1000, monday);
+
+  EXPECT_TRUE(limiter.CanUse(0, monday));
+  EXPECT_FALSE(limiter.CanUse(1, monday));
+
+  EXPECT_TRUE(limiter.CanUse(0, tuesday));
+  EXPECT_TRUE(limiter.CanUse(1, tuesday));
+  EXPECT_TRUE(limiter.CanUse(1999, tuesday));
+  EXPECT_TRUE(limiter.CanUse(2000, tuesday));
+  EXPECT_FALSE(limiter.CanUse(2001, tuesday));
+
+  limiter.Use(1000, tuesday);
+
+  EXPECT_TRUE(limiter.CanUse(1000, tuesday));
+  EXPECT_FALSE(limiter.CanUse(1001, tuesday));
+
+  limiter.Use(1000, thursday);
+
+  EXPECT_TRUE(limiter.CanUse(1000, tuesday));
+  EXPECT_FALSE(limiter.CanUse(1001, tuesday));
+}
+
+}  // namespace rtc
diff --git a/base/ratetracker.cc b/base/ratetracker.cc
new file mode 100644
index 0000000..a59ec2f
--- /dev/null
+++ b/base/ratetracker.cc
@@ -0,0 +1,154 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/ratetracker.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+
+static const int64_t kTimeUnset = -1;
+
+RateTracker::RateTracker(int64_t bucket_milliseconds, size_t bucket_count)
+    : bucket_milliseconds_(bucket_milliseconds),
+      bucket_count_(bucket_count),
+      sample_buckets_(new size_t[bucket_count + 1]),
+      total_sample_count_(0u),
+      bucket_start_time_milliseconds_(kTimeUnset) {
+  RTC_CHECK(bucket_milliseconds > 0);
+  RTC_CHECK(bucket_count > 0);
+}
+
+RateTracker::~RateTracker() {
+  delete[] sample_buckets_;
+}
+
+double RateTracker::ComputeRateForInterval(
+    int64_t interval_milliseconds) const {
+  if (bucket_start_time_milliseconds_ == kTimeUnset) {
+    return 0.0;
+  }
+  int64_t current_time = Time();
+  // Calculate which buckets to sum up given the current time.  If the time
+  // has passed to a new bucket then we have to skip some of the oldest buckets.
+  int64_t available_interval_milliseconds =
+      std::min(interval_milliseconds,
+               bucket_milliseconds_ * static_cast<int64_t>(bucket_count_));
+  // number of old buckets (i.e. after the current bucket in the ring buffer)
+  // that are expired given our current time interval.
+  size_t buckets_to_skip;
+  // Number of milliseconds of the first bucket that are not a portion of the
+  // current interval.
+  int64_t milliseconds_to_skip;
+  if (current_time >
+      initialization_time_milliseconds_ + available_interval_milliseconds) {
+    int64_t time_to_skip =
+        current_time - bucket_start_time_milliseconds_ +
+        static_cast<int64_t>(bucket_count_) * bucket_milliseconds_ -
+        available_interval_milliseconds;
+    buckets_to_skip = time_to_skip / bucket_milliseconds_;
+    milliseconds_to_skip = time_to_skip % bucket_milliseconds_;
+  } else {
+    buckets_to_skip = bucket_count_ - current_bucket_;
+    milliseconds_to_skip = 0;
+    available_interval_milliseconds =
+        TimeDiff(current_time, initialization_time_milliseconds_);
+    // Let one bucket interval pass after initialization before reporting.
+    if (available_interval_milliseconds < bucket_milliseconds_) {
+      return 0.0;
+    }
+  }
+  // If we're skipping all buckets that means that there have been no samples
+  // within the sampling interval so report 0.
+  if (buckets_to_skip > bucket_count_ || available_interval_milliseconds == 0) {
+    return 0.0;
+  }
+  size_t start_bucket = NextBucketIndex(current_bucket_ + buckets_to_skip);
+  // Only count a portion of the first bucket according to how much of the
+  // first bucket is within the current interval.
+  size_t total_samples = ((sample_buckets_[start_bucket] *
+      (bucket_milliseconds_ - milliseconds_to_skip)) +
+      (bucket_milliseconds_ >> 1)) /
+      bucket_milliseconds_;
+  // All other buckets in the interval are counted in their entirety.
+  for (size_t i = NextBucketIndex(start_bucket);
+      i != NextBucketIndex(current_bucket_);
+      i = NextBucketIndex(i)) {
+    total_samples += sample_buckets_[i];
+  }
+  // Convert to samples per second.
+  return static_cast<double>(total_samples * 1000) /
+         static_cast<double>(available_interval_milliseconds);
+}
+
+double RateTracker::ComputeTotalRate() const {
+  if (bucket_start_time_milliseconds_ == kTimeUnset) {
+    return 0.0;
+  }
+  int64_t current_time = Time();
+  if (current_time <= initialization_time_milliseconds_) {
+    return 0.0;
+  }
+  return static_cast<double>(total_sample_count_ * 1000) /
+         static_cast<double>(
+             TimeDiff(current_time, initialization_time_milliseconds_));
+}
+
+size_t RateTracker::TotalSampleCount() const {
+  return total_sample_count_;
+}
+
+void RateTracker::AddSamples(size_t sample_count) {
+  EnsureInitialized();
+  int64_t current_time = Time();
+  // Advance the current bucket as needed for the current time, and reset
+  // bucket counts as we advance.
+  for (size_t i = 0;
+       i <= bucket_count_ &&
+       current_time >= bucket_start_time_milliseconds_ + bucket_milliseconds_;
+       ++i) {
+    bucket_start_time_milliseconds_ += bucket_milliseconds_;
+    current_bucket_ = NextBucketIndex(current_bucket_);
+    sample_buckets_[current_bucket_] = 0;
+  }
+  // Ensure that bucket_start_time_milliseconds_ is updated appropriately if
+  // the entire buffer of samples has been expired.
+  bucket_start_time_milliseconds_ += bucket_milliseconds_ *
+      ((current_time - bucket_start_time_milliseconds_) / bucket_milliseconds_);
+  // Add all samples in the bucket that includes the current time.
+  sample_buckets_[current_bucket_] += sample_count;
+  total_sample_count_ += sample_count;
+}
+
+int64_t RateTracker::Time() const {
+  return rtc::TimeMillis();
+}
+
+void RateTracker::EnsureInitialized() {
+  if (bucket_start_time_milliseconds_ == kTimeUnset) {
+    initialization_time_milliseconds_ = Time();
+    bucket_start_time_milliseconds_ = initialization_time_milliseconds_;
+    current_bucket_ = 0;
+    // We only need to initialize the first bucket because we reset buckets when
+    // current_bucket_ increments.
+    sample_buckets_[current_bucket_] = 0;
+  }
+}
+
+size_t RateTracker::NextBucketIndex(size_t bucket_index) const {
+  return (bucket_index + 1u) % (bucket_count_ + 1u);
+}
+
+}  // namespace rtc
diff --git a/base/ratetracker.h b/base/ratetracker.h
index d1fd75d..6ae9bec 100644
--- a/base/ratetracker.h
+++ b/base/ratetracker.h
@@ -11,9 +11,59 @@
 #ifndef WEBRTC_BASE_RATETRACKER_H_
 #define WEBRTC_BASE_RATETRACKER_H_
 
+#include <stdlib.h>
+#include "webrtc/base/basictypes.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/ratetracker.h"
+namespace rtc {
+
+// Computes units per second over a given interval by tracking the units over
+// each bucket of a given size and calculating the instantaneous rate assuming
+// that over each bucket the rate was constant.
+class RateTracker {
+ public:
+  RateTracker(int64_t bucket_milliseconds, size_t bucket_count);
+  virtual ~RateTracker();
+
+  // Computes the average rate over the most recent interval_milliseconds,
+  // or if the first sample was added within this period, computes the rate
+  // since the first sample was added.
+  double ComputeRateForInterval(int64_t interval_milliseconds) const;
+
+  // Computes the average rate over the rate tracker's recording interval
+  // of bucket_milliseconds * bucket_count.
+  double ComputeRate() const {
+    return ComputeRateForInterval(bucket_milliseconds_ *
+                                  static_cast<int64_t>(bucket_count_));
+  }
+
+  // Computes the average rate since the first sample was added to the
+  // rate tracker.
+  double ComputeTotalRate() const;
+
+  // The total number of samples added.
+  size_t TotalSampleCount() const;
+
+  // Reads the current time in order to determine the appropriate bucket for
+  // these samples, and increments the count for that bucket by sample_count.
+  void AddSamples(size_t sample_count);
+
+ protected:
+  // overrideable for tests
+  virtual int64_t Time() const;
+
+ private:
+  void EnsureInitialized();
+  size_t NextBucketIndex(size_t bucket_index) const;
+
+  const int64_t bucket_milliseconds_;
+  const size_t bucket_count_;
+  size_t* sample_buckets_;
+  size_t total_sample_count_;
+  size_t current_bucket_;
+  int64_t bucket_start_time_milliseconds_;
+  int64_t initialization_time_milliseconds_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_RATETRACKER_H_
diff --git a/base/ratetracker_unittest.cc b/base/ratetracker_unittest.cc
new file mode 100644
index 0000000..e2b51c1
--- /dev/null
+++ b/base/ratetracker_unittest.cc
@@ -0,0 +1,168 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/ratetracker.h"
+
+namespace rtc {
+namespace {
+  const uint32_t kBucketIntervalMs = 100;
+}  // namespace
+
+class RateTrackerForTest : public RateTracker {
+ public:
+  RateTrackerForTest() : RateTracker(kBucketIntervalMs, 10u), time_(0) {}
+  int64_t Time() const override { return time_; }
+  void AdvanceTime(int delta) { time_ += delta; }
+
+ private:
+  int64_t time_;
+};
+
+TEST(RateTrackerTest, Test30FPS) {
+  RateTrackerForTest tracker;
+
+  for (int i = 0; i < 300; ++i) {
+    tracker.AddSamples(1);
+    tracker.AdvanceTime(33);
+    if (i % 3 == 0) {
+      tracker.AdvanceTime(1);
+    }
+  }
+  EXPECT_DOUBLE_EQ(30.0, tracker.ComputeRateForInterval(50000));
+}
+
+TEST(RateTrackerTest, Test60FPS) {
+  RateTrackerForTest tracker;
+
+  for (int i = 0; i < 300; ++i) {
+    tracker.AddSamples(1);
+    tracker.AdvanceTime(16);
+    if (i % 3 != 0) {
+      tracker.AdvanceTime(1);
+    }
+  }
+  EXPECT_DOUBLE_EQ(60.0, tracker.ComputeRateForInterval(1000));
+}
+
+TEST(RateTrackerTest, TestRateTrackerBasics) {
+  RateTrackerForTest tracker;
+  EXPECT_DOUBLE_EQ(0.0, tracker.ComputeRateForInterval(1000));
+
+  // Add a sample.
+  tracker.AddSamples(1234);
+  // Advance the clock by less than one bucket interval (no rate returned).
+  tracker.AdvanceTime(kBucketIntervalMs - 1);
+  EXPECT_DOUBLE_EQ(0.0, tracker.ComputeRate());
+  // Advance the clock by 100 ms (one bucket interval).
+  tracker.AdvanceTime(1);
+  EXPECT_DOUBLE_EQ(12340.0, tracker.ComputeRateForInterval(1000));
+  EXPECT_DOUBLE_EQ(12340.0, tracker.ComputeRate());
+  EXPECT_EQ(1234U, tracker.TotalSampleCount());
+  EXPECT_DOUBLE_EQ(12340.0, tracker.ComputeTotalRate());
+
+  // Repeat.
+  tracker.AddSamples(1234);
+  tracker.AdvanceTime(100);
+  EXPECT_DOUBLE_EQ(12340.0, tracker.ComputeRateForInterval(1000));
+  EXPECT_DOUBLE_EQ(12340.0, tracker.ComputeRate());
+  EXPECT_EQ(1234U * 2, tracker.TotalSampleCount());
+  EXPECT_DOUBLE_EQ(12340.0, tracker.ComputeTotalRate());
+
+  // Advance the clock by 800 ms, so we've elapsed a full second.
+  // units_second should now be filled in properly.
+  tracker.AdvanceTime(800);
+  EXPECT_DOUBLE_EQ(1234.0 * 2.0, tracker.ComputeRateForInterval(1000));
+  EXPECT_DOUBLE_EQ(1234.0 * 2.0, tracker.ComputeRate());
+  EXPECT_EQ(1234U * 2, tracker.TotalSampleCount());
+  EXPECT_DOUBLE_EQ(1234.0 * 2.0, tracker.ComputeTotalRate());
+
+  // Poll the tracker again immediately. The reported rate should stay the same.
+  EXPECT_DOUBLE_EQ(1234.0 * 2.0, tracker.ComputeRateForInterval(1000));
+  EXPECT_DOUBLE_EQ(1234.0 * 2.0, tracker.ComputeRate());
+  EXPECT_EQ(1234U * 2, tracker.TotalSampleCount());
+  EXPECT_DOUBLE_EQ(1234.0 * 2.0, tracker.ComputeTotalRate());
+
+  // Do nothing and advance by a second. We should drop down to zero.
+  tracker.AdvanceTime(1000);
+  EXPECT_DOUBLE_EQ(0.0, tracker.ComputeRateForInterval(1000));
+  EXPECT_DOUBLE_EQ(0.0, tracker.ComputeRate());
+  EXPECT_EQ(1234U * 2, tracker.TotalSampleCount());
+  EXPECT_DOUBLE_EQ(1234.0, tracker.ComputeTotalRate());
+
+  // Send a bunch of data at a constant rate for 5.5 "seconds".
+  // We should report the rate properly.
+  for (int i = 0; i < 5500; i += 100) {
+    tracker.AddSamples(9876U);
+    tracker.AdvanceTime(100);
+  }
+  EXPECT_DOUBLE_EQ(9876.0 * 10.0, tracker.ComputeRateForInterval(1000));
+  EXPECT_DOUBLE_EQ(9876.0 * 10.0, tracker.ComputeRate());
+  EXPECT_EQ(1234U * 2 + 9876U * 55, tracker.TotalSampleCount());
+  EXPECT_DOUBLE_EQ((1234.0 * 2.0 + 9876.0 * 55.0) / 7.5,
+      tracker.ComputeTotalRate());
+
+  // Advance the clock by 500 ms. Since we sent nothing over this half-second,
+  // the reported rate should be reduced by half.
+  tracker.AdvanceTime(500);
+  EXPECT_DOUBLE_EQ(9876.0 * 5.0, tracker.ComputeRateForInterval(1000));
+  EXPECT_DOUBLE_EQ(9876.0 * 5.0, tracker.ComputeRate());
+  EXPECT_EQ(1234U * 2 + 9876U * 55, tracker.TotalSampleCount());
+  EXPECT_DOUBLE_EQ((1234.0 * 2.0 + 9876.0 * 55.0) / 8.0,
+      tracker.ComputeTotalRate());
+
+  // Rate over the last half second should be zero.
+  EXPECT_DOUBLE_EQ(0.0, tracker.ComputeRateForInterval(500));
+}
+
+TEST(RateTrackerTest, TestLongPeriodBetweenSamples) {
+  RateTrackerForTest tracker;
+  tracker.AddSamples(1);
+  tracker.AdvanceTime(1000);
+  EXPECT_DOUBLE_EQ(1.0, tracker.ComputeRate());
+
+  tracker.AdvanceTime(2000);
+  EXPECT_DOUBLE_EQ(0.0, tracker.ComputeRate());
+
+  tracker.AdvanceTime(2000);
+  tracker.AddSamples(1);
+  EXPECT_DOUBLE_EQ(1.0, tracker.ComputeRate());
+}
+
+TEST(RateTrackerTest, TestRolloff) {
+  RateTrackerForTest tracker;
+  for (int i = 0; i < 10; ++i) {
+    tracker.AddSamples(1U);
+    tracker.AdvanceTime(100);
+  }
+  EXPECT_DOUBLE_EQ(10.0, tracker.ComputeRate());
+
+  for (int i = 0; i < 10; ++i) {
+    tracker.AddSamples(1U);
+    tracker.AdvanceTime(50);
+  }
+  EXPECT_DOUBLE_EQ(15.0, tracker.ComputeRate());
+  EXPECT_DOUBLE_EQ(20.0, tracker.ComputeRateForInterval(500));
+
+  for (int i = 0; i < 10; ++i) {
+    tracker.AddSamples(1U);
+    tracker.AdvanceTime(50);
+  }
+  EXPECT_DOUBLE_EQ(20.0, tracker.ComputeRate());
+}
+
+TEST(RateTrackerTest, TestGetUnitSecondsAfterInitialValue) {
+  RateTrackerForTest tracker;
+  tracker.AddSamples(1234);
+  tracker.AdvanceTime(1000);
+  EXPECT_DOUBLE_EQ(1234.0, tracker.ComputeRateForInterval(1000));
+}
+
+}  // namespace rtc
diff --git a/base/refcount.h b/base/refcount.h
index 4a7cea3..565ae49 100644
--- a/base/refcount.h
+++ b/base/refcount.h
@@ -10,9 +10,20 @@
 #ifndef WEBRTC_BASE_REFCOUNT_H_
 #define WEBRTC_BASE_REFCOUNT_H_
 
+#include "webrtc/base/refcountedobject.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/refcount.h"
+namespace rtc {
+
+// Reference count interface.
+class RefCountInterface {
+ public:
+  virtual int AddRef() const = 0;
+  virtual int Release() const = 0;
+
+ protected:
+  virtual ~RefCountInterface() {}
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_REFCOUNT_H_
diff --git a/base/refcountedobject.h b/base/refcountedobject.h
index 78304fa..285ed36 100644
--- a/base/refcountedobject.h
+++ b/base/refcountedobject.h
@@ -10,9 +10,52 @@
 #ifndef WEBRTC_BASE_REFCOUNTEDOBJECT_H_
 #define WEBRTC_BASE_REFCOUNTEDOBJECT_H_
 
+#include <utility>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/refcountedobject.h"
+#include "webrtc/base/atomicops.h"
+
+namespace rtc {
+
+template <class T>
+class RefCountedObject : public T {
+ public:
+  RefCountedObject() {}
+
+  template <class P0>
+  explicit RefCountedObject(P0&& p0) : T(std::forward<P0>(p0)) {}
+
+  template <class P0, class P1, class... Args>
+  RefCountedObject(P0&& p0, P1&& p1, Args&&... args)
+      : T(std::forward<P0>(p0),
+          std::forward<P1>(p1),
+          std::forward<Args>(args)...) {}
+
+  virtual int AddRef() const { return AtomicOps::Increment(&ref_count_); }
+
+  virtual int Release() const {
+    int count = AtomicOps::Decrement(&ref_count_);
+    if (!count) {
+      delete this;
+    }
+    return count;
+  }
+
+  // Return whether the reference count is one. If the reference count is used
+  // in the conventional way, a reference count of 1 implies that the current
+  // thread owns the reference and no other thread shares it. This call
+  // performs the test for a reference count of one, and performs the memory
+  // barrier needed for the owning thread to act on the object, knowing that it
+  // has exclusive access to the object.
+  virtual bool HasOneRef() const {
+    return AtomicOps::AcquireLoad(&ref_count_) == 1;
+  }
+
+ protected:
+  virtual ~RefCountedObject() {}
+
+  mutable volatile int ref_count_ = 0;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_REFCOUNTEDOBJECT_H_
diff --git a/base/refcountedobject_unittest.cc b/base/refcountedobject_unittest.cc
new file mode 100644
index 0000000..f6096be
--- /dev/null
+++ b/base/refcountedobject_unittest.cc
@@ -0,0 +1,91 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/refcount.h"
+
+namespace rtc {
+
+namespace {
+
+class A {
+ public:
+  A() {}
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(A);
+};
+
+class RefClass : public RefCountInterface {
+ public:
+  RefClass() {}
+
+ protected:
+  ~RefClass() override {}
+};
+
+class RefClassWithRvalue : public RefCountInterface {
+ public:
+  explicit RefClassWithRvalue(std::unique_ptr<A> a) : a_(std::move(a)) {}
+
+ protected:
+  ~RefClassWithRvalue() override {}
+
+ public:
+  std::unique_ptr<A> a_;
+};
+
+class RefClassWithMixedValues : public RefCountInterface {
+ public:
+  RefClassWithMixedValues(std::unique_ptr<A> a, int b, const std::string& c)
+      : a_(std::move(a)), b_(b), c_(c) {}
+
+ protected:
+  ~RefClassWithMixedValues() override {}
+
+ public:
+  std::unique_ptr<A> a_;
+  int b_;
+  std::string c_;
+};
+
+}  // namespace
+
+TEST(RefCountedObject, Basic) {
+  scoped_refptr<RefCountedObject<RefClass>> aref(
+      new RefCountedObject<RefClass>());
+  EXPECT_TRUE(aref->HasOneRef());
+  EXPECT_EQ(2, aref->AddRef());
+  EXPECT_EQ(1, aref->Release());
+}
+
+TEST(RefCountedObject, SupportRValuesInCtor) {
+  std::unique_ptr<A> a(new A());
+  scoped_refptr<RefClassWithRvalue> ref(
+      new RefCountedObject<RefClassWithRvalue>(std::move(a)));
+  EXPECT_TRUE(ref->a_.get() != nullptr);
+  EXPECT_TRUE(a.get() == nullptr);
+}
+
+TEST(RefCountedObject, SupportMixedTypesInCtor) {
+  std::unique_ptr<A> a(new A());
+  int b = 9;
+  std::string c = "hello";
+  scoped_refptr<RefClassWithMixedValues> ref(
+      new RefCountedObject<RefClassWithMixedValues>(std::move(a), b, c));
+  EXPECT_TRUE(ref->a_.get() != nullptr);
+  EXPECT_TRUE(a.get() == nullptr);
+  EXPECT_EQ(b, ref->b_);
+  EXPECT_EQ(c, ref->c_);
+}
+
+}  // namespace rtc
diff --git a/base/rollingaccumulator.h b/base/rollingaccumulator.h
index a7de4f1..6627375 100644
--- a/base/rollingaccumulator.h
+++ b/base/rollingaccumulator.h
@@ -11,9 +11,164 @@
 #ifndef WEBRTC_BASE_ROLLINGACCUMULATOR_H_
 #define WEBRTC_BASE_ROLLINGACCUMULATOR_H_
 
+#include <algorithm>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/rollingaccumulator.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+
+namespace rtc {
+
+// RollingAccumulator stores and reports statistics
+// over N most recent samples.
+//
+// T is assumed to be an int, long, double or float.
+template<typename T>
+class RollingAccumulator {
+ public:
+  explicit RollingAccumulator(size_t max_count)
+    : samples_(max_count) {
+    Reset();
+  }
+  ~RollingAccumulator() {
+  }
+
+  size_t max_count() const {
+    return samples_.size();
+  }
+
+  size_t count() const {
+    return count_;
+  }
+
+  void Reset() {
+    count_ = 0U;
+    next_index_ = 0U;
+    sum_ = 0.0;
+    sum_2_ = 0.0;
+    max_ = T();
+    max_stale_ = false;
+    min_ = T();
+    min_stale_ = false;
+  }
+
+  void AddSample(T sample) {
+    if (count_ == max_count()) {
+      // Remove oldest sample.
+      T sample_to_remove = samples_[next_index_];
+      sum_ -= sample_to_remove;
+      sum_2_ -= static_cast<double>(sample_to_remove) * sample_to_remove;
+      if (sample_to_remove >= max_) {
+        max_stale_ = true;
+      }
+      if (sample_to_remove <= min_) {
+        min_stale_ = true;
+      }
+    } else {
+      // Increase count of samples.
+      ++count_;
+    }
+    // Add new sample.
+    samples_[next_index_] = sample;
+    sum_ += sample;
+    sum_2_ += static_cast<double>(sample) * sample;
+    if (count_ == 1 || sample >= max_) {
+      max_ = sample;
+      max_stale_ = false;
+    }
+    if (count_ == 1 || sample <= min_) {
+      min_ = sample;
+      min_stale_ = false;
+    }
+    // Update next_index_.
+    next_index_ = (next_index_ + 1) % max_count();
+  }
+
+  T ComputeSum() const {
+    return static_cast<T>(sum_);
+  }
+
+  double ComputeMean() const {
+    if (count_ == 0) {
+      return 0.0;
+    }
+    return sum_ / count_;
+  }
+
+  T ComputeMax() const {
+    if (max_stale_) {
+      RTC_DCHECK(count_ > 0) <<
+                 "It shouldn't be possible for max_stale_ && count_ == 0";
+      max_ = samples_[next_index_];
+      for (size_t i = 1u; i < count_; i++) {
+        max_ = std::max(max_, samples_[(next_index_ + i) % max_count()]);
+      }
+      max_stale_ = false;
+    }
+    return max_;
+  }
+
+  T ComputeMin() const {
+    if (min_stale_) {
+      RTC_DCHECK(count_ > 0) <<
+                 "It shouldn't be possible for min_stale_ && count_ == 0";
+      min_ = samples_[next_index_];
+      for (size_t i = 1u; i < count_; i++) {
+        min_ = std::min(min_, samples_[(next_index_ + i) % max_count()]);
+      }
+      min_stale_ = false;
+    }
+    return min_;
+  }
+
+  // O(n) time complexity.
+  // Weights nth sample with weight (learning_rate)^n. Learning_rate should be
+  // between (0.0, 1.0], otherwise the non-weighted mean is returned.
+  double ComputeWeightedMean(double learning_rate) const {
+    if (count_ < 1 || learning_rate <= 0.0 || learning_rate >= 1.0) {
+      return ComputeMean();
+    }
+    double weighted_mean = 0.0;
+    double current_weight = 1.0;
+    double weight_sum = 0.0;
+    const size_t max_size = max_count();
+    for (size_t i = 0; i < count_; ++i) {
+      current_weight *= learning_rate;
+      weight_sum += current_weight;
+      // Add max_size to prevent underflow.
+      size_t index = (next_index_ + max_size - i - 1) % max_size;
+      weighted_mean += current_weight * samples_[index];
+    }
+    return weighted_mean / weight_sum;
+  }
+
+  // Compute estimated variance.  Estimation is more accurate
+  // as the number of samples grows.
+  double ComputeVariance() const {
+    if (count_ == 0) {
+      return 0.0;
+    }
+    // Var = E[x^2] - (E[x])^2
+    double count_inv = 1.0 / count_;
+    double mean_2 = sum_2_ * count_inv;
+    double mean = sum_ * count_inv;
+    return mean_2 - (mean * mean);
+  }
+
+ private:
+  size_t count_;
+  size_t next_index_;
+  double sum_;    // Sum(x) - double to avoid overflow
+  double sum_2_;  // Sum(x*x) - double to avoid overflow
+  mutable T max_;
+  mutable bool max_stale_;
+  mutable T min_;
+  mutable bool min_stale_;
+  std::vector<T> samples_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(RollingAccumulator);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_ROLLINGACCUMULATOR_H_
diff --git a/base/rollingaccumulator_unittest.cc b/base/rollingaccumulator_unittest.cc
new file mode 100644
index 0000000..7e3d8cd
--- /dev/null
+++ b/base/rollingaccumulator_unittest.cc
@@ -0,0 +1,118 @@
+/*
+ *  Copyright 2011 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/rollingaccumulator.h"
+
+namespace rtc {
+
+namespace {
+
+const double kLearningRate = 0.5;
+
+}  // namespace
+
+TEST(RollingAccumulatorTest, ZeroSamples) {
+  RollingAccumulator<int> accum(10);
+
+  EXPECT_EQ(0U, accum.count());
+  EXPECT_DOUBLE_EQ(0.0, accum.ComputeMean());
+  EXPECT_DOUBLE_EQ(0.0, accum.ComputeVariance());
+  EXPECT_EQ(0, accum.ComputeMin());
+  EXPECT_EQ(0, accum.ComputeMax());
+}
+
+TEST(RollingAccumulatorTest, SomeSamples) {
+  RollingAccumulator<int> accum(10);
+  for (int i = 0; i < 4; ++i) {
+    accum.AddSample(i);
+  }
+
+  EXPECT_EQ(4U, accum.count());
+  EXPECT_EQ(6, accum.ComputeSum());
+  EXPECT_DOUBLE_EQ(1.5, accum.ComputeMean());
+  EXPECT_NEAR(2.26666, accum.ComputeWeightedMean(kLearningRate), 0.01);
+  EXPECT_DOUBLE_EQ(1.25, accum.ComputeVariance());
+  EXPECT_EQ(0, accum.ComputeMin());
+  EXPECT_EQ(3, accum.ComputeMax());
+}
+
+TEST(RollingAccumulatorTest, RollingSamples) {
+  RollingAccumulator<int> accum(10);
+  for (int i = 0; i < 12; ++i) {
+    accum.AddSample(i);
+  }
+
+  EXPECT_EQ(10U, accum.count());
+  EXPECT_EQ(65, accum.ComputeSum());
+  EXPECT_DOUBLE_EQ(6.5, accum.ComputeMean());
+  EXPECT_NEAR(10.0, accum.ComputeWeightedMean(kLearningRate), 0.01);
+  EXPECT_NEAR(9.0, accum.ComputeVariance(), 1.0);
+  EXPECT_EQ(2, accum.ComputeMin());
+  EXPECT_EQ(11, accum.ComputeMax());
+}
+
+TEST(RollingAccumulatorTest, ResetSamples) {
+  RollingAccumulator<int> accum(10);
+
+  for (int i = 0; i < 10; ++i) {
+    accum.AddSample(100);
+  }
+  EXPECT_EQ(10U, accum.count());
+  EXPECT_DOUBLE_EQ(100.0, accum.ComputeMean());
+  EXPECT_EQ(100, accum.ComputeMin());
+  EXPECT_EQ(100, accum.ComputeMax());
+
+  accum.Reset();
+  EXPECT_EQ(0U, accum.count());
+
+  for (int i = 0; i < 5; ++i) {
+    accum.AddSample(i);
+  }
+
+  EXPECT_EQ(5U, accum.count());
+  EXPECT_EQ(10, accum.ComputeSum());
+  EXPECT_DOUBLE_EQ(2.0, accum.ComputeMean());
+  EXPECT_EQ(0, accum.ComputeMin());
+  EXPECT_EQ(4, accum.ComputeMax());
+}
+
+TEST(RollingAccumulatorTest, RollingSamplesDouble) {
+  RollingAccumulator<double> accum(10);
+  for (int i = 0; i < 23; ++i) {
+    accum.AddSample(5 * i);
+  }
+
+  EXPECT_EQ(10u, accum.count());
+  EXPECT_DOUBLE_EQ(875.0, accum.ComputeSum());
+  EXPECT_DOUBLE_EQ(87.5, accum.ComputeMean());
+  EXPECT_NEAR(105.049, accum.ComputeWeightedMean(kLearningRate), 0.1);
+  EXPECT_NEAR(229.166667, accum.ComputeVariance(), 25);
+  EXPECT_DOUBLE_EQ(65.0, accum.ComputeMin());
+  EXPECT_DOUBLE_EQ(110.0, accum.ComputeMax());
+}
+
+TEST(RollingAccumulatorTest, ComputeWeightedMeanCornerCases) {
+  RollingAccumulator<int> accum(10);
+  EXPECT_DOUBLE_EQ(0.0, accum.ComputeWeightedMean(kLearningRate));
+  EXPECT_DOUBLE_EQ(0.0, accum.ComputeWeightedMean(0.0));
+  EXPECT_DOUBLE_EQ(0.0, accum.ComputeWeightedMean(1.1));
+
+  for (int i = 0; i < 8; ++i) {
+    accum.AddSample(i);
+  }
+
+  EXPECT_DOUBLE_EQ(3.5, accum.ComputeMean());
+  EXPECT_DOUBLE_EQ(3.5, accum.ComputeWeightedMean(0));
+  EXPECT_DOUBLE_EQ(3.5, accum.ComputeWeightedMean(1.1));
+  EXPECT_NEAR(6.0, accum.ComputeWeightedMean(kLearningRate), 0.1);
+}
+
+}  // namespace rtc
diff --git a/base/rtccertificate.cc b/base/rtccertificate.cc
new file mode 100644
index 0000000..3b3b8c9
--- /dev/null
+++ b/base/rtccertificate.cc
@@ -0,0 +1,70 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/rtccertificate.h"
+
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+scoped_refptr<RTCCertificate> RTCCertificate::Create(
+    std::unique_ptr<SSLIdentity> identity) {
+  return new RefCountedObject<RTCCertificate>(identity.release());
+}
+
+RTCCertificate::RTCCertificate(SSLIdentity* identity)
+    : identity_(identity) {
+  RTC_DCHECK(identity_);
+}
+
+RTCCertificate::~RTCCertificate() {
+}
+
+uint64_t RTCCertificate::Expires() const {
+  int64_t expires = ssl_certificate().CertificateExpirationTime();
+  if (expires != -1)
+    return static_cast<uint64_t>(expires) * kNumMillisecsPerSec;
+  // If the expiration time could not be retrieved return an expired timestamp.
+  return 0;  // = 1970-01-01
+}
+
+bool RTCCertificate::HasExpired(uint64_t now) const {
+  return Expires() <= now;
+}
+
+const SSLCertificate& RTCCertificate::ssl_certificate() const {
+  return identity_->certificate();
+}
+
+RTCCertificatePEM RTCCertificate::ToPEM() const {
+  return RTCCertificatePEM(identity_->PrivateKeyToPEMString(),
+                           ssl_certificate().ToPEMString());
+}
+
+scoped_refptr<RTCCertificate> RTCCertificate::FromPEM(
+    const RTCCertificatePEM& pem) {
+  std::unique_ptr<SSLIdentity> identity(SSLIdentity::FromPEMStrings(
+      pem.private_key(), pem.certificate()));
+  if (!identity)
+    return nullptr;
+  return new RefCountedObject<RTCCertificate>(identity.release());
+}
+
+bool RTCCertificate::operator==(const RTCCertificate& certificate) const {
+  return *this->identity_ == *certificate.identity_;
+}
+
+bool RTCCertificate::operator!=(const RTCCertificate& certificate) const {
+  return !(*this == certificate);
+}
+
+}  // namespace rtc
diff --git a/base/rtccertificate.h b/base/rtccertificate.h
index 22d8fe7..dfc7680 100644
--- a/base/rtccertificate.h
+++ b/base/rtccertificate.h
@@ -11,9 +11,77 @@
 #ifndef WEBRTC_BASE_RTCCERTIFICATE_H_
 #define WEBRTC_BASE_RTCCERTIFICATE_H_
 
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/rtccertificate.h"
+#include <memory>
+
+#include "webrtc/base/refcount.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+#include "webrtc/base/sslidentity.h"
+
+namespace rtc {
+
+// This class contains PEM strings of an RTCCertificate's private key and
+// certificate and acts as a text representation of RTCCertificate. Certificates
+// can be serialized and deserialized to and from this format, which allows for
+// cloning and storing of certificates to disk. The PEM format is that of
+// |SSLIdentity::PrivateKeyToPEMString| and |SSLCertificate::ToPEMString|, e.g.
+// the string representations used by OpenSSL.
+class RTCCertificatePEM {
+ public:
+  RTCCertificatePEM(
+      const std::string& private_key,
+      const std::string& certificate)
+      : private_key_(private_key),
+        certificate_(certificate) {}
+
+  const std::string& private_key() const { return private_key_; }
+  const std::string& certificate() const { return certificate_; }
+
+ private:
+  std::string private_key_;
+  std::string certificate_;
+};
+
+// A thin abstraction layer between "lower level crypto stuff" like
+// SSLCertificate and WebRTC usage. Takes ownership of some lower level objects,
+// reference counting protects these from premature destruction.
+class RTCCertificate : public RefCountInterface {
+ public:
+  // Takes ownership of |identity|.
+  static scoped_refptr<RTCCertificate> Create(
+      std::unique_ptr<SSLIdentity> identity);
+
+  // Returns the expiration time in ms relative to epoch, 1970-01-01T00:00:00Z.
+  uint64_t Expires() const;
+  // Checks if the certificate has expired, where |now| is expressed in ms
+  // relative to epoch, 1970-01-01T00:00:00Z.
+  bool HasExpired(uint64_t now) const;
+  const SSLCertificate& ssl_certificate() const;
+
+  // TODO(hbos): If possible, remove once RTCCertificate and its
+  // ssl_certificate() is used in all relevant places. Should not pass around
+  // raw SSLIdentity* for the sake of accessing SSLIdentity::certificate().
+  // However, some places might need SSLIdentity* for its public/private key...
+  SSLIdentity* identity() const { return identity_.get(); }
+
+  // To/from PEM, a text representation of the RTCCertificate.
+  RTCCertificatePEM ToPEM() const;
+  // Can return nullptr if the certificate is invalid.
+  static scoped_refptr<RTCCertificate> FromPEM(const RTCCertificatePEM& pem);
+  bool operator==(const RTCCertificate& certificate) const;
+  bool operator!=(const RTCCertificate& certificate) const;
+
+ protected:
+  explicit RTCCertificate(SSLIdentity* identity);
+  ~RTCCertificate() override;
+
+ private:
+  // The SSLIdentity is the owner of the SSLCertificate. To protect our
+  // ssl_certificate() we take ownership of |identity_|.
+  std::unique_ptr<SSLIdentity> identity_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_RTCCERTIFICATE_H_
diff --git a/base/rtccertificate_unittest.cc b/base/rtccertificate_unittest.cc
new file mode 100644
index 0000000..b318717
--- /dev/null
+++ b/base/rtccertificate_unittest.cc
@@ -0,0 +1,146 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <utility>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/fakesslidentity.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/rtccertificate.h"
+#include "webrtc/base/safe_conversions.h"
+#include "webrtc/base/sslidentity.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+
+namespace {
+
+static const char* kTestCertCommonName = "RTCCertificateTest's certificate";
+
+}  // namespace
+
+class RTCCertificateTest : public testing::Test {
+ public:
+  RTCCertificateTest() {}
+  ~RTCCertificateTest() {}
+
+ protected:
+  scoped_refptr<RTCCertificate> GenerateECDSA() {
+    std::unique_ptr<SSLIdentity> identity(
+        SSLIdentity::Generate(kTestCertCommonName, KeyParams::ECDSA()));
+    RTC_CHECK(identity);
+    return RTCCertificate::Create(std::move(identity));
+  }
+
+  // Timestamp note:
+  //   All timestamps in this unittest are expressed in number of seconds since
+  // epoch, 1970-01-01T00:00:00Z (UTC). The RTCCertificate interface uses ms,
+  // but only seconds-precision is supported by SSLCertificate. To make the
+  // tests clearer we convert everything to seconds since the precision matters
+  // when generating certificates or comparing timestamps.
+  //   As a result, ExpiresSeconds and HasExpiredSeconds are used instead of
+  // RTCCertificate::Expires and ::HasExpired for ms -> s conversion.
+
+  uint64_t NowSeconds() const {
+    return TimeNanos() / kNumNanosecsPerSec;
+  }
+
+  uint64_t ExpiresSeconds(const scoped_refptr<RTCCertificate>& cert) const {
+    uint64_t exp_ms = cert->Expires();
+    uint64_t exp_s = exp_ms / kNumMillisecsPerSec;
+    // Make sure this did not result in loss of precision.
+    RTC_CHECK_EQ(exp_s * kNumMillisecsPerSec, exp_ms);
+    return exp_s;
+  }
+
+  bool HasExpiredSeconds(const scoped_refptr<RTCCertificate>& cert,
+                         uint64_t now_s) const {
+    return cert->HasExpired(now_s * kNumMillisecsPerSec);
+  }
+
+  // An RTC_CHECK ensures that |expires_s| this is in valid range of time_t as
+  // is required by SSLIdentityParams. On some 32-bit systems time_t is limited
+  // to < 2^31. On such systems this will fail for expiration times of year 2038
+  // or later.
+  scoped_refptr<RTCCertificate> GenerateCertificateWithExpires(
+      uint64_t expires_s) const {
+    RTC_CHECK(IsValueInRangeForNumericType<time_t>(expires_s));
+
+    SSLIdentityParams params;
+    params.common_name = kTestCertCommonName;
+    params.not_before = 0;
+    params.not_after = static_cast<time_t>(expires_s);
+    // Certificate type does not matter for our purposes, using ECDSA because it
+    // is fast to generate.
+    params.key_params = KeyParams::ECDSA();
+
+    std::unique_ptr<SSLIdentity> identity(SSLIdentity::GenerateForTest(params));
+    return RTCCertificate::Create(std::move(identity));
+  }
+};
+
+TEST_F(RTCCertificateTest, NewCertificateNotExpired) {
+  // Generate a real certificate without specifying the expiration time.
+  // Certificate type doesn't matter, using ECDSA because it's fast to generate.
+  scoped_refptr<RTCCertificate> certificate = GenerateECDSA();
+
+  uint64_t now = NowSeconds();
+  EXPECT_FALSE(HasExpiredSeconds(certificate, now));
+  // Even without specifying the expiration time we would expect it to be valid
+  // for at least half an hour.
+  EXPECT_FALSE(HasExpiredSeconds(certificate, now + 30*60));
+}
+
+TEST_F(RTCCertificateTest, UsesExpiresAskedFor) {
+  uint64_t now = NowSeconds();
+  scoped_refptr<RTCCertificate> certificate =
+      GenerateCertificateWithExpires(now);
+  EXPECT_EQ(now, ExpiresSeconds(certificate));
+}
+
+TEST_F(RTCCertificateTest, ExpiresInOneSecond) {
+  // Generate a certificate that expires in 1s.
+  uint64_t now = NowSeconds();
+  scoped_refptr<RTCCertificate> certificate =
+      GenerateCertificateWithExpires(now + 1);
+  // Now it should not have expired.
+  EXPECT_FALSE(HasExpiredSeconds(certificate, now));
+  // In 2s it should have expired.
+  EXPECT_TRUE(HasExpiredSeconds(certificate, now + 2));
+}
+
+TEST_F(RTCCertificateTest, DifferentCertificatesNotEqual) {
+  scoped_refptr<RTCCertificate> a = GenerateECDSA();
+  scoped_refptr<RTCCertificate> b = GenerateECDSA();
+  EXPECT_TRUE(*a != *b);
+}
+
+TEST_F(RTCCertificateTest, CloneWithPEMSerialization) {
+  scoped_refptr<RTCCertificate> orig = GenerateECDSA();
+
+  // To PEM.
+  RTCCertificatePEM orig_pem = orig->ToPEM();
+  // Clone from PEM.
+  scoped_refptr<RTCCertificate> clone = RTCCertificate::FromPEM(orig_pem);
+  EXPECT_TRUE(clone);
+  EXPECT_TRUE(*orig == *clone);
+  EXPECT_EQ(orig->Expires(), clone->Expires());
+}
+
+TEST_F(RTCCertificateTest, FromPEMWithInvalidPEM) {
+  RTCCertificatePEM pem("not a valid PEM", "not a valid PEM");
+  scoped_refptr<RTCCertificate> certificate = RTCCertificate::FromPEM(pem);
+  EXPECT_FALSE(certificate);
+}
+
+}  // namespace rtc
diff --git a/base/rtccertificategenerator.cc b/base/rtccertificategenerator.cc
new file mode 100644
index 0000000..461a00c
--- /dev/null
+++ b/base/rtccertificategenerator.cc
@@ -0,0 +1,160 @@
+/*
+ *  Copyright 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/rtccertificategenerator.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/sslidentity.h"
+
+namespace rtc {
+
+namespace {
+
+// A certificates' subject and issuer name.
+const char kIdentityName[] = "WebRTC";
+
+uint64_t kYearInSeconds = 365 * 24 * 60 * 60;
+
+enum {
+  MSG_GENERATE,
+  MSG_GENERATE_DONE,
+};
+
+// Helper class for generating certificates asynchronously; a single task
+// instance is responsible for a single asynchronous certificate generation
+// request. We are using a separate helper class so that a generation request
+// can outlive the |RTCCertificateGenerator| that spawned it.
+class RTCCertificateGenerationTask : public RefCountInterface,
+                                     public MessageHandler {
+ public:
+  RTCCertificateGenerationTask(
+      Thread* signaling_thread,
+      Thread* worker_thread,
+      const KeyParams& key_params,
+      const Optional<uint64_t>& expires_ms,
+      const scoped_refptr<RTCCertificateGeneratorCallback>& callback)
+      : signaling_thread_(signaling_thread),
+        worker_thread_(worker_thread),
+        key_params_(key_params),
+        expires_ms_(expires_ms),
+        callback_(callback) {
+    RTC_DCHECK(signaling_thread_);
+    RTC_DCHECK(worker_thread_);
+    RTC_DCHECK(callback_);
+  }
+  ~RTCCertificateGenerationTask() override {}
+
+  // Handles |MSG_GENERATE| and its follow-up |MSG_GENERATE_DONE|.
+  void OnMessage(Message* msg) override {
+    switch (msg->message_id) {
+      case MSG_GENERATE:
+        RTC_DCHECK(worker_thread_->IsCurrent());
+
+        // Perform the certificate generation work here on the worker thread.
+        certificate_ = RTCCertificateGenerator::GenerateCertificate(
+            key_params_, expires_ms_);
+
+        // Handle callbacks on signaling thread. Pass on the |msg->pdata|
+        // (which references |this| with ref counting) to that thread.
+        signaling_thread_->Post(RTC_FROM_HERE, this, MSG_GENERATE_DONE,
+                                msg->pdata);
+        break;
+      case MSG_GENERATE_DONE:
+        RTC_DCHECK(signaling_thread_->IsCurrent());
+
+        // Perform callback with result here on the signaling thread.
+        if (certificate_) {
+          callback_->OnSuccess(certificate_);
+        } else {
+          callback_->OnFailure();
+        }
+
+        // Destroy |msg->pdata| which references |this| with ref counting. This
+        // may result in |this| being deleted - do not touch member variables
+        // after this line.
+        delete msg->pdata;
+        return;
+      default:
+        RTC_NOTREACHED();
+    }
+  }
+
+ private:
+  Thread* const signaling_thread_;
+  Thread* const worker_thread_;
+  const KeyParams key_params_;
+  const Optional<uint64_t> expires_ms_;
+  const scoped_refptr<RTCCertificateGeneratorCallback> callback_;
+  scoped_refptr<RTCCertificate> certificate_;
+};
+
+}  // namespace
+
+// static
+scoped_refptr<RTCCertificate>
+RTCCertificateGenerator::GenerateCertificate(
+    const KeyParams& key_params,
+    const Optional<uint64_t>& expires_ms) {
+  if (!key_params.IsValid())
+    return nullptr;
+  SSLIdentity* identity;
+  if (!expires_ms) {
+    identity = SSLIdentity::Generate(kIdentityName, key_params);
+  } else {
+    uint64_t expires_s = *expires_ms / 1000;
+    // Limit the expiration time to something reasonable (a year). This was
+    // somewhat arbitrarily chosen. It also ensures that the value is not too
+    // large for the unspecified |time_t|.
+    expires_s = std::min(expires_s, kYearInSeconds);
+    // TODO(torbjorng): Stop using |time_t|, its type is unspecified. It it safe
+    // to assume it can hold up to a year's worth of seconds (and more), but
+    // |SSLIdentity::Generate| should stop relying on |time_t|.
+    // See bugs.webrtc.org/5720.
+    time_t cert_lifetime_s = static_cast<time_t>(expires_s);
+    identity = SSLIdentity::GenerateWithExpiration(
+        kIdentityName, key_params, cert_lifetime_s);
+  }
+  if (!identity)
+    return nullptr;
+  std::unique_ptr<SSLIdentity> identity_sptr(identity);
+  return RTCCertificate::Create(std::move(identity_sptr));
+}
+
+RTCCertificateGenerator::RTCCertificateGenerator(
+    Thread* signaling_thread, Thread* worker_thread)
+    : signaling_thread_(signaling_thread),
+      worker_thread_(worker_thread) {
+  RTC_DCHECK(signaling_thread_);
+  RTC_DCHECK(worker_thread_);
+}
+
+void RTCCertificateGenerator::GenerateCertificateAsync(
+    const KeyParams& key_params,
+    const Optional<uint64_t>& expires_ms,
+    const scoped_refptr<RTCCertificateGeneratorCallback>& callback) {
+  RTC_DCHECK(signaling_thread_->IsCurrent());
+  RTC_DCHECK(callback);
+
+  // Create a new |RTCCertificateGenerationTask| for this generation request. It
+  // is reference counted and referenced by the message data, ensuring it lives
+  // until the task has completed (independent of |RTCCertificateGenerator|).
+  ScopedRefMessageData<RTCCertificateGenerationTask>* msg_data =
+      new ScopedRefMessageData<RTCCertificateGenerationTask>(
+          new RefCountedObject<RTCCertificateGenerationTask>(
+              signaling_thread_, worker_thread_, key_params, expires_ms,
+              callback));
+  worker_thread_->Post(RTC_FROM_HERE, msg_data->data().get(), MSG_GENERATE,
+                       msg_data);
+}
+
+}  // namespace rtc
diff --git a/base/rtccertificategenerator.h b/base/rtccertificategenerator.h
index fac1cec..c131d69 100644
--- a/base/rtccertificategenerator.h
+++ b/base/rtccertificategenerator.h
@@ -11,9 +11,76 @@
 #ifndef WEBRTC_BASE_RTCCERTIFICATEGENERATOR_H_
 #define WEBRTC_BASE_RTCCERTIFICATEGENERATOR_H_
 
+#include "webrtc/base/optional.h"
+#include "webrtc/base/refcount.h"
+#include "webrtc/base/rtccertificate.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+#include "webrtc/base/sslidentity.h"
+#include "webrtc/base/thread.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/rtccertificategenerator.h"
+namespace rtc {
+
+// See |RTCCertificateGeneratorInterface::GenerateCertificateAsync|.
+class RTCCertificateGeneratorCallback : public RefCountInterface {
+ public:
+  virtual void OnSuccess(
+    const scoped_refptr<RTCCertificate>& certificate) = 0;
+  virtual void OnFailure() = 0;
+
+ protected:
+  ~RTCCertificateGeneratorCallback() override {}
+};
+
+// Generates |RTCCertificate|s.
+// See |RTCCertificateGenerator| for the WebRTC repo's implementation.
+class RTCCertificateGeneratorInterface {
+ public:
+  virtual ~RTCCertificateGeneratorInterface() {}
+
+  // Generates a certificate asynchronously on the worker thread.
+  // Must be called on the signaling thread. The |callback| is invoked with the
+  // result on the signaling thread. |exipres_ms| optionally specifies for how
+  // long we want the certificate to be valid, but the implementation may choose
+  // its own restrictions on the expiration time.
+  virtual void GenerateCertificateAsync(
+      const KeyParams& key_params,
+      const Optional<uint64_t>& expires_ms,
+      const scoped_refptr<RTCCertificateGeneratorCallback>& callback) = 0;
+};
+
+// Standard implementation of |RTCCertificateGeneratorInterface|.
+// The static function |GenerateCertificate| generates a certificate on the
+// current thread. The |RTCCertificateGenerator| instance generates certificates
+// asynchronously on the worker thread with |GenerateCertificateAsync|.
+class RTCCertificateGenerator : public RTCCertificateGeneratorInterface {
+ public:
+  // Generates a certificate on the current thread. Returns null on failure.
+  // If |expires_ms| is specified, the certificate will expire in approximately
+  // that many milliseconds from now. |expires_ms| is limited to a year, a
+  // larger value than that is clamped down to a year. If |expires_ms| is not
+  // specified, a default expiration time is used.
+  static scoped_refptr<RTCCertificate> GenerateCertificate(
+      const KeyParams& key_params,
+      const Optional<uint64_t>& expires_ms);
+
+  RTCCertificateGenerator(Thread* signaling_thread, Thread* worker_thread);
+  ~RTCCertificateGenerator() override {}
+
+  // |RTCCertificateGeneratorInterface| overrides.
+  // If |expires_ms| is specified, the certificate will expire in approximately
+  // that many milliseconds from now. |expires_ms| is limited to a year, a
+  // larger value than that is clamped down to a year. If |expires_ms| is not
+  // specified, a default expiration time is used.
+  void GenerateCertificateAsync(
+      const KeyParams& key_params,
+      const Optional<uint64_t>& expires_ms,
+      const scoped_refptr<RTCCertificateGeneratorCallback>& callback) override;
+
+ private:
+  Thread* const signaling_thread_;
+  Thread* const worker_thread_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_RTCCERTIFICATEGENERATOR_H_
diff --git a/base/rtccertificategenerator_unittest.cc b/base/rtccertificategenerator_unittest.cc
new file mode 100644
index 0000000..8dc3cd2
--- /dev/null
+++ b/base/rtccertificategenerator_unittest.cc
@@ -0,0 +1,152 @@
+/*
+ *  Copyright 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/rtccertificategenerator.h"
+
+#include <memory>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/optional.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+class RTCCertificateGeneratorFixture : public RTCCertificateGeneratorCallback {
+ public:
+  RTCCertificateGeneratorFixture()
+      : signaling_thread_(Thread::Current()),
+        worker_thread_(new Thread()),
+        generate_async_completed_(false) {
+    RTC_CHECK(signaling_thread_);
+    RTC_CHECK(worker_thread_->Start());
+    generator_.reset(
+      new RTCCertificateGenerator(signaling_thread_, worker_thread_.get()));
+  }
+  ~RTCCertificateGeneratorFixture() override {}
+
+  RTCCertificateGenerator* generator() const { return generator_.get(); }
+  RTCCertificate* certificate() const { return certificate_.get(); }
+
+  void OnSuccess(const scoped_refptr<RTCCertificate>& certificate) override {
+    RTC_CHECK(signaling_thread_->IsCurrent());
+    RTC_CHECK(certificate);
+    certificate_ = certificate;
+    generate_async_completed_ = true;
+  }
+  void OnFailure() override {
+    RTC_CHECK(signaling_thread_->IsCurrent());
+    certificate_ = nullptr;
+    generate_async_completed_ = true;
+  }
+
+  bool GenerateAsyncCompleted() {
+    RTC_CHECK(signaling_thread_->IsCurrent());
+    if (generate_async_completed_) {
+      // Reset flag so that future generation requests are not considered done.
+      generate_async_completed_ = false;
+      return true;
+    }
+    return false;
+  }
+
+ protected:
+  Thread* const signaling_thread_;
+  std::unique_ptr<Thread> worker_thread_;
+  std::unique_ptr<RTCCertificateGenerator> generator_;
+  scoped_refptr<RTCCertificate> certificate_;
+  bool generate_async_completed_;
+};
+
+class RTCCertificateGeneratorTest
+    : public testing::Test {
+ public:
+  RTCCertificateGeneratorTest()
+      : fixture_(new RefCountedObject<RTCCertificateGeneratorFixture>()) {}
+  ~RTCCertificateGeneratorTest() {}
+
+ protected:
+  static const int kGenerationTimeoutMs = 10000;
+
+  scoped_refptr<RTCCertificateGeneratorFixture> fixture_;
+};
+
+TEST_F(RTCCertificateGeneratorTest, GenerateECDSA) {
+  EXPECT_TRUE(RTCCertificateGenerator::GenerateCertificate(
+      KeyParams::ECDSA(),
+      Optional<uint64_t>()));
+}
+
+TEST_F(RTCCertificateGeneratorTest, GenerateRSA) {
+  EXPECT_TRUE(RTCCertificateGenerator::GenerateCertificate(
+      KeyParams::RSA(),
+      Optional<uint64_t>()));
+}
+
+TEST_F(RTCCertificateGeneratorTest, GenerateAsyncECDSA) {
+  EXPECT_FALSE(fixture_->certificate());
+  fixture_->generator()->GenerateCertificateAsync(
+      KeyParams::ECDSA(),
+      Optional<uint64_t>(),
+      fixture_);
+  // Until generation has completed, the certificate is null. Since this is an
+  // async call, generation must not have completed until we process messages
+  // posted to this thread (which is done by |EXPECT_TRUE_WAIT|).
+  EXPECT_FALSE(fixture_->GenerateAsyncCompleted());
+  EXPECT_FALSE(fixture_->certificate());
+  EXPECT_TRUE_WAIT(fixture_->GenerateAsyncCompleted(), kGenerationTimeoutMs);
+  EXPECT_TRUE(fixture_->certificate());
+}
+
+TEST_F(RTCCertificateGeneratorTest, GenerateWithExpires) {
+  // By generating two certificates with different expiration we can compare the
+  // two expiration times relative to each other without knowing the current
+  // time relative to epoch, 1970-01-01T00:00:00Z. This verifies that the
+  // expiration parameter is correctly used relative to the generator's clock,
+  // but does not verify that this clock is relative to epoch.
+
+  // Generate a certificate that expires immediately.
+  scoped_refptr<RTCCertificate> cert_a =
+      RTCCertificateGenerator::GenerateCertificate(
+          KeyParams::ECDSA(), Optional<uint64_t>(0));
+  EXPECT_TRUE(cert_a);
+
+  // Generate a certificate that expires in one minute.
+  const uint64_t kExpiresMs = 60000;
+  scoped_refptr<RTCCertificate> cert_b =
+      RTCCertificateGenerator::GenerateCertificate(
+          KeyParams::ECDSA(), Optional<uint64_t>(kExpiresMs));
+  EXPECT_TRUE(cert_b);
+
+  // Verify that |cert_b| expires approximately |kExpiresMs| after |cert_a|
+  // (allowing a +/- 1 second plus maximum generation time difference).
+  EXPECT_GT(cert_b->Expires(), cert_a->Expires());
+  uint64_t expires_diff = cert_b->Expires() - cert_a->Expires();
+  EXPECT_GE(expires_diff, kExpiresMs);
+  EXPECT_LE(expires_diff, kExpiresMs + 2*kGenerationTimeoutMs + 1000);
+}
+
+TEST_F(RTCCertificateGeneratorTest, GenerateWithInvalidParamsShouldFail) {
+  KeyParams invalid_params = KeyParams::RSA(0, 0);
+  EXPECT_FALSE(invalid_params.IsValid());
+
+  EXPECT_FALSE(RTCCertificateGenerator::GenerateCertificate(
+      invalid_params, Optional<uint64_t>()));
+
+  fixture_->generator()->GenerateCertificateAsync(
+      invalid_params,
+      Optional<uint64_t>(),
+      fixture_);
+  EXPECT_TRUE_WAIT(fixture_->GenerateAsyncCompleted(), kGenerationTimeoutMs);
+  EXPECT_FALSE(fixture_->certificate());
+}
+
+}  // namespace rtc
diff --git a/base/safe_compare.h b/base/safe_compare.h
index acdd9ce..a57f082 100644
--- a/base/safe_compare.h
+++ b/base/safe_compare.h
@@ -31,9 +31,146 @@
 #ifndef WEBRTC_BASE_SAFE_COMPARE_H_
 #define WEBRTC_BASE_SAFE_COMPARE_H_
 
+#include <stddef.h>
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/safe_compare.h"
+#include <type_traits>
+#include <utility>
+
+#include "webrtc/base/type_traits.h"
+
+namespace rtc {
+
+namespace safe_cmp_impl {
+
+template <size_t N>
+struct LargerIntImpl : std::false_type {};
+template <>
+struct LargerIntImpl<sizeof(int8_t)> : std::true_type {
+  using type = int16_t;
+};
+template <>
+struct LargerIntImpl<sizeof(int16_t)> : std::true_type {
+  using type = int32_t;
+};
+template <>
+struct LargerIntImpl<sizeof(int32_t)> : std::true_type {
+  using type = int64_t;
+};
+
+// LargerInt<T1, T2>::value is true iff there's a signed type that's larger
+// than T1 (and no larger than the larger of T2 and int*, for performance
+// reasons); and if there is such a type, LargerInt<T1, T2>::type is an alias
+// for it.
+template <typename T1, typename T2>
+struct LargerInt
+    : LargerIntImpl<sizeof(T1) < sizeof(T2) || sizeof(T1) < sizeof(int*)
+                        ? sizeof(T1)
+                        : 0> {};
+
+template <typename T>
+constexpr typename std::make_unsigned<T>::type MakeUnsigned(T a) {
+  return static_cast<typename std::make_unsigned<T>::type>(a);
+}
+
+// Overload for when both T1 and T2 have the same signedness.
+template <typename Op,
+          typename T1,
+          typename T2,
+          typename std::enable_if<std::is_signed<T1>::value ==
+                                  std::is_signed<T2>::value>::type* = nullptr>
+constexpr bool Cmp(T1 a, T2 b) {
+  return Op::Op(a, b);
+}
+
+// Overload for signed - unsigned comparison that can be promoted to a bigger
+// signed type.
+template <typename Op,
+          typename T1,
+          typename T2,
+          typename std::enable_if<std::is_signed<T1>::value &&
+                                  std::is_unsigned<T2>::value &&
+                                  LargerInt<T2, T1>::value>::type* = nullptr>
+constexpr bool Cmp(T1 a, T2 b) {
+  return Op::Op(a, static_cast<typename LargerInt<T2, T1>::type>(b));
+}
+
+// Overload for unsigned - signed comparison that can be promoted to a bigger
+// signed type.
+template <typename Op,
+          typename T1,
+          typename T2,
+          typename std::enable_if<std::is_unsigned<T1>::value &&
+                                  std::is_signed<T2>::value &&
+                                  LargerInt<T1, T2>::value>::type* = nullptr>
+constexpr bool Cmp(T1 a, T2 b) {
+  return Op::Op(static_cast<typename LargerInt<T1, T2>::type>(a), b);
+}
+
+// Overload for signed - unsigned comparison that can't be promoted to a bigger
+// signed type.
+template <typename Op,
+          typename T1,
+          typename T2,
+          typename std::enable_if<std::is_signed<T1>::value &&
+                                  std::is_unsigned<T2>::value &&
+                                  !LargerInt<T2, T1>::value>::type* = nullptr>
+constexpr bool Cmp(T1 a, T2 b) {
+  return a < 0 ? Op::Op(-1, 0) : Op::Op(safe_cmp_impl::MakeUnsigned(a), b);
+}
+
+// Overload for unsigned - signed comparison that can't be promoted to a bigger
+// signed type.
+template <typename Op,
+          typename T1,
+          typename T2,
+          typename std::enable_if<std::is_unsigned<T1>::value &&
+                                  std::is_signed<T2>::value &&
+                                  !LargerInt<T1, T2>::value>::type* = nullptr>
+constexpr bool Cmp(T1 a, T2 b) {
+  return b < 0 ? Op::Op(0, -1) : Op::Op(a, safe_cmp_impl::MakeUnsigned(b));
+}
+
+#define RTC_SAFECMP_MAKE_OP(name, op)      \
+  struct name {                            \
+    template <typename T1, typename T2>    \
+    static constexpr bool Op(T1 a, T2 b) { \
+      return a op b;                       \
+    }                                      \
+  };
+RTC_SAFECMP_MAKE_OP(EqOp, ==)
+RTC_SAFECMP_MAKE_OP(NeOp, !=)
+RTC_SAFECMP_MAKE_OP(LtOp, <)
+RTC_SAFECMP_MAKE_OP(LeOp, <=)
+RTC_SAFECMP_MAKE_OP(GtOp, >)
+RTC_SAFECMP_MAKE_OP(GeOp, >=)
+#undef RTC_SAFECMP_MAKE_OP
+
+}  // namespace safe_cmp_impl
+
+#define RTC_SAFECMP_MAKE_FUN(name)                                            \
+  template <typename T1, typename T2>                                         \
+  constexpr                                                                   \
+      typename std::enable_if<IsIntlike<T1>::value && IsIntlike<T2>::value,   \
+                              bool>::type Safe##name(T1 a, T2 b) {            \
+    /* Unary plus here turns enums into real integral types. */               \
+    return safe_cmp_impl::Cmp<safe_cmp_impl::name##Op>(+a, +b);               \
+  }                                                                           \
+  template <typename T1, typename T2>                                         \
+  constexpr                                                                   \
+      typename std::enable_if<!IsIntlike<T1>::value || !IsIntlike<T2>::value, \
+                              bool>::type Safe##name(const T1& a,             \
+                                                     const T2& b) {           \
+    return safe_cmp_impl::name##Op::Op(a, b);                                 \
+  }
+RTC_SAFECMP_MAKE_FUN(Eq)
+RTC_SAFECMP_MAKE_FUN(Ne)
+RTC_SAFECMP_MAKE_FUN(Lt)
+RTC_SAFECMP_MAKE_FUN(Le)
+RTC_SAFECMP_MAKE_FUN(Gt)
+RTC_SAFECMP_MAKE_FUN(Ge)
+#undef RTC_SAFECMP_MAKE_FUN
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SAFE_COMPARE_H_
diff --git a/base/safe_compare_unittest.cc b/base/safe_compare_unittest.cc
new file mode 100644
index 0000000..1ebb722
--- /dev/null
+++ b/base/safe_compare_unittest.cc
@@ -0,0 +1,394 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <limits>
+
+#include "webrtc/base/safe_compare.h"
+#include "webrtc/test/gtest.h"
+
+namespace rtc {
+
+namespace {
+
+constexpr std::uintmax_t umax = std::numeric_limits<std::uintmax_t>::max();
+constexpr std::intmax_t imin = std::numeric_limits<std::intmax_t>::min();
+constexpr std::intmax_t m1 = -1;
+
+// m1 and umax have the same representation because we use 2's complement
+// arithmetic, so naive casting will confuse them.
+static_assert(static_cast<std::uintmax_t>(m1) == umax, "");
+static_assert(m1 == static_cast<std::intmax_t>(umax), "");
+
+static const std::pair<int, int> p1(1, 1);
+static const std::pair<int, int> p2(1, 2);
+
+}  // namespace
+
+// clang-format off
+
+// These functions aren't used in the tests, but it's useful to look at the
+// compiler output for them, and verify that (1) the same-signedness *Safe
+// functions result in exactly the same code as their *Ref counterparts, and
+// that (2) the mixed-signedness *Safe functions have just a few extra
+// arithmetic and logic instructions (but no extra control flow instructions).
+bool TestLessThanRef(      int a,      int b) { return a < b; }
+bool TestLessThanRef( unsigned a, unsigned b) { return a < b; }
+bool TestLessThanSafe(     int a,      int b) { return SafeLt(a, b); }
+bool TestLessThanSafe(unsigned a, unsigned b) { return SafeLt(a, b); }
+bool TestLessThanSafe(unsigned a,      int b) { return SafeLt(a, b); }
+bool TestLessThanSafe(     int a, unsigned b) { return SafeLt(a, b); }
+
+// For these, we expect the *Ref and *Safe functions to result in identical
+// code, except for the ones that compare a signed variable with an unsigned
+// constant; in that case, the *Ref function does an unsigned comparison (fast
+// but incorrect) and the *Safe function spends a few extra instructions on
+// doing it right.
+bool TestLessThan17Ref(       int a) { return a < 17; }
+bool TestLessThan17Ref(  unsigned a) { return a < 17; }
+bool TestLessThan17uRef(      int a) { return static_cast<unsigned>(a) < 17u; }
+bool TestLessThan17uRef( unsigned a) { return a < 17u; }
+bool TestLessThan17Safe(      int a) { return SafeLt(a, 17); }
+bool TestLessThan17Safe( unsigned a) { return SafeLt(a, 17); }
+bool TestLessThan17uSafe(     int a) { return SafeLt(a, 17u); }
+bool TestLessThan17uSafe(unsigned a) { return SafeLt(a, 17u); }
+
+// Cases where we can't convert to a larger signed type.
+bool TestLessThanMax( intmax_t a, uintmax_t b) { return SafeLt(a, b); }
+bool TestLessThanMax(uintmax_t a,  intmax_t b) { return SafeLt(a, b); }
+bool TestLessThanMax17u( intmax_t a) { return SafeLt(a, uintmax_t{17}); }
+bool TestLessThanMax17( uintmax_t a) { return SafeLt(a,  intmax_t{17}); }
+
+// Cases where the compiler should be able to compute the result at compile
+// time.
+bool TestLessThanConst1() { return SafeLt(  -1,    1); }
+bool TestLessThanConst2() { return SafeLt(  m1, umax); }
+bool TestLessThanConst3() { return SafeLt(umax, imin); }
+bool TestLessThanConst4(unsigned a) { return SafeLt( a, -1); }
+bool TestLessThanConst5(unsigned a) { return SafeLt(-1,  a); }
+bool TestLessThanConst6(unsigned a) { return SafeLt( a,  a); }
+
+// clang-format on
+
+TEST(SafeCmpTest, Eq) {
+  static_assert(!SafeEq(-1, 2), "");
+  static_assert(!SafeEq(-1, 2u), "");
+  static_assert(!SafeEq(2, -1), "");
+  static_assert(!SafeEq(2u, -1), "");
+
+  static_assert(!SafeEq(1, 2), "");
+  static_assert(!SafeEq(1, 2u), "");
+  static_assert(!SafeEq(1u, 2), "");
+  static_assert(!SafeEq(1u, 2u), "");
+  static_assert(!SafeEq(2, 1), "");
+  static_assert(!SafeEq(2, 1u), "");
+  static_assert(!SafeEq(2u, 1), "");
+  static_assert(!SafeEq(2u, 1u), "");
+
+  static_assert(SafeEq(2, 2), "");
+  static_assert(SafeEq(2, 2u), "");
+  static_assert(SafeEq(2u, 2), "");
+  static_assert(SafeEq(2u, 2u), "");
+
+  static_assert(SafeEq(imin, imin), "");
+  static_assert(!SafeEq(imin, umax), "");
+  static_assert(!SafeEq(umax, imin), "");
+  static_assert(SafeEq(umax, umax), "");
+
+  static_assert(SafeEq(m1, m1), "");
+  static_assert(!SafeEq(m1, umax), "");
+  static_assert(!SafeEq(umax, m1), "");
+  static_assert(SafeEq(umax, umax), "");
+
+  static_assert(!SafeEq(1, 2), "");
+  static_assert(!SafeEq(1, 2.0), "");
+  static_assert(!SafeEq(1.0, 2), "");
+  static_assert(!SafeEq(1.0, 2.0), "");
+  static_assert(!SafeEq(2, 1), "");
+  static_assert(!SafeEq(2, 1.0), "");
+  static_assert(!SafeEq(2.0, 1), "");
+  static_assert(!SafeEq(2.0, 1.0), "");
+
+  static_assert(SafeEq(2, 2), "");
+  static_assert(SafeEq(2, 2.0), "");
+  static_assert(SafeEq(2.0, 2), "");
+  static_assert(SafeEq(2.0, 2.0), "");
+
+  EXPECT_TRUE(SafeEq(p1, p1));
+  EXPECT_FALSE(SafeEq(p1, p2));
+  EXPECT_FALSE(SafeEq(p2, p1));
+  EXPECT_TRUE(SafeEq(p2, p2));
+}
+
+TEST(SafeCmpTest, Ne) {
+  static_assert(SafeNe(-1, 2), "");
+  static_assert(SafeNe(-1, 2u), "");
+  static_assert(SafeNe(2, -1), "");
+  static_assert(SafeNe(2u, -1), "");
+
+  static_assert(SafeNe(1, 2), "");
+  static_assert(SafeNe(1, 2u), "");
+  static_assert(SafeNe(1u, 2), "");
+  static_assert(SafeNe(1u, 2u), "");
+  static_assert(SafeNe(2, 1), "");
+  static_assert(SafeNe(2, 1u), "");
+  static_assert(SafeNe(2u, 1), "");
+  static_assert(SafeNe(2u, 1u), "");
+
+  static_assert(!SafeNe(2, 2), "");
+  static_assert(!SafeNe(2, 2u), "");
+  static_assert(!SafeNe(2u, 2), "");
+  static_assert(!SafeNe(2u, 2u), "");
+
+  static_assert(!SafeNe(imin, imin), "");
+  static_assert(SafeNe(imin, umax), "");
+  static_assert(SafeNe(umax, imin), "");
+  static_assert(!SafeNe(umax, umax), "");
+
+  static_assert(!SafeNe(m1, m1), "");
+  static_assert(SafeNe(m1, umax), "");
+  static_assert(SafeNe(umax, m1), "");
+  static_assert(!SafeNe(umax, umax), "");
+
+  static_assert(SafeNe(1, 2), "");
+  static_assert(SafeNe(1, 2.0), "");
+  static_assert(SafeNe(1.0, 2), "");
+  static_assert(SafeNe(1.0, 2.0), "");
+  static_assert(SafeNe(2, 1), "");
+  static_assert(SafeNe(2, 1.0), "");
+  static_assert(SafeNe(2.0, 1), "");
+  static_assert(SafeNe(2.0, 1.0), "");
+
+  static_assert(!SafeNe(2, 2), "");
+  static_assert(!SafeNe(2, 2.0), "");
+  static_assert(!SafeNe(2.0, 2), "");
+  static_assert(!SafeNe(2.0, 2.0), "");
+
+  EXPECT_FALSE(SafeNe(p1, p1));
+  EXPECT_TRUE(SafeNe(p1, p2));
+  EXPECT_TRUE(SafeNe(p2, p1));
+  EXPECT_FALSE(SafeNe(p2, p2));
+}
+
+TEST(SafeCmpTest, Lt) {
+  static_assert(SafeLt(-1, 2), "");
+  static_assert(SafeLt(-1, 2u), "");
+  static_assert(!SafeLt(2, -1), "");
+  static_assert(!SafeLt(2u, -1), "");
+
+  static_assert(SafeLt(1, 2), "");
+  static_assert(SafeLt(1, 2u), "");
+  static_assert(SafeLt(1u, 2), "");
+  static_assert(SafeLt(1u, 2u), "");
+  static_assert(!SafeLt(2, 1), "");
+  static_assert(!SafeLt(2, 1u), "");
+  static_assert(!SafeLt(2u, 1), "");
+  static_assert(!SafeLt(2u, 1u), "");
+
+  static_assert(!SafeLt(2, 2), "");
+  static_assert(!SafeLt(2, 2u), "");
+  static_assert(!SafeLt(2u, 2), "");
+  static_assert(!SafeLt(2u, 2u), "");
+
+  static_assert(!SafeLt(imin, imin), "");
+  static_assert(SafeLt(imin, umax), "");
+  static_assert(!SafeLt(umax, imin), "");
+  static_assert(!SafeLt(umax, umax), "");
+
+  static_assert(!SafeLt(m1, m1), "");
+  static_assert(SafeLt(m1, umax), "");
+  static_assert(!SafeLt(umax, m1), "");
+  static_assert(!SafeLt(umax, umax), "");
+
+  static_assert(SafeLt(1, 2), "");
+  static_assert(SafeLt(1, 2.0), "");
+  static_assert(SafeLt(1.0, 2), "");
+  static_assert(SafeLt(1.0, 2.0), "");
+  static_assert(!SafeLt(2, 1), "");
+  static_assert(!SafeLt(2, 1.0), "");
+  static_assert(!SafeLt(2.0, 1), "");
+  static_assert(!SafeLt(2.0, 1.0), "");
+
+  static_assert(!SafeLt(2, 2), "");
+  static_assert(!SafeLt(2, 2.0), "");
+  static_assert(!SafeLt(2.0, 2), "");
+  static_assert(!SafeLt(2.0, 2.0), "");
+
+  EXPECT_FALSE(SafeLt(p1, p1));
+  EXPECT_TRUE(SafeLt(p1, p2));
+  EXPECT_FALSE(SafeLt(p2, p1));
+  EXPECT_FALSE(SafeLt(p2, p2));
+}
+
+TEST(SafeCmpTest, Le) {
+  static_assert(SafeLe(-1, 2), "");
+  static_assert(SafeLe(-1, 2u), "");
+  static_assert(!SafeLe(2, -1), "");
+  static_assert(!SafeLe(2u, -1), "");
+
+  static_assert(SafeLe(1, 2), "");
+  static_assert(SafeLe(1, 2u), "");
+  static_assert(SafeLe(1u, 2), "");
+  static_assert(SafeLe(1u, 2u), "");
+  static_assert(!SafeLe(2, 1), "");
+  static_assert(!SafeLe(2, 1u), "");
+  static_assert(!SafeLe(2u, 1), "");
+  static_assert(!SafeLe(2u, 1u), "");
+
+  static_assert(SafeLe(2, 2), "");
+  static_assert(SafeLe(2, 2u), "");
+  static_assert(SafeLe(2u, 2), "");
+  static_assert(SafeLe(2u, 2u), "");
+
+  static_assert(SafeLe(imin, imin), "");
+  static_assert(SafeLe(imin, umax), "");
+  static_assert(!SafeLe(umax, imin), "");
+  static_assert(SafeLe(umax, umax), "");
+
+  static_assert(SafeLe(m1, m1), "");
+  static_assert(SafeLe(m1, umax), "");
+  static_assert(!SafeLe(umax, m1), "");
+  static_assert(SafeLe(umax, umax), "");
+
+  static_assert(SafeLe(1, 2), "");
+  static_assert(SafeLe(1, 2.0), "");
+  static_assert(SafeLe(1.0, 2), "");
+  static_assert(SafeLe(1.0, 2.0), "");
+  static_assert(!SafeLe(2, 1), "");
+  static_assert(!SafeLe(2, 1.0), "");
+  static_assert(!SafeLe(2.0, 1), "");
+  static_assert(!SafeLe(2.0, 1.0), "");
+
+  static_assert(SafeLe(2, 2), "");
+  static_assert(SafeLe(2, 2.0), "");
+  static_assert(SafeLe(2.0, 2), "");
+  static_assert(SafeLe(2.0, 2.0), "");
+
+  EXPECT_TRUE(SafeLe(p1, p1));
+  EXPECT_TRUE(SafeLe(p1, p2));
+  EXPECT_FALSE(SafeLe(p2, p1));
+  EXPECT_TRUE(SafeLe(p2, p2));
+}
+
+TEST(SafeCmpTest, Gt) {
+  static_assert(!SafeGt(-1, 2), "");
+  static_assert(!SafeGt(-1, 2u), "");
+  static_assert(SafeGt(2, -1), "");
+  static_assert(SafeGt(2u, -1), "");
+
+  static_assert(!SafeGt(1, 2), "");
+  static_assert(!SafeGt(1, 2u), "");
+  static_assert(!SafeGt(1u, 2), "");
+  static_assert(!SafeGt(1u, 2u), "");
+  static_assert(SafeGt(2, 1), "");
+  static_assert(SafeGt(2, 1u), "");
+  static_assert(SafeGt(2u, 1), "");
+  static_assert(SafeGt(2u, 1u), "");
+
+  static_assert(!SafeGt(2, 2), "");
+  static_assert(!SafeGt(2, 2u), "");
+  static_assert(!SafeGt(2u, 2), "");
+  static_assert(!SafeGt(2u, 2u), "");
+
+  static_assert(!SafeGt(imin, imin), "");
+  static_assert(!SafeGt(imin, umax), "");
+  static_assert(SafeGt(umax, imin), "");
+  static_assert(!SafeGt(umax, umax), "");
+
+  static_assert(!SafeGt(m1, m1), "");
+  static_assert(!SafeGt(m1, umax), "");
+  static_assert(SafeGt(umax, m1), "");
+  static_assert(!SafeGt(umax, umax), "");
+
+  static_assert(!SafeGt(1, 2), "");
+  static_assert(!SafeGt(1, 2.0), "");
+  static_assert(!SafeGt(1.0, 2), "");
+  static_assert(!SafeGt(1.0, 2.0), "");
+  static_assert(SafeGt(2, 1), "");
+  static_assert(SafeGt(2, 1.0), "");
+  static_assert(SafeGt(2.0, 1), "");
+  static_assert(SafeGt(2.0, 1.0), "");
+
+  static_assert(!SafeGt(2, 2), "");
+  static_assert(!SafeGt(2, 2.0), "");
+  static_assert(!SafeGt(2.0, 2), "");
+  static_assert(!SafeGt(2.0, 2.0), "");
+
+  EXPECT_FALSE(SafeGt(p1, p1));
+  EXPECT_FALSE(SafeGt(p1, p2));
+  EXPECT_TRUE(SafeGt(p2, p1));
+  EXPECT_FALSE(SafeGt(p2, p2));
+}
+
+TEST(SafeCmpTest, Ge) {
+  static_assert(!SafeGe(-1, 2), "");
+  static_assert(!SafeGe(-1, 2u), "");
+  static_assert(SafeGe(2, -1), "");
+  static_assert(SafeGe(2u, -1), "");
+
+  static_assert(!SafeGe(1, 2), "");
+  static_assert(!SafeGe(1, 2u), "");
+  static_assert(!SafeGe(1u, 2), "");
+  static_assert(!SafeGe(1u, 2u), "");
+  static_assert(SafeGe(2, 1), "");
+  static_assert(SafeGe(2, 1u), "");
+  static_assert(SafeGe(2u, 1), "");
+  static_assert(SafeGe(2u, 1u), "");
+
+  static_assert(SafeGe(2, 2), "");
+  static_assert(SafeGe(2, 2u), "");
+  static_assert(SafeGe(2u, 2), "");
+  static_assert(SafeGe(2u, 2u), "");
+
+  static_assert(SafeGe(imin, imin), "");
+  static_assert(!SafeGe(imin, umax), "");
+  static_assert(SafeGe(umax, imin), "");
+  static_assert(SafeGe(umax, umax), "");
+
+  static_assert(SafeGe(m1, m1), "");
+  static_assert(!SafeGe(m1, umax), "");
+  static_assert(SafeGe(umax, m1), "");
+  static_assert(SafeGe(umax, umax), "");
+
+  static_assert(!SafeGe(1, 2), "");
+  static_assert(!SafeGe(1, 2.0), "");
+  static_assert(!SafeGe(1.0, 2), "");
+  static_assert(!SafeGe(1.0, 2.0), "");
+  static_assert(SafeGe(2, 1), "");
+  static_assert(SafeGe(2, 1.0), "");
+  static_assert(SafeGe(2.0, 1), "");
+  static_assert(SafeGe(2.0, 1.0), "");
+
+  static_assert(SafeGe(2, 2), "");
+  static_assert(SafeGe(2, 2.0), "");
+  static_assert(SafeGe(2.0, 2), "");
+  static_assert(SafeGe(2.0, 2.0), "");
+
+  EXPECT_TRUE(SafeGe(p1, p1));
+  EXPECT_FALSE(SafeGe(p1, p2));
+  EXPECT_TRUE(SafeGe(p2, p1));
+  EXPECT_TRUE(SafeGe(p2, p2));
+}
+
+TEST(SafeCmpTest, Enum) {
+  enum E1 { e1 = 13 };
+  enum { e2 = 13 };
+  enum E3 : unsigned { e3 = 13 };
+  enum : unsigned { e4 = 13 };
+  static_assert(SafeEq(13, e1), "");
+  static_assert(SafeEq(13u, e1), "");
+  static_assert(SafeEq(13, e2), "");
+  static_assert(SafeEq(13u, e2), "");
+  static_assert(SafeEq(13, e3), "");
+  static_assert(SafeEq(13u, e3), "");
+  static_assert(SafeEq(13, e4), "");
+  static_assert(SafeEq(13u, e4), "");
+}
+
+}  // namespace rtc
diff --git a/base/safe_conversions.h b/base/safe_conversions.h
index ac0bb65..ff9cc44 100644
--- a/base/safe_conversions.h
+++ b/base/safe_conversions.h
@@ -13,9 +13,64 @@
 #ifndef WEBRTC_BASE_SAFE_CONVERSIONS_H_
 #define WEBRTC_BASE_SAFE_CONVERSIONS_H_
 
+#include <limits>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/safe_conversions.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/safe_conversions_impl.h"
+
+namespace rtc {
+
+// Convenience function that returns true if the supplied value is in range
+// for the destination type.
+template <typename Dst, typename Src>
+inline bool IsValueInRangeForNumericType(Src value) {
+  return internal::RangeCheck<Dst>(value) == internal::TYPE_VALID;
+}
+
+// checked_cast<> and dchecked_cast<> are analogous to static_cast<> for
+// numeric types, except that they [D]CHECK that the specified numeric
+// conversion will not overflow or underflow. NaN source will always trigger
+// the [D]CHECK.
+template <typename Dst, typename Src>
+inline Dst checked_cast(Src value) {
+  RTC_CHECK(IsValueInRangeForNumericType<Dst>(value));
+  return static_cast<Dst>(value);
+}
+template <typename Dst, typename Src>
+inline Dst dchecked_cast(Src value) {
+  RTC_DCHECK(IsValueInRangeForNumericType<Dst>(value));
+  return static_cast<Dst>(value);
+}
+
+// saturated_cast<> is analogous to static_cast<> for numeric types, except
+// that the specified numeric conversion will saturate rather than overflow or
+// underflow. NaN assignment to an integral will trigger a RTC_CHECK condition.
+template <typename Dst, typename Src>
+inline Dst saturated_cast(Src value) {
+  // Optimization for floating point values, which already saturate.
+  if (std::numeric_limits<Dst>::is_iec559)
+    return static_cast<Dst>(value);
+
+  switch (internal::RangeCheck<Dst>(value)) {
+    case internal::TYPE_VALID:
+      return static_cast<Dst>(value);
+
+    case internal::TYPE_UNDERFLOW:
+      return std::numeric_limits<Dst>::min();
+
+    case internal::TYPE_OVERFLOW:
+      return std::numeric_limits<Dst>::max();
+
+    // Should fail only on attempting to assign NaN to a saturated integer.
+    case internal::TYPE_INVALID:
+      FATAL();
+      return std::numeric_limits<Dst>::max();
+  }
+
+  FATAL();
+  return static_cast<Dst>(value);
+}
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SAFE_CONVERSIONS_H_
diff --git a/base/safe_conversions_impl.h b/base/safe_conversions_impl.h
index 497e156..52e52ef 100644
--- a/base/safe_conversions_impl.h
+++ b/base/safe_conversions_impl.h
@@ -13,9 +13,176 @@
 #ifndef WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_
 #define WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_
 
+#include <limits>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/safe_conversions_impl.h"
+namespace rtc {
+namespace internal {
+
+enum DstSign {
+  DST_UNSIGNED,
+  DST_SIGNED
+};
+
+enum SrcSign {
+  SRC_UNSIGNED,
+  SRC_SIGNED
+};
+
+enum DstRange {
+  OVERLAPS_RANGE,
+  CONTAINS_RANGE
+};
+
+// Helper templates to statically determine if our destination type can contain
+// all values represented by the source type.
+
+template <typename Dst, typename Src,
+          DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ?
+                                DST_SIGNED : DST_UNSIGNED,
+          SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ?
+                                SRC_SIGNED : SRC_UNSIGNED>
+struct StaticRangeCheck {};
+
+template <typename Dst, typename Src>
+struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_SIGNED> {
+  typedef std::numeric_limits<Dst> DstLimits;
+  typedef std::numeric_limits<Src> SrcLimits;
+  // Compare based on max_exponent, which we must compute for integrals.
+  static const size_t kDstMaxExponent = DstLimits::is_iec559 ?
+                                        DstLimits::max_exponent :
+                                        (sizeof(Dst) * 8 - 1);
+  static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ?
+                                        SrcLimits::max_exponent :
+                                        (sizeof(Src) * 8 - 1);
+  static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ?
+                                CONTAINS_RANGE : OVERLAPS_RANGE;
+};
+
+template <typename Dst, typename Src>
+struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED> {
+  static const DstRange value = sizeof(Dst) >= sizeof(Src) ?
+                                CONTAINS_RANGE : OVERLAPS_RANGE;
+};
+
+template <typename Dst, typename Src>
+struct StaticRangeCheck<Dst, Src, DST_SIGNED, SRC_UNSIGNED> {
+  typedef std::numeric_limits<Dst> DstLimits;
+  typedef std::numeric_limits<Src> SrcLimits;
+  // Compare based on max_exponent, which we must compute for integrals.
+  static const size_t kDstMaxExponent = DstLimits::is_iec559 ?
+                                        DstLimits::max_exponent :
+                                        (sizeof(Dst) * 8 - 1);
+  static const size_t kSrcMaxExponent = sizeof(Src) * 8;
+  static const DstRange value = kDstMaxExponent >= kSrcMaxExponent ?
+                                CONTAINS_RANGE : OVERLAPS_RANGE;
+};
+
+template <typename Dst, typename Src>
+struct StaticRangeCheck<Dst, Src, DST_UNSIGNED, SRC_SIGNED> {
+  static const DstRange value = OVERLAPS_RANGE;
+};
+
+
+enum RangeCheckResult {
+  TYPE_VALID = 0,      // Value can be represented by the destination type.
+  TYPE_UNDERFLOW = 1,  // Value would overflow.
+  TYPE_OVERFLOW = 2,   // Value would underflow.
+  TYPE_INVALID = 3     // Source value is invalid (i.e. NaN).
+};
+
+// This macro creates a RangeCheckResult from an upper and lower bound
+// check by taking advantage of the fact that only NaN can be out of range in
+// both directions at once.
+#define BASE_NUMERIC_RANGE_CHECK_RESULT(is_in_upper_bound, is_in_lower_bound) \
+    RangeCheckResult(((is_in_upper_bound) ? 0 : TYPE_OVERFLOW) | \
+                            ((is_in_lower_bound) ? 0 : TYPE_UNDERFLOW))
+
+template <typename Dst,
+          typename Src,
+          DstSign IsDstSigned = std::numeric_limits<Dst>::is_signed ?
+                                DST_SIGNED : DST_UNSIGNED,
+          SrcSign IsSrcSigned = std::numeric_limits<Src>::is_signed ?
+                                SRC_SIGNED : SRC_UNSIGNED,
+          DstRange IsSrcRangeContained = StaticRangeCheck<Dst, Src>::value>
+struct RangeCheckImpl {};
+
+// The following templates are for ranges that must be verified at runtime. We
+// split it into checks based on signedness to avoid confusing casts and
+// compiler warnings on signed an unsigned comparisons.
+
+// Dst range always contains the result: nothing to check.
+template <typename Dst, typename Src, DstSign IsDstSigned, SrcSign IsSrcSigned>
+struct RangeCheckImpl<Dst, Src, IsDstSigned, IsSrcSigned, CONTAINS_RANGE> {
+  static RangeCheckResult Check(Src value) {
+    return TYPE_VALID;
+  }
+};
+
+// Signed to signed narrowing.
+template <typename Dst, typename Src>
+struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
+  static RangeCheckResult Check(Src value) {
+    typedef std::numeric_limits<Dst> DstLimits;
+    return DstLimits::is_iec559 ?
+           BASE_NUMERIC_RANGE_CHECK_RESULT(
+               value <= static_cast<Src>(DstLimits::max()),
+               value >= static_cast<Src>(DstLimits::max() * -1)) :
+           BASE_NUMERIC_RANGE_CHECK_RESULT(
+               value <= static_cast<Src>(DstLimits::max()),
+               value >= static_cast<Src>(DstLimits::min()));
+  }
+};
+
+// Unsigned to unsigned narrowing.
+template <typename Dst, typename Src>
+struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
+  static RangeCheckResult Check(Src value) {
+    typedef std::numeric_limits<Dst> DstLimits;
+    return BASE_NUMERIC_RANGE_CHECK_RESULT(
+               value <= static_cast<Src>(DstLimits::max()), true);
+  }
+};
+
+// Unsigned to signed.
+template <typename Dst, typename Src>
+struct RangeCheckImpl<Dst, Src, DST_SIGNED, SRC_UNSIGNED, OVERLAPS_RANGE> {
+  static RangeCheckResult Check(Src value) {
+    typedef std::numeric_limits<Dst> DstLimits;
+    return sizeof(Dst) > sizeof(Src) ? TYPE_VALID :
+           BASE_NUMERIC_RANGE_CHECK_RESULT(
+               value <= static_cast<Src>(DstLimits::max()), true);
+  }
+};
+
+// Signed to unsigned.
+template <typename Dst, typename Src>
+struct RangeCheckImpl<Dst, Src, DST_UNSIGNED, SRC_SIGNED, OVERLAPS_RANGE> {
+  static RangeCheckResult Check(Src value) {
+    typedef std::numeric_limits<Dst> DstLimits;
+    typedef std::numeric_limits<Src> SrcLimits;
+    // Compare based on max_exponent, which we must compute for integrals.
+    static const size_t kDstMaxExponent = sizeof(Dst) * 8;
+    static const size_t kSrcMaxExponent = SrcLimits::is_iec559 ?
+                                          SrcLimits::max_exponent :
+                                          (sizeof(Src) * 8 - 1);
+    return (kDstMaxExponent >= kSrcMaxExponent) ?
+           BASE_NUMERIC_RANGE_CHECK_RESULT(true, value >= static_cast<Src>(0)) :
+           BASE_NUMERIC_RANGE_CHECK_RESULT(
+               value <= static_cast<Src>(DstLimits::max()),
+               value >= static_cast<Src>(0));
+  }
+};
+
+template <typename Dst, typename Src>
+inline RangeCheckResult RangeCheck(Src value) {
+  static_assert(std::numeric_limits<Src>::is_specialized,
+                "argument must be numeric");
+  static_assert(std::numeric_limits<Dst>::is_specialized,
+                "result must be numeric");
+  return RangeCheckImpl<Dst, Src>::Check(value);
+}
+
+}  // namespace internal
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SAFE_CONVERSIONS_IMPL_H_
diff --git a/base/safe_minmax.h b/base/safe_minmax.h
index 54d99b7..bf1cf43 100644
--- a/base/safe_minmax.h
+++ b/base/safe_minmax.h
@@ -8,11 +8,328 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+// Minimum and maximum
+// ===================
+//
+//   rtc::SafeMin(x, y)
+//   rtc::SafeMax(x, y)
+//
+// (These are both constexpr.)
+//
+// Accept two arguments of either any two integral or any two floating-point
+// types, and return the smaller and larger value, respectively, with no
+// truncation or wrap-around. If only one of the input types is statically
+// guaranteed to be able to represent the result, the return type is that type;
+// if either one would do, the result type is the smaller type. (One of these
+// two cases always applies.)
+//
+//   * The case with one floating-point and one integral type is not allowed,
+//     because the floating-point type will have greater range, but may not
+//     have sufficient precision to represent the integer value exactly.)
+//
+// Clamp (a.k.a. constrain to a given interval)
+// ============================================
+//
+//   rtc::SafeClamp(x, a, b)
+//
+// Accepts three arguments of any mix of integral types or any mix of
+// floating-point types, and returns the value in the closed interval [a, b]
+// that is closest to x (that is, if x < a it returns a; if x > b it returns b;
+// and if a <= x <= b it returns x). As for SafeMin() and SafeMax(), there is
+// no truncation or wrap-around. The result type
+//
+//   1. is statically guaranteed to be able to represent the result;
+//
+//   2. is no larger than the largest of the three argument types; and
+//
+//   3. has the same signedness as the type of the third argument, if this is
+//      possible without violating the First or Second Law.
+//
+// There is always at least one type that meets criteria 1 and 2. If more than
+// one type meets these criteria equally well, the result type is one of the
+// types that is smallest. Note that unlike SafeMin() and SafeMax(),
+// SafeClamp() will sometimes pick a return type that isn't the type of any of
+// its arguments.
+//
+//   * In this context, a type A is smaller than a type B if it has a smaller
+//     range; that is, if A::max() - A::min() < B::max() - B::min(). For
+//     example, int8_t < int16_t == uint16_t < int32_t, and all integral types
+//     are smaller than all floating-point types.)
+//
+//   * As for SafeMin and SafeMax, mixing integer and floating-point arguments
+//     is not allowed, because floating-point types have greater range than
+//     integer types, but do not have sufficient precision to represent the
+//     values of most integer types exactly.
+//
+// Requesting a specific return type
+// =================================
+//
+// All three functions allow callers to explicitly specify the return type as a
+// template parameter, overriding the default return type. E.g.
+//
+//   rtc::SafeMin<int>(x, y)  // returns an int
+//
+// If the requested type is statically guaranteed to be able to represent the
+// result, then everything's fine, and the return type is as requested. But if
+// the requested type is too small, a static_assert is triggered.
+
 #ifndef WEBRTC_BASE_SAFE_MINMAX_H_
 #define WEBRTC_BASE_SAFE_MINMAX_H_
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/safe_minmax.h"
+#include <limits>
+#include <type_traits>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/safe_compare.h"
+#include "webrtc/base/type_traits.h"
+
+namespace rtc {
+
+namespace safe_minmax_impl {
+
+// Make the range of a type available via something other than a constexpr
+// function, to work around MSVC limitations. See
+// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
+template <typename T>
+struct Limits {
+  static constexpr T lowest = std::numeric_limits<T>::lowest();
+  static constexpr T max = std::numeric_limits<T>::max();
+};
+
+template <typename T, bool is_enum = std::is_enum<T>::value>
+struct UnderlyingType;
+
+template <typename T>
+struct UnderlyingType<T, false> {
+  using type = T;
+};
+
+template <typename T>
+struct UnderlyingType<T, true> {
+  using type = typename std::underlying_type<T>::type;
+};
+
+// Given two types T1 and T2, find types that can hold the smallest (in
+// ::min_t) and the largest (in ::max_t) of the two values.
+template <typename T1,
+          typename T2,
+          bool int1 = IsIntlike<T1>::value,
+          bool int2 = IsIntlike<T2>::value>
+struct MType {
+  static_assert(int1 == int2,
+                "You may not mix integral and floating-point arguments");
+};
+
+// Specialization for when neither type is integral (and therefore presumably
+// floating-point).
+template <typename T1, typename T2>
+struct MType<T1, T2, false, false> {
+  using min_t = typename std::common_type<T1, T2>::type;
+  static_assert(std::is_same<min_t, T1>::value ||
+                    std::is_same<min_t, T2>::value,
+                "");
+
+  using max_t = typename std::common_type<T1, T2>::type;
+  static_assert(std::is_same<max_t, T1>::value ||
+                    std::is_same<max_t, T2>::value,
+                "");
+};
+
+// Specialization for when both types are integral.
+template <typename T1, typename T2>
+struct MType<T1, T2, true, true> {
+  // The type with the lowest minimum value. In case of a tie, the type with
+  // the lowest maximum value. In case that too is a tie, the types have the
+  // same range, and we arbitrarily pick T1.
+  using min_t = typename std::conditional<
+      SafeLt(Limits<T1>::lowest, Limits<T2>::lowest),
+      T1,
+      typename std::conditional<
+          SafeGt(Limits<T1>::lowest, Limits<T2>::lowest),
+          T2,
+          typename std::conditional<SafeLe(Limits<T1>::max, Limits<T2>::max),
+                                    T1,
+                                    T2>::type>::type>::type;
+  static_assert(std::is_same<min_t, T1>::value ||
+                    std::is_same<min_t, T2>::value,
+                "");
+
+  // The type with the highest maximum value. In case of a tie, the types have
+  // the same range (because in C++, integer types with the same maximum also
+  // have the same minimum).
+  static_assert(SafeNe(Limits<T1>::max, Limits<T2>::max) ||
+                    SafeEq(Limits<T1>::lowest, Limits<T2>::lowest),
+                "integer types with the same max should have the same min");
+  using max_t = typename std::
+      conditional<SafeGe(Limits<T1>::max, Limits<T2>::max), T1, T2>::type;
+  static_assert(std::is_same<max_t, T1>::value ||
+                    std::is_same<max_t, T2>::value,
+                "");
+};
+
+// A dummy type that we pass around at compile time but never actually use.
+// Declared but not defined.
+struct DefaultType;
+
+// ::type is A, except we fall back to B if A is DefaultType. We static_assert
+// that the chosen type can hold all values that B can hold.
+template <typename A, typename B>
+struct TypeOr {
+  using type = typename std::
+      conditional<std::is_same<A, DefaultType>::value, B, A>::type;
+  static_assert(SafeLe(Limits<type>::lowest, Limits<B>::lowest) &&
+                    SafeGe(Limits<type>::max, Limits<B>::max),
+                "The specified type isn't large enough");
+  static_assert(IsIntlike<type>::value == IsIntlike<B>::value &&
+                    std::is_floating_point<type>::value ==
+                        std::is_floating_point<type>::value,
+                "float<->int conversions not allowed");
+};
+
+}  // namespace safe_minmax_impl
+
+template <
+    typename R = safe_minmax_impl::DefaultType,
+    typename T1 = safe_minmax_impl::DefaultType,
+    typename T2 = safe_minmax_impl::DefaultType,
+    typename R2 = typename safe_minmax_impl::TypeOr<
+        R,
+        typename safe_minmax_impl::MType<
+            typename safe_minmax_impl::UnderlyingType<T1>::type,
+            typename safe_minmax_impl::UnderlyingType<T2>::type>::min_t>::type>
+constexpr R2 SafeMin(T1 a, T2 b) {
+  static_assert(IsIntlike<T1>::value || std::is_floating_point<T1>::value,
+                "The first argument must be integral or floating-point");
+  static_assert(IsIntlike<T2>::value || std::is_floating_point<T2>::value,
+                "The second argument must be integral or floating-point");
+  return SafeLt(a, b) ? static_cast<R2>(a) : static_cast<R2>(b);
+}
+
+template <
+    typename R = safe_minmax_impl::DefaultType,
+    typename T1 = safe_minmax_impl::DefaultType,
+    typename T2 = safe_minmax_impl::DefaultType,
+    typename R2 = typename safe_minmax_impl::TypeOr<
+        R,
+        typename safe_minmax_impl::MType<
+            typename safe_minmax_impl::UnderlyingType<T1>::type,
+            typename safe_minmax_impl::UnderlyingType<T2>::type>::max_t>::type>
+constexpr R2 SafeMax(T1 a, T2 b) {
+  static_assert(IsIntlike<T1>::value || std::is_floating_point<T1>::value,
+                "The first argument must be integral or floating-point");
+  static_assert(IsIntlike<T2>::value || std::is_floating_point<T2>::value,
+                "The second argument must be integral or floating-point");
+  return SafeGt(a, b) ? static_cast<R2>(a) : static_cast<R2>(b);
+}
+
+namespace safe_minmax_impl {
+
+// Given three types T, L, and H, let ::type be a suitable return value for
+// SafeClamp(T, L, H). See the docs at the top of this file for details.
+template <typename T,
+          typename L,
+          typename H,
+          bool int1 = IsIntlike<T>::value,
+          bool int2 = IsIntlike<L>::value,
+          bool int3 = IsIntlike<H>::value>
+struct ClampType {
+  static_assert(int1 == int2 && int1 == int3,
+                "You may not mix integral and floating-point arguments");
+};
+
+// Specialization for when all three types are floating-point.
+template <typename T, typename L, typename H>
+struct ClampType<T, L, H, false, false, false> {
+  using type = typename std::common_type<T, L, H>::type;
+};
+
+// Specialization for when all three types are integral.
+template <typename T, typename L, typename H>
+struct ClampType<T, L, H, true, true, true> {
+ private:
+  // Range of the return value. The return type must be able to represent this
+  // full range.
+  static constexpr auto r_min =
+      SafeMax(Limits<L>::lowest, SafeMin(Limits<H>::lowest, Limits<T>::lowest));
+  static constexpr auto r_max =
+      SafeMin(Limits<H>::max, SafeMax(Limits<L>::max, Limits<T>::max));
+
+  // Is the given type an acceptable return type? (That is, can it represent
+  // all possible return values, and is it no larger than the largest of the
+  // input types?)
+  template <typename A>
+  struct AcceptableType {
+   private:
+    static constexpr bool not_too_large = sizeof(A) <= sizeof(L) ||
+                                          sizeof(A) <= sizeof(H) ||
+                                          sizeof(A) <= sizeof(T);
+    static constexpr bool range_contained =
+        SafeLe(Limits<A>::lowest, r_min) && SafeLe(r_max, Limits<A>::max);
+
+   public:
+    static constexpr bool value = not_too_large && range_contained;
+  };
+
+  using best_signed_type = typename std::conditional<
+      AcceptableType<int8_t>::value,
+      int8_t,
+      typename std::conditional<
+          AcceptableType<int16_t>::value,
+          int16_t,
+          typename std::conditional<AcceptableType<int32_t>::value,
+                                    int32_t,
+                                    int64_t>::type>::type>::type;
+
+  using best_unsigned_type = typename std::conditional<
+      AcceptableType<uint8_t>::value,
+      uint8_t,
+      typename std::conditional<
+          AcceptableType<uint16_t>::value,
+          uint16_t,
+          typename std::conditional<AcceptableType<uint32_t>::value,
+                                    uint32_t,
+                                    uint64_t>::type>::type>::type;
+
+ public:
+  // Pick the best type, preferring the same signedness as T but falling back
+  // to the other one if necessary.
+  using type = typename std::conditional<
+      std::is_signed<T>::value,
+      typename std::conditional<AcceptableType<best_signed_type>::value,
+                                best_signed_type,
+                                best_unsigned_type>::type,
+      typename std::conditional<AcceptableType<best_unsigned_type>::value,
+                                best_unsigned_type,
+                                best_signed_type>::type>::type;
+  static_assert(AcceptableType<type>::value, "");
+};
+
+}  // namespace safe_minmax_impl
+
+template <
+    typename R = safe_minmax_impl::DefaultType,
+    typename T = safe_minmax_impl::DefaultType,
+    typename L = safe_minmax_impl::DefaultType,
+    typename H = safe_minmax_impl::DefaultType,
+    typename R2 = typename safe_minmax_impl::TypeOr<
+        R,
+        typename safe_minmax_impl::ClampType<
+            typename safe_minmax_impl::UnderlyingType<T>::type,
+            typename safe_minmax_impl::UnderlyingType<L>::type,
+            typename safe_minmax_impl::UnderlyingType<H>::type>::type>::type>
+R2 SafeClamp(T x, L min, H max) {
+  static_assert(IsIntlike<H>::value || std::is_floating_point<H>::value,
+                "The first argument must be integral or floating-point");
+  static_assert(IsIntlike<T>::value || std::is_floating_point<T>::value,
+                "The second argument must be integral or floating-point");
+  static_assert(IsIntlike<L>::value || std::is_floating_point<L>::value,
+                "The third argument must be integral or floating-point");
+  RTC_DCHECK_LE(min, max);
+  return SafeLe(x, min)
+             ? static_cast<R2>(min)
+             : SafeGe(x, max) ? static_cast<R2>(max) : static_cast<R2>(x);
+}
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SAFE_MINMAX_H_
diff --git a/base/safe_minmax_unittest.cc b/base/safe_minmax_unittest.cc
new file mode 100644
index 0000000..eb0fe80
--- /dev/null
+++ b/base/safe_minmax_unittest.cc
@@ -0,0 +1,344 @@
+/*
+ *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+#include <limits>
+
+#include "webrtc/base/safe_minmax.h"
+#include "webrtc/test/gtest.h"
+
+namespace rtc {
+
+namespace {
+
+// Functions that check that SafeMin(), SafeMax(), and SafeClamp() return the
+// specified type. The functions that end in "R" use an explicitly given return
+// type.
+
+template <typename T1, typename T2, typename Tmin, typename Tmax>
+constexpr bool TypeCheckMinMax() {
+  return std::is_same<decltype(SafeMin(std::declval<T1>(), std::declval<T2>())),
+                      Tmin>::value &&
+         std::is_same<decltype(SafeMax(std::declval<T1>(), std::declval<T2>())),
+                      Tmax>::value;
+}
+
+template <typename T1, typename T2, typename R>
+constexpr bool TypeCheckMinR() {
+  return std::is_same<
+      decltype(SafeMin<R>(std::declval<T1>(), std::declval<T2>())), R>::value;
+}
+
+template <typename T1, typename T2, typename R>
+constexpr bool TypeCheckMaxR() {
+  return std::is_same<
+      decltype(SafeMax<R>(std::declval<T1>(), std::declval<T2>())), R>::value;
+}
+
+template <typename T, typename L, typename H, typename R>
+constexpr bool TypeCheckClamp() {
+  return std::is_same<decltype(SafeClamp(std::declval<T>(), std::declval<L>(),
+                                         std::declval<H>())),
+                      R>::value;
+}
+
+template <typename T, typename L, typename H, typename R>
+constexpr bool TypeCheckClampR() {
+  return std::is_same<decltype(SafeClamp<R>(std::declval<T>(),
+                                            std::declval<L>(),
+                                            std::declval<H>())),
+                      R>::value;
+}
+
+// clang-format off
+
+// SafeMin/SafeMax: Check that all combinations of signed/unsigned 8/64 bits
+// give the correct default result type.
+static_assert(TypeCheckMinMax<  int8_t,   int8_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckMinMax<  int8_t,  uint8_t,   int8_t,  uint8_t>(), "");
+static_assert(TypeCheckMinMax<  int8_t,  int64_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckMinMax<  int8_t, uint64_t,   int8_t, uint64_t>(), "");
+static_assert(TypeCheckMinMax< uint8_t,   int8_t,   int8_t,  uint8_t>(), "");
+static_assert(TypeCheckMinMax< uint8_t,  uint8_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckMinMax< uint8_t,  int64_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckMinMax< uint8_t, uint64_t,  uint8_t, uint64_t>(), "");
+static_assert(TypeCheckMinMax< int64_t,   int8_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckMinMax< int64_t,  uint8_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckMinMax< int64_t,  int64_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckMinMax< int64_t, uint64_t,  int64_t, uint64_t>(), "");
+static_assert(TypeCheckMinMax<uint64_t,   int8_t,   int8_t, uint64_t>(), "");
+static_assert(TypeCheckMinMax<uint64_t,  uint8_t,  uint8_t, uint64_t>(), "");
+static_assert(TypeCheckMinMax<uint64_t,  int64_t,  int64_t, uint64_t>(), "");
+static_assert(TypeCheckMinMax<uint64_t, uint64_t, uint64_t, uint64_t>(), "");
+
+// SafeClamp: Check that all combinations of signed/unsigned 8/64 bits give the
+// correct result type.
+static_assert(TypeCheckClamp<  int8_t,   int8_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,   int8_t,  uint8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,   int8_t,  int64_t,   int8_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,   int8_t, uint64_t,   int8_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,  uint8_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,  uint8_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,  uint8_t,  int64_t,  int16_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,  uint8_t, uint64_t,  int16_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,  int64_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,  int64_t,  uint8_t,  int16_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,  int64_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp<  int8_t,  int64_t, uint64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp<  int8_t, uint64_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp<  int8_t, uint64_t,  uint8_t,  int16_t>(), "");
+static_assert(TypeCheckClamp<  int8_t, uint64_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp<  int8_t, uint64_t, uint64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,   int8_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,   int8_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,   int8_t,  int64_t,  int16_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,   int8_t, uint64_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,  uint8_t,   int8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,  uint8_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,  uint8_t,  int64_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,  uint8_t, uint64_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,  int64_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,  int64_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,  int64_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< uint8_t,  int64_t, uint64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp< uint8_t, uint64_t,   int8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t, uint64_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp< uint8_t, uint64_t,  int64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp< uint8_t, uint64_t, uint64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp< int64_t,   int8_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp< int64_t,   int8_t,  uint8_t,  int16_t>(), "");
+static_assert(TypeCheckClamp< int64_t,   int8_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< int64_t,   int8_t, uint64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< int64_t,  uint8_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp< int64_t,  uint8_t,  uint8_t,  int16_t>(), "");
+static_assert(TypeCheckClamp< int64_t,  uint8_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< int64_t,  uint8_t, uint64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< int64_t,  int64_t,   int8_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< int64_t,  int64_t,  uint8_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< int64_t,  int64_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< int64_t,  int64_t, uint64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< int64_t, uint64_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp< int64_t, uint64_t,  uint8_t,  int16_t>(), "");
+static_assert(TypeCheckClamp< int64_t, uint64_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp< int64_t, uint64_t, uint64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,   int8_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,   int8_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,   int8_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,   int8_t, uint64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,  uint8_t,   int8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,  uint8_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,  uint8_t,  int64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,  uint8_t, uint64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,  int64_t,   int8_t,   int8_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,  int64_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,  int64_t,  int64_t,  int64_t>(), "");
+static_assert(TypeCheckClamp<uint64_t,  int64_t, uint64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp<uint64_t, uint64_t,   int8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp<uint64_t, uint64_t,  uint8_t,  uint8_t>(), "");
+static_assert(TypeCheckClamp<uint64_t, uint64_t,  int64_t, uint64_t>(), "");
+static_assert(TypeCheckClamp<uint64_t, uint64_t, uint64_t, uint64_t>(), "");
+
+enum DefaultE { kFoo = -17 };
+enum UInt8E : uint8_t { kBar = 17 };
+
+// SafeMin/SafeMax: Check that we can use enum types.
+static_assert(TypeCheckMinMax<unsigned, unsigned, unsigned, unsigned>(), "");
+static_assert(TypeCheckMinMax<unsigned, DefaultE,      int, unsigned>(), "");
+static_assert(TypeCheckMinMax<unsigned,   UInt8E,  uint8_t, unsigned>(), "");
+static_assert(TypeCheckMinMax<DefaultE, unsigned,      int, unsigned>(), "");
+static_assert(TypeCheckMinMax<DefaultE, DefaultE,      int,      int>(), "");
+static_assert(TypeCheckMinMax<DefaultE,   UInt8E,      int,      int>(), "");
+static_assert(TypeCheckMinMax<  UInt8E, unsigned,  uint8_t, unsigned>(), "");
+static_assert(TypeCheckMinMax<  UInt8E, DefaultE,     int,       int>(), "");
+static_assert(TypeCheckMinMax<  UInt8E,   UInt8E,  uint8_t,  uint8_t>(), "");
+
+// SafeClamp: Check that we can use enum types.
+static_assert(TypeCheckClamp<unsigned, unsigned, unsigned, unsigned>(), "");
+static_assert(TypeCheckClamp<unsigned, unsigned, DefaultE, unsigned>(), "");
+static_assert(TypeCheckClamp<unsigned, unsigned,   UInt8E,  uint8_t>(), "");
+static_assert(TypeCheckClamp<unsigned, DefaultE, unsigned, unsigned>(), "");
+static_assert(TypeCheckClamp<unsigned, DefaultE, DefaultE,      int>(), "");
+static_assert(TypeCheckClamp<unsigned, DefaultE,   UInt8E,  uint8_t>(), "");
+static_assert(TypeCheckClamp<unsigned,   UInt8E, unsigned, unsigned>(), "");
+static_assert(TypeCheckClamp<unsigned,   UInt8E, DefaultE, unsigned>(), "");
+static_assert(TypeCheckClamp<unsigned,   UInt8E,   UInt8E,  uint8_t>(), "");
+static_assert(TypeCheckClamp<DefaultE, unsigned, unsigned, unsigned>(), "");
+static_assert(TypeCheckClamp<DefaultE, unsigned, DefaultE,      int>(), "");
+static_assert(TypeCheckClamp<DefaultE, unsigned,   UInt8E,  int16_t>(), "");
+static_assert(TypeCheckClamp<DefaultE, DefaultE, unsigned,      int>(), "");
+static_assert(TypeCheckClamp<DefaultE, DefaultE, DefaultE,      int>(), "");
+static_assert(TypeCheckClamp<DefaultE, DefaultE,   UInt8E,      int>(), "");
+static_assert(TypeCheckClamp<DefaultE,   UInt8E, unsigned,      int>(), "");
+static_assert(TypeCheckClamp<DefaultE,   UInt8E, DefaultE,      int>(), "");
+static_assert(TypeCheckClamp<DefaultE,   UInt8E,   UInt8E,  int16_t>(), "");
+static_assert(TypeCheckClamp<  UInt8E, unsigned, unsigned, unsigned>(), "");
+static_assert(TypeCheckClamp<  UInt8E, unsigned, DefaultE, unsigned>(), "");
+static_assert(TypeCheckClamp<  UInt8E, unsigned,   UInt8E,  uint8_t>(), "");
+static_assert(TypeCheckClamp<  UInt8E, DefaultE, unsigned, unsigned>(), "");
+static_assert(TypeCheckClamp<  UInt8E, DefaultE, DefaultE,      int>(), "");
+static_assert(TypeCheckClamp<  UInt8E, DefaultE,   UInt8E,  uint8_t>(), "");
+static_assert(TypeCheckClamp<  UInt8E,   UInt8E, unsigned,  uint8_t>(), "");
+static_assert(TypeCheckClamp<  UInt8E,   UInt8E, DefaultE,  uint8_t>(), "");
+static_assert(TypeCheckClamp<  UInt8E,   UInt8E,   UInt8E,  uint8_t>(), "");
+
+using ld = long double;
+
+// SafeMin/SafeMax: Check that all floating-point combinations give the
+// correct result type.
+static_assert(TypeCheckMinMax< float,  float,  float,  float>(), "");
+static_assert(TypeCheckMinMax< float, double, double, double>(), "");
+static_assert(TypeCheckMinMax< float,     ld,     ld,     ld>(), "");
+static_assert(TypeCheckMinMax<double,  float, double, double>(), "");
+static_assert(TypeCheckMinMax<double, double, double, double>(), "");
+static_assert(TypeCheckMinMax<double,     ld,     ld,     ld>(), "");
+static_assert(TypeCheckMinMax<    ld,  float,     ld,     ld>(), "");
+static_assert(TypeCheckMinMax<    ld, double,     ld,     ld>(), "");
+static_assert(TypeCheckMinMax<    ld,     ld,     ld,     ld>(), "");
+
+// SafeClamp: Check that all floating-point combinations give the correct
+// result type.
+static_assert(TypeCheckClamp< float,  float,  float,  float>(), "");
+static_assert(TypeCheckClamp< float,  float, double, double>(), "");
+static_assert(TypeCheckClamp< float,  float,     ld,     ld>(), "");
+static_assert(TypeCheckClamp< float, double,  float, double>(), "");
+static_assert(TypeCheckClamp< float, double, double, double>(), "");
+static_assert(TypeCheckClamp< float, double,     ld,     ld>(), "");
+static_assert(TypeCheckClamp< float,     ld,  float,     ld>(), "");
+static_assert(TypeCheckClamp< float,     ld, double,     ld>(), "");
+static_assert(TypeCheckClamp< float,     ld,     ld,     ld>(), "");
+static_assert(TypeCheckClamp<double,  float,  float, double>(), "");
+static_assert(TypeCheckClamp<double,  float, double, double>(), "");
+static_assert(TypeCheckClamp<double,  float,     ld,     ld>(), "");
+static_assert(TypeCheckClamp<double, double,  float, double>(), "");
+static_assert(TypeCheckClamp<double, double, double, double>(), "");
+static_assert(TypeCheckClamp<double, double,     ld,     ld>(), "");
+static_assert(TypeCheckClamp<double,     ld,  float,     ld>(), "");
+static_assert(TypeCheckClamp<double,     ld, double,     ld>(), "");
+static_assert(TypeCheckClamp<double,     ld,     ld,     ld>(), "");
+static_assert(TypeCheckClamp<    ld,  float,  float,     ld>(), "");
+static_assert(TypeCheckClamp<    ld,  float, double,     ld>(), "");
+static_assert(TypeCheckClamp<    ld,  float,     ld,     ld>(), "");
+static_assert(TypeCheckClamp<    ld, double,  float,     ld>(), "");
+static_assert(TypeCheckClamp<    ld, double, double,     ld>(), "");
+static_assert(TypeCheckClamp<    ld, double,     ld,     ld>(), "");
+static_assert(TypeCheckClamp<    ld,     ld,  float,     ld>(), "");
+static_assert(TypeCheckClamp<    ld,     ld, double,     ld>(), "");
+static_assert(TypeCheckClamp<    ld,     ld,     ld,     ld>(), "");
+
+// clang-format on
+
+// SafeMin/SafeMax: Check some cases of explicitly specified return type. The
+// commented-out lines give compilation errors due to the requested return type
+// being too small or requiring an int<->float conversion.
+static_assert(TypeCheckMinR<int8_t, int8_t, int16_t>(), "");
+// static_assert(TypeCheckMinR<int8_t, int8_t, float>(), "");
+static_assert(TypeCheckMinR<uint32_t, uint64_t, uint32_t>(), "");
+// static_assert(TypeCheckMaxR<uint64_t, float, float>(), "");
+// static_assert(TypeCheckMaxR<uint64_t, double, float>(), "");
+static_assert(TypeCheckMaxR<uint32_t, int32_t, uint32_t>(), "");
+// static_assert(TypeCheckMaxR<uint32_t, int32_t, int32_t>(), "");
+
+// SafeClamp: Check some cases of explicitly specified return type. The
+// commented-out lines give compilation errors due to the requested return type
+// being too small.
+static_assert(TypeCheckClampR<int16_t, int8_t, uint8_t, int16_t>(), "");
+static_assert(TypeCheckClampR<int16_t, int8_t, uint8_t, int32_t>(), "");
+// static_assert(TypeCheckClampR<int16_t, int8_t, uint8_t, uint32_t>(), "");
+
+template <typename T1, typename T2, typename Tmin, typename Tmax>
+constexpr bool CheckMinMax(T1 a, T2 b, Tmin min, Tmax max) {
+  return TypeCheckMinMax<T1, T2, Tmin, Tmax>() && SafeMin(a, b) == min &&
+         SafeMax(a, b) == max;
+}
+
+template <typename T, typename L, typename H, typename R>
+bool CheckClamp(T x, L min, H max, R clamped) {
+  return TypeCheckClamp<T, L, H, R>() && SafeClamp(x, min, max) == clamped;
+}
+
+// SafeMin/SafeMax: Check a few values.
+static_assert(CheckMinMax(int8_t{1}, int8_t{-1}, int8_t{-1}, int8_t{1}), "");
+static_assert(CheckMinMax(uint8_t{1}, int8_t{-1}, int8_t{-1}, uint8_t{1}), "");
+static_assert(CheckMinMax(uint8_t{5}, uint64_t{2}, uint8_t{2}, uint64_t{5}),
+              "");
+static_assert(CheckMinMax(std::numeric_limits<int32_t>::min(),
+                          std::numeric_limits<uint32_t>::max(),
+                          std::numeric_limits<int32_t>::min(),
+                          std::numeric_limits<uint32_t>::max()),
+              "");
+static_assert(CheckMinMax(std::numeric_limits<int32_t>::min(),
+                          std::numeric_limits<uint16_t>::max(),
+                          std::numeric_limits<int32_t>::min(),
+                          int32_t{std::numeric_limits<uint16_t>::max()}),
+              "");
+// static_assert(CheckMinMax(1.f, 2, 1.f, 2.f), "");
+static_assert(CheckMinMax(1.f, 0.0, 0.0, 1.0), "");
+
+// SafeClamp: Check a few values.
+TEST(SafeMinmaxTest, Clamp) {
+  EXPECT_TRUE(CheckClamp(int32_t{-1000000}, std::numeric_limits<int16_t>::min(),
+                         std::numeric_limits<int16_t>::max(),
+                         std::numeric_limits<int16_t>::min()));
+  EXPECT_TRUE(CheckClamp(uint32_t{1000000}, std::numeric_limits<int16_t>::min(),
+                         std::numeric_limits<int16_t>::max(),
+                         std::numeric_limits<int16_t>::max()));
+  EXPECT_TRUE(CheckClamp(3.f, -1.0, 1.f, 1.0));
+  EXPECT_TRUE(CheckClamp(3.0, -1.f, 1.f, 1.0));
+}
+
+}  // namespace
+
+// These functions aren't used in the tests, but it's useful to look at the
+// compiler output for them, and verify that (1) the same-signedness Test*Safe
+// functions result in exactly the same code as their Test*Ref counterparts,
+// and that (2) the mixed-signedness Test*Safe functions have just a few extra
+// arithmetic and logic instructions (but no extra control flow instructions).
+
+// clang-format off
+int32_t  TestMinRef(  int32_t a,  int32_t b) { return std::min(a, b); }
+uint32_t TestMinRef( uint32_t a, uint32_t b) { return std::min(a, b); }
+int32_t  TestMinSafe( int32_t a,  int32_t b) { return SafeMin(a, b); }
+int32_t  TestMinSafe( int32_t a, uint32_t b) { return SafeMin(a, b); }
+int32_t  TestMinSafe(uint32_t a,  int32_t b) { return SafeMin(a, b); }
+uint32_t TestMinSafe(uint32_t a, uint32_t b) { return SafeMin(a, b); }
+// clang-format on
+
+int32_t TestClampRef(int32_t x, int32_t a, int32_t b) {
+  return std::max(a, std::min(x, b));
+}
+uint32_t TestClampRef(uint32_t x, uint32_t a, uint32_t b) {
+  return std::max(a, std::min(x, b));
+}
+int32_t TestClampSafe(int32_t x, int32_t a, int32_t b) {
+  return SafeClamp(x, a, b);
+}
+int32_t TestClampSafe(int32_t x, int32_t a, uint32_t b) {
+  return SafeClamp(x, a, b);
+}
+int32_t TestClampSafe(int32_t x, uint32_t a, int32_t b) {
+  return SafeClamp(x, a, b);
+}
+uint32_t TestClampSafe(int32_t x, uint32_t a, uint32_t b) {
+  return SafeClamp(x, a, b);
+}
+int32_t TestClampSafe(uint32_t x, int32_t a, int32_t b) {
+  return SafeClamp(x, a, b);
+}
+uint32_t TestClampSafe(uint32_t x, int32_t a, uint32_t b) {
+  return SafeClamp(x, a, b);
+}
+int32_t TestClampSafe(uint32_t x, uint32_t a, int32_t b) {
+  return SafeClamp(x, a, b);
+}
+uint32_t TestClampSafe(uint32_t x, uint32_t a, uint32_t b) {
+  return SafeClamp(x, a, b);
+}
+
+}  // namespace rtc
diff --git a/base/sanitizer.h b/base/sanitizer.h
index 56a5e10..e27a692 100644
--- a/base/sanitizer.h
+++ b/base/sanitizer.h
@@ -11,9 +11,106 @@
 #ifndef WEBRTC_BASE_SANITIZER_H_
 #define WEBRTC_BASE_SANITIZER_H_
 
+#if defined(__has_feature)
+#if __has_feature(address_sanitizer)
+#define RTC_HAS_ASAN 1
+#endif
+#if __has_feature(memory_sanitizer)
+#define RTC_HAS_MSAN 1
+#endif
+#endif
+#ifndef RTC_HAS_ASAN
+#define RTC_HAS_ASAN 0
+#endif
+#ifndef RTC_HAS_MSAN
+#define RTC_HAS_MSAN 0
+#endif
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sanitizer.h"
+#if RTC_HAS_ASAN
+#include <sanitizer/asan_interface.h>
+#endif
+#if RTC_HAS_MSAN
+#include <sanitizer/msan_interface.h>
+#endif
+
+#ifdef __has_attribute
+#if __has_attribute(no_sanitize)
+#define RTC_NO_SANITIZE(what) __attribute__((no_sanitize(what)))
+#endif
+#endif
+#ifndef RTC_NO_SANITIZE
+#define RTC_NO_SANITIZE(what)
+#endif
+
+// Ask ASan to mark the memory range [ptr, ptr + element_size * num_elements)
+// as being unaddressable, so that reads and writes are not allowed. ASan may
+// narrow the range to the nearest alignment boundaries.
+static inline void rtc_AsanPoison(const volatile void* ptr,
+                                  size_t element_size,
+                                  size_t num_elements) {
+#if RTC_HAS_ASAN
+  ASAN_POISON_MEMORY_REGION(ptr, element_size * num_elements);
+#endif
+}
+
+// Ask ASan to mark the memory range [ptr, ptr + element_size * num_elements)
+// as being addressable, so that reads and writes are allowed. ASan may widen
+// the range to the nearest alignment boundaries.
+static inline void rtc_AsanUnpoison(const volatile void* ptr,
+                                    size_t element_size,
+                                    size_t num_elements) {
+#if RTC_HAS_ASAN
+  ASAN_UNPOISON_MEMORY_REGION(ptr, element_size * num_elements);
+#endif
+}
+
+// Ask MSan to mark the memory range [ptr, ptr + element_size * num_elements)
+// as being uninitialized.
+static inline void rtc_MsanMarkUninitialized(const volatile void* ptr,
+                                             size_t element_size,
+                                             size_t num_elements) {
+#if RTC_HAS_MSAN
+  __msan_poison(ptr, element_size * num_elements);
+#endif
+}
+
+// Force an MSan check (if any bits in the memory range [ptr, ptr +
+// element_size * num_elements) are uninitialized the call will crash with an
+// MSan report).
+static inline void rtc_MsanCheckInitialized(const volatile void* ptr,
+                                            size_t element_size,
+                                            size_t num_elements) {
+#if RTC_HAS_MSAN
+  __msan_check_mem_is_initialized(ptr, element_size * num_elements);
+#endif
+}
+
+#ifdef __cplusplus
+
+namespace rtc {
+
+template <typename T>
+inline void AsanPoison(const T& mem) {
+  rtc_AsanPoison(mem.data(), sizeof(mem.data()[0]), mem.size());
+}
+
+template <typename T>
+inline void AsanUnpoison(const T& mem) {
+  rtc_AsanUnpoison(mem.data(), sizeof(mem.data()[0]), mem.size());
+}
+
+template <typename T>
+inline void MsanMarkUninitialized(const T& mem) {
+  rtc_MsanMarkUninitialized(mem.data(), sizeof(mem.data()[0]), mem.size());
+}
+
+template <typename T>
+inline void MsanCheckInitialized(const T& mem) {
+  rtc_MsanCheckInitialized(mem.data(), sizeof(mem.data()[0]), mem.size());
+}
+
+}  // namespace rtc
+
+#endif  // __cplusplus
 
 #endif  // WEBRTC_BASE_SANITIZER_H_
diff --git a/base/scoped_ref_ptr.h b/base/scoped_ref_ptr.h
index 2599562..26aff03 100644
--- a/base/scoped_ref_ptr.h
+++ b/base/scoped_ref_ptr.h
@@ -63,9 +63,101 @@
 #ifndef WEBRTC_BASE_SCOPED_REF_PTR_H_
 #define WEBRTC_BASE_SCOPED_REF_PTR_H_
 
+#include <memory>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/scoped_ref_ptr.h"
+namespace rtc {
+
+template <class T>
+class scoped_refptr {
+ public:
+  scoped_refptr() : ptr_(nullptr) {}
+
+  scoped_refptr(T* p) : ptr_(p) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  template <typename U>
+  scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {
+    if (ptr_)
+      ptr_->AddRef();
+  }
+
+  // Move constructors.
+  scoped_refptr(scoped_refptr<T>&& r) : ptr_(r.release()) {}
+
+  template <typename U>
+  scoped_refptr(scoped_refptr<U>&& r) : ptr_(r.release()) {}
+
+  ~scoped_refptr() {
+    if (ptr_)
+      ptr_->Release();
+  }
+
+  T* get() const { return ptr_; }
+  operator T*() const { return ptr_; }
+  T* operator->() const { return ptr_; }
+
+  // Release a pointer.
+  // The return value is the current pointer held by this object.
+  // If this object holds a null pointer, the return value is null.
+  // After this operation, this object will hold a null pointer,
+  // and will not own the object any more.
+  T* release() {
+    T* retVal = ptr_;
+    ptr_ = nullptr;
+    return retVal;
+  }
+
+  scoped_refptr<T>& operator=(T* p) {
+    // AddRef first so that self assignment should work
+    if (p)
+      p->AddRef();
+    if (ptr_ )
+      ptr_ ->Release();
+    ptr_ = p;
+    return *this;
+  }
+
+  scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
+    return *this = r.ptr_;
+  }
+
+  template <typename U>
+  scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
+    return *this = r.get();
+  }
+
+  scoped_refptr<T>& operator=(scoped_refptr<T>&& r) {
+    scoped_refptr<T>(std::move(r)).swap(*this);
+    return *this;
+  }
+
+  template <typename U>
+  scoped_refptr<T>& operator=(scoped_refptr<U>&& r) {
+    scoped_refptr<T>(std::move(r)).swap(*this);
+    return *this;
+  }
+
+  void swap(T** pp) {
+    T* p = ptr_;
+    ptr_ = *pp;
+    *pp = p;
+  }
+
+  void swap(scoped_refptr<T>& r) {
+    swap(&r.ptr_);
+  }
+
+ protected:
+  T* ptr_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SCOPED_REF_PTR_H_
diff --git a/base/sequenced_task_checker.h b/base/sequenced_task_checker.h
index e586b8d..4df5b54 100644
--- a/base/sequenced_task_checker.h
+++ b/base/sequenced_task_checker.h
@@ -11,9 +11,68 @@
 #ifndef WEBRTC_BASE_SEQUENCED_TASK_CHECKER_H_
 #define WEBRTC_BASE_SEQUENCED_TASK_CHECKER_H_
 
+// Apart from debug builds, we also enable the sequence checker in
+// builds with RTC_DCHECK_IS_ON so that trybots and waterfall bots
+// with this define will get the same level of checking as debug bots.
+#define ENABLE_SEQUENCED_TASK_CHECKER RTC_DCHECK_IS_ON
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sequenced_task_checker.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/thread_annotations.h"
+#include "webrtc/base/sequenced_task_checker_impl.h"
 
+namespace rtc {
+
+// Do nothing implementation, for use in release mode.
+//
+// Note: You should almost always use the SequencedTaskChecker class to get the
+// right version for your build configuration.
+class SequencedTaskCheckerDoNothing {
+ public:
+  bool CalledSequentially() const { return true; }
+
+  void Detach() {}
+};
+
+// SequencedTaskChecker is a helper class used to help verify that some methods
+// of a class are called on the same task queue or thread. A
+// SequencedTaskChecker is bound to a a task queue if the object is
+// created on a task queue, or a thread otherwise.
+//
+//
+// Example:
+// class MyClass {
+//  public:
+//   void Foo() {
+//     RTC_DCHECK(sequence_checker_.CalledSequentially());
+//     ... (do stuff) ...
+//   }
+//
+//  private:
+//   SequencedTaskChecker sequence_checker_;
+// }
+//
+// In Release mode, CalledOnValidThread will always return true.
+#if ENABLE_SEQUENCED_TASK_CHECKER
+class LOCKABLE SequencedTaskChecker : public SequencedTaskCheckerImpl {};
+#else
+class LOCKABLE SequencedTaskChecker : public SequencedTaskCheckerDoNothing {};
+#endif  // ENABLE_SEQUENCED_TASK_CHECKER_H_
+
+namespace internal {
+class SCOPED_LOCKABLE SequencedTaskCheckerScope {
+ public:
+  explicit SequencedTaskCheckerScope(const SequencedTaskChecker* checker)
+      EXCLUSIVE_LOCK_FUNCTION(checker);
+  ~SequencedTaskCheckerScope() UNLOCK_FUNCTION();
+};
+
+}  // namespace internal
+
+#define RTC_DCHECK_CALLED_SEQUENTIALLY(x) \
+  rtc::internal::SequencedTaskCheckerScope checker(x)
+
+#undef ENABLE_SEQUENCED_TASK_CHECKER
+
+}  // namespace rtc
 #endif  // WEBRTC_BASE_SEQUENCED_TASK_CHECKER_H_
diff --git a/base/sequenced_task_checker_impl.cc b/base/sequenced_task_checker_impl.cc
new file mode 100644
index 0000000..f03e0b6
--- /dev/null
+++ b/base/sequenced_task_checker_impl.cc
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/sequenced_task_checker_impl.h"
+
+#if defined(WEBRTC_MAC)
+#include <dispatch/dispatch.h>
+#endif
+
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/sequenced_task_checker.h"
+#include "webrtc/base/task_queue.h"
+
+namespace rtc {
+
+SequencedTaskCheckerImpl::SequencedTaskCheckerImpl()
+    : attached_(true), valid_queue_(TaskQueue::Current()) {}
+
+SequencedTaskCheckerImpl::~SequencedTaskCheckerImpl() {}
+
+bool SequencedTaskCheckerImpl::CalledSequentially() const {
+  QueueId current_queue = TaskQueue::Current();
+#if defined(WEBRTC_MAC)
+  // If we're not running on a TaskQueue, use the system dispatch queue
+  // label as an identifier.
+  if (current_queue == nullptr)
+    current_queue = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
+#endif
+  CritScope scoped_lock(&lock_);
+  if (!attached_) {  // true if previously detached.
+    valid_queue_ = current_queue;
+    attached_ = true;
+  }
+  if (!valid_queue_)
+    return thread_checker_.CalledOnValidThread();
+  return valid_queue_ == current_queue;
+}
+
+void SequencedTaskCheckerImpl::Detach() {
+  CritScope scoped_lock(&lock_);
+  attached_ = false;
+  valid_queue_ = nullptr;
+  thread_checker_.DetachFromThread();
+}
+
+namespace internal {
+
+SequencedTaskCheckerScope::SequencedTaskCheckerScope(
+    const SequencedTaskChecker* checker) {
+  RTC_DCHECK(checker->CalledSequentially());
+}
+
+SequencedTaskCheckerScope::~SequencedTaskCheckerScope() {}
+
+}  // namespace internal
+}  // namespace rtc
diff --git a/base/sequenced_task_checker_impl.h b/base/sequenced_task_checker_impl.h
index 4972539..684b1dc 100644
--- a/base/sequenced_task_checker_impl.h
+++ b/base/sequenced_task_checker_impl.h
@@ -11,9 +11,35 @@
 #ifndef WEBRTC_BASE_SEQUENCED_TASK_CHECKER_IMPL_H_
 #define WEBRTC_BASE_SEQUENCED_TASK_CHECKER_IMPL_H_
 
+#include "webrtc/base/thread_checker.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sequenced_task_checker_impl.h"
+namespace rtc {
 
+class TaskQueue;
+// Real implementation of SequencedTaskChecker, for use in debug mode, or
+// for temporary use in release mode.
+//
+// Note: You should almost always use the SequencedTaskChecker class to get the
+// right version for your build configuration.
+class SequencedTaskCheckerImpl {
+ public:
+  SequencedTaskCheckerImpl();
+  ~SequencedTaskCheckerImpl();
+
+  bool CalledSequentially() const;
+
+  // Changes the task queue or thread that is checked for in IsCurrent.  This
+  // may be useful when an object may be created on one task queue / thread and
+  // then used exclusively on another thread.
+  void Detach();
+
+ private:
+  typedef const void* QueueId;
+  CriticalSection lock_;
+  ThreadChecker thread_checker_;
+  mutable bool attached_;
+  mutable QueueId valid_queue_;
+};
+
+}  // namespace rtc
 #endif  // WEBRTC_BASE_SEQUENCED_TASK_CHECKER_IMPL_H_
diff --git a/base/sequenced_task_checker_unittest.cc b/base/sequenced_task_checker_unittest.cc
new file mode 100644
index 0000000..ae6e09d
--- /dev/null
+++ b/base/sequenced_task_checker_unittest.cc
@@ -0,0 +1,273 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/sequenced_task_checker.h"
+#include "webrtc/base/task_queue.h"
+#include "webrtc/test/gtest.h"
+
+namespace rtc {
+
+namespace {
+// Calls SequencedTaskChecker::CalledSequentially on another thread.
+class CallCalledSequentiallyOnThread {
+ public:
+  CallCalledSequentiallyOnThread(bool expect_true,
+                                 SequencedTaskChecker* sequenced_task_checker)
+      : expect_true_(expect_true),
+        thread_has_run_event_(false, false),
+        thread_(&Run, this, "call_do_stuff_on_thread"),
+        sequenced_task_checker_(sequenced_task_checker) {
+    thread_.Start();
+  }
+  ~CallCalledSequentiallyOnThread() {
+    EXPECT_TRUE(thread_has_run_event_.Wait(1000));
+    thread_.Stop();
+  }
+
+ private:
+  static void Run(void* obj) {
+    CallCalledSequentiallyOnThread* call_stuff_on_thread =
+        static_cast<CallCalledSequentiallyOnThread*>(obj);
+    EXPECT_EQ(
+        call_stuff_on_thread->expect_true_,
+        call_stuff_on_thread->sequenced_task_checker_->CalledSequentially());
+    call_stuff_on_thread->thread_has_run_event_.Set();
+  }
+
+  const bool expect_true_;
+  Event thread_has_run_event_;
+  PlatformThread thread_;
+  SequencedTaskChecker* const sequenced_task_checker_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(CallCalledSequentiallyOnThread);
+};
+
+// Deletes SequencedTaskChecker on a different thread.
+class DeleteSequencedCheckerOnThread {
+ public:
+  explicit DeleteSequencedCheckerOnThread(
+      std::unique_ptr<SequencedTaskChecker> sequenced_task_checker)
+      : thread_(&Run, this, "delete_sequenced_task_checker_on_thread"),
+        thread_has_run_event_(false, false),
+        sequenced_task_checker_(std::move(sequenced_task_checker)) {
+    thread_.Start();
+  }
+
+  ~DeleteSequencedCheckerOnThread() {
+    EXPECT_TRUE(thread_has_run_event_.Wait(1000));
+    thread_.Stop();
+  }
+
+ private:
+  static bool Run(void* obj) {
+    DeleteSequencedCheckerOnThread* instance =
+        static_cast<DeleteSequencedCheckerOnThread*>(obj);
+    instance->sequenced_task_checker_.reset();
+    instance->thread_has_run_event_.Set();
+    return false;
+  }
+
+ private:
+  PlatformThread thread_;
+  Event thread_has_run_event_;
+  std::unique_ptr<SequencedTaskChecker> sequenced_task_checker_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(DeleteSequencedCheckerOnThread);
+};
+
+void RunMethodOnDifferentThread(bool expect_true) {
+  std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
+      new SequencedTaskChecker());
+
+  CallCalledSequentiallyOnThread call_on_thread(expect_true,
+                                                sequenced_task_checker.get());
+}
+
+void RunMethodOnDifferentTaskQueue(bool expect_true) {
+  std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
+      new SequencedTaskChecker());
+
+  static const char kQueueName[] = "MethodNotAllowedOnDifferentTq";
+  TaskQueue queue(kQueueName);
+  Event done_event(false, false);
+  queue.PostTask([&sequenced_task_checker, &done_event, expect_true] {
+    if (expect_true)
+      EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
+    else
+      EXPECT_FALSE(sequenced_task_checker->CalledSequentially());
+    done_event.Set();
+  });
+  EXPECT_TRUE(done_event.Wait(1000));
+}
+
+void DetachThenCallFromDifferentTaskQueue(bool expect_true) {
+  std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
+      new SequencedTaskChecker());
+
+  sequenced_task_checker->Detach();
+
+  Event done_event(false, false);
+  TaskQueue queue1("DetachThenCallFromDifferentTaskQueueImpl1");
+  queue1.PostTask([&sequenced_task_checker, &done_event] {
+    EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
+    done_event.Set();
+  });
+  EXPECT_TRUE(done_event.Wait(1000));
+
+  // CalledSequentially should return false in debug builds after moving to
+  // another task queue.
+  TaskQueue queue2("DetachThenCallFromDifferentTaskQueueImpl2");
+  queue2.PostTask([&sequenced_task_checker, &done_event, expect_true] {
+    if (expect_true)
+      EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
+    else
+      EXPECT_FALSE(sequenced_task_checker->CalledSequentially());
+    done_event.Set();
+  });
+  EXPECT_TRUE(done_event.Wait(1000));
+}
+}  // namespace
+
+TEST(SequencedTaskCheckerTest, CallsAllowedOnSameThread) {
+  std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
+      new SequencedTaskChecker());
+
+  EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
+
+  // Verify that the destructor doesn't assert.
+  sequenced_task_checker.reset();
+}
+
+TEST(SequencedTaskCheckerTest, DestructorAllowedOnDifferentThread) {
+  std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
+      new SequencedTaskChecker());
+
+  // Verify that the destructor doesn't assert when called on a different
+  // thread.
+  DeleteSequencedCheckerOnThread delete_on_thread(
+      std::move(sequenced_task_checker));
+}
+
+TEST(SequencedTaskCheckerTest, DetachFromThread) {
+  std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
+      new SequencedTaskChecker());
+
+  sequenced_task_checker->Detach();
+  CallCalledSequentiallyOnThread call_on_thread(true,
+                                                sequenced_task_checker.get());
+}
+
+TEST(SequencedTaskCheckerTest, DetachFromThreadAndUseOnTaskQueue) {
+  std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
+      new SequencedTaskChecker());
+
+  sequenced_task_checker->Detach();
+  static const char kQueueName[] = "DetachFromThreadAndUseOnTaskQueue";
+  TaskQueue queue(kQueueName);
+  Event done_event(false, false);
+  queue.PostTask([&sequenced_task_checker, &done_event] {
+    EXPECT_TRUE(sequenced_task_checker->CalledSequentially());
+    done_event.Set();
+  });
+  EXPECT_TRUE(done_event.Wait(1000));
+}
+
+TEST(SequencedTaskCheckerTest, DetachFromTaskQueueAndUseOnThread) {
+  TaskQueue queue("DetachFromTaskQueueAndUseOnThread");
+  Event done_event(false, false);
+  queue.PostTask([&done_event] {
+    std::unique_ptr<SequencedTaskChecker> sequenced_task_checker(
+        new SequencedTaskChecker());
+
+    sequenced_task_checker->Detach();
+    CallCalledSequentiallyOnThread call_on_thread(true,
+                                                  sequenced_task_checker.get());
+    done_event.Set();
+  });
+  EXPECT_TRUE(done_event.Wait(1000));
+}
+
+#if RTC_DCHECK_IS_ON
+TEST(SequencedTaskCheckerTest, MethodNotAllowedOnDifferentThreadInDebug) {
+  RunMethodOnDifferentThread(false);
+}
+#else
+TEST(SequencedTaskCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
+  RunMethodOnDifferentThread(true);
+}
+#endif
+
+#if RTC_DCHECK_IS_ON
+TEST(SequencedTaskCheckerTest, MethodNotAllowedOnDifferentTaskQueueInDebug) {
+  RunMethodOnDifferentTaskQueue(false);
+}
+#else
+TEST(SequencedTaskCheckerTest, MethodAllowedOnDifferentTaskQueueInRelease) {
+  RunMethodOnDifferentTaskQueue(true);
+}
+#endif
+
+#if RTC_DCHECK_IS_ON
+TEST(SequencedTaskCheckerTest, DetachFromTaskQueueInDebug) {
+  DetachThenCallFromDifferentTaskQueue(false);
+}
+#else
+TEST(SequencedTaskCheckerTest, DetachFromTaskQueueInRelease) {
+  DetachThenCallFromDifferentTaskQueue(true);
+}
+#endif
+
+class TestAnnotations {
+ public:
+  TestAnnotations() : test_var_(false) {}
+
+  void ModifyTestVar() {
+    RTC_DCHECK_CALLED_SEQUENTIALLY(&checker_);
+    test_var_ = true;
+  }
+
+ private:
+  bool test_var_ GUARDED_BY(&checker_);
+  SequencedTaskChecker checker_;
+};
+
+TEST(SequencedTaskCheckerTest, TestAnnotations) {
+  TestAnnotations annotations;
+  annotations.ModifyTestVar();
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+
+void TestAnnotationsOnWrongQueue() {
+  TestAnnotations annotations;
+  static const char kQueueName[] = "TestAnnotationsOnWrongQueueDebug";
+  TaskQueue queue(kQueueName);
+  Event done_event(false, false);
+  queue.PostTask([&annotations, &done_event] {
+    annotations.ModifyTestVar();
+    done_event.Set();
+  });
+  EXPECT_TRUE(done_event.Wait(1000));
+}
+
+#if RTC_DCHECK_IS_ON
+TEST(SequencedTaskCheckerTest, TestAnnotationsOnWrongQueueDebug) {
+  ASSERT_DEATH({ TestAnnotationsOnWrongQueue(); }, "");
+}
+#else
+TEST(SequencedTaskCheckerTest, TestAnnotationsOnWrongQueueRelease) {
+  TestAnnotationsOnWrongQueue();
+}
+#endif
+#endif  // GTEST_HAS_DEATH_TEST
+}  // namespace rtc
diff --git a/base/sha1.cc b/base/sha1.cc
new file mode 100644
index 0000000..5816152
--- /dev/null
+++ b/base/sha1.cc
@@ -0,0 +1,298 @@
+/*
+ * SHA-1 in C
+ * By Steve Reid <sreid@sea-to-sky.net>
+ * 100% Public Domain
+ *
+ * -----------------
+ * Modified 7/98
+ * By James H. Brown <jbrown@burgoyne.com>
+ * Still 100% Public Domain
+ *
+ * Corrected a problem which generated improper hash values on 16 bit machines
+ * Routine SHA1Update changed from
+ *   void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+ * len)
+ * to
+ *   void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+ * long len)
+ *
+ * The 'len' parameter was declared an int which works fine on 32 bit machines.
+ * However, on 16 bit machines an int is too small for the shifts being done
+ * against
+ * it.  This caused the hash function to generate incorrect values if len was
+ * greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+ *
+ * Since the file IO in main() reads 16K at a time, any file 8K or larger would
+ * be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+ * "a"s).
+ *
+ * I also changed the declaration of variables i & j in SHA1Update to
+ * unsigned long from unsigned int for the same reason.
+ *
+ * These changes should make no difference to any 32 bit implementations since
+ * an
+ * int and a long are the same size in those environments.
+ *
+ * --
+ * I also corrected a few compiler warnings generated by Borland C.
+ * 1. Added #include <process.h> for exit() prototype
+ * 2. Removed unused variable 'j' in SHA1Final
+ * 3. Changed exit(0) to return(0) at end of main.
+ *
+ * ALL changes I made can be located by searching for comments containing 'JHB'
+ * -----------------
+ * Modified 8/98
+ * By Steve Reid <sreid@sea-to-sky.net>
+ * Still 100% public domain
+ *
+ * 1- Removed #include <process.h> and used return() instead of exit()
+ * 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+ * 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+ *
+ * -----------------
+ * Modified 4/01
+ * By Saul Kravitz <Saul.Kravitz@celera.com>
+ * Still 100% PD
+ * Modified to run on Compaq Alpha hardware.
+ *
+ * -----------------
+ * Modified 07/2002
+ * By Ralph Giles <giles@ghostscript.com>
+ * Still 100% public domain
+ * modified for use with stdint types, autoconf
+ * code cleanup, removed attribution comments
+ * switched SHA1Final() argument order for consistency
+ * use SHA1_ prefix for public api
+ * move public api to sha1.h
+ *
+ * -----------------
+ * Modified 02/2012
+ * By Justin Uberti <juberti@google.com>
+ * Remove underscore from SHA1 prefix to avoid conflict with OpenSSL
+ * Remove test code
+ * Untabify
+ *
+ * -----------------
+ * Modified 03/2012
+ * By Ronghua Wu <ronghuawu@google.com>
+ * Change the typedef of uint32(8)_t to uint32(8). We need this because in the
+ * chromium android build, the stdio.h will include stdint.h which already
+ * defined uint32(8)_t.
+ *
+ * -----------------
+ * Modified 04/2012
+ * By Frank Barchard <fbarchard@google.com>
+ * Ported to C++, Google style, change len to size_t, enable SHA1HANDSOFF
+ *
+ * Test Vectors (from FIPS PUB 180-1)
+ * "abc"
+ *   A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ *   84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * A million repetitions of "a"
+ *   34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+ *
+ * -----------------
+ * Modified 05/2015
+ * By Sergey Ulanov <sergeyu@chromium.org>
+ * Removed static buffer to make computation thread-safe.
+ *
+ * -----------------
+ * Modified 10/2015
+ * By Peter Boström <pbos@webrtc.org>
+ * Change uint32(8) back to uint32(8)_t (undoes (03/2012) change).
+ */
+
+// Enabling SHA1HANDSOFF preserves the caller's data buffer.
+// Disabling SHA1HANDSOFF the buffer will be modified (end swapped).
+#define SHA1HANDSOFF
+
+#include "webrtc/base/sha1.h"
+
+#include <stdio.h>
+#include <string.h>
+
+namespace rtc {
+
+namespace {
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+// blk0() and blk() perform the initial expand.
+// I got the idea of expanding during the round function from SSLeay
+// FIXME: can we do this in an endian-proof way?
+#ifdef RTC_ARCH_CPU_BIG_ENDIAN
+#define blk0(i) block->l[i]
+#else
+#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
+    (rol(block->l[i], 8) & 0x00FF00FF))
+#endif
+#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
+    block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
+
+// (R0+R1), R2, R3, R4 are the different operations used in SHA1.
+#define R0(v, w, x, y, z, i) \
+    z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+    w = rol(w, 30);
+#define R1(v, w, x, y, z, i) \
+    z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+    w = rol(w, 30);
+#define R2(v, w, x, y, z, i) \
+    z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5);\
+    w = rol(w, 30);
+#define R3(v, w, x, y, z, i) \
+    z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+    w = rol(w, 30);
+#define R4(v, w, x, y, z, i) \
+    z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+    w = rol(w, 30);
+
+#ifdef VERBOSE  // SAK
+void SHAPrintContext(SHA1_CTX *context, char *msg) {
+  printf("%s (%d,%d) %x %x %x %x %x\n",
+         msg,
+         context->count[0], context->count[1],
+         context->state[0],
+         context->state[1],
+         context->state[2],
+         context->state[3],
+         context->state[4]);
+}
+#endif /* VERBOSE */
+
+// Hash a single 512-bit block. This is the core of the algorithm.
+void SHA1Transform(uint32_t state[5], const uint8_t buffer[64]) {
+  union CHAR64LONG16 {
+    uint8_t c[64];
+    uint32_t l[16];
+  };
+#ifdef SHA1HANDSOFF
+  uint8_t workspace[64];
+  memcpy(workspace, buffer, 64);
+  CHAR64LONG16* block = reinterpret_cast<CHAR64LONG16*>(workspace);
+#else
+  // Note(fbarchard): This option does modify the user's data buffer.
+  CHAR64LONG16* block = const_cast<CHAR64LONG16*>(
+      reinterpret_cast<const CHAR64LONG16*>(buffer));
+#endif
+
+  // Copy context->state[] to working vars.
+  uint32_t a = state[0];
+  uint32_t b = state[1];
+  uint32_t c = state[2];
+  uint32_t d = state[3];
+  uint32_t e = state[4];
+
+  // 4 rounds of 20 operations each. Loop unrolled.
+  // Note(fbarchard): The following has lint warnings for multiple ; on
+  // a line and no space after , but is left as-is to be similar to the
+  // original code.
+  R0(a,b,c,d,e,0); R0(e,a,b,c,d,1); R0(d,e,a,b,c,2); R0(c,d,e,a,b,3);
+  R0(b,c,d,e,a,4); R0(a,b,c,d,e,5); R0(e,a,b,c,d,6); R0(d,e,a,b,c,7);
+  R0(c,d,e,a,b,8); R0(b,c,d,e,a,9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+  R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+  R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+  R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+  R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+  R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+  R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+  R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+  R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+  R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+  R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+  R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+  R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+  R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+  R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+  R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+  R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+  R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+  // Add the working vars back into context.state[].
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+  state[4] += e;
+}
+
+}  // namespace
+
+// SHA1Init - Initialize new context.
+void SHA1Init(SHA1_CTX* context) {
+  // SHA1 initialization constants.
+  context->state[0] = 0x67452301;
+  context->state[1] = 0xEFCDAB89;
+  context->state[2] = 0x98BADCFE;
+  context->state[3] = 0x10325476;
+  context->state[4] = 0xC3D2E1F0;
+  context->count[0] = context->count[1] = 0;
+}
+
+// Run your data through this.
+void SHA1Update(SHA1_CTX* context, const uint8_t* data, size_t input_len) {
+  size_t i = 0;
+
+#ifdef VERBOSE
+  SHAPrintContext(context, "before");
+#endif
+
+  // Compute number of bytes mod 64.
+  size_t index = (context->count[0] >> 3) & 63;
+
+  // Update number of bits.
+  // TODO: Use uint64_t instead of 2 uint32_t for count.
+  // count[0] has low 29 bits for byte count + 3 pad 0's making 32 bits for
+  // bit count.
+  // Add bit count to low uint32_t
+  context->count[0] += static_cast<uint32_t>(input_len << 3);
+  if (context->count[0] < static_cast<uint32_t>(input_len << 3)) {
+    ++context->count[1];  // if overlow (carry), add one to high word
+  }
+  context->count[1] += static_cast<uint32_t>(input_len >> 29);
+  if ((index + input_len) > 63) {
+    i = 64 - index;
+    memcpy(&context->buffer[index], data, i);
+    SHA1Transform(context->state, context->buffer);
+    for (; i + 63 < input_len; i += 64) {
+      SHA1Transform(context->state, data + i);
+    }
+    index = 0;
+  }
+  memcpy(&context->buffer[index], &data[i], input_len - i);
+
+#ifdef VERBOSE
+  SHAPrintContext(context, "after ");
+#endif
+}
+
+// Add padding and return the message digest.
+void SHA1Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) {
+  uint8_t finalcount[8];
+  for (int i = 0; i < 8; ++i) {
+    // Endian independent
+    finalcount[i] = static_cast<uint8_t>(
+        (context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255);
+  }
+  SHA1Update(context, reinterpret_cast<const uint8_t*>("\200"), 1);
+  while ((context->count[0] & 504) != 448) {
+    SHA1Update(context, reinterpret_cast<const uint8_t*>("\0"), 1);
+  }
+  SHA1Update(context, finalcount, 8);  // Should cause a SHA1Transform().
+  for (int i = 0; i < SHA1_DIGEST_SIZE; ++i) {
+    digest[i] = static_cast<uint8_t>(
+        (context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+  }
+
+  // Wipe variables.
+  memset(context->buffer, 0, 64);
+  memset(context->state, 0, 20);
+  memset(context->count, 0, 8);
+  memset(finalcount, 0, 8);   // SWR
+
+#ifdef SHA1HANDSOFF  // Make SHA1Transform overwrite its own static vars.
+  SHA1Transform(context->state, context->buffer);
+#endif
+}
+
+}  // namespace rtc
diff --git a/base/sha1.h b/base/sha1.h
index fde3e59..aa5a6a5 100644
--- a/base/sha1.h
+++ b/base/sha1.h
@@ -10,9 +10,24 @@
 #ifndef WEBRTC_BASE_SHA1_H_
 #define WEBRTC_BASE_SHA1_H_
 
+#include <stdint.h>
+#include <stdlib.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sha1.h"
+namespace rtc {
+
+struct SHA1_CTX {
+  uint32_t state[5];
+  // TODO: Change bit count to uint64_t.
+  uint32_t count[2];  // Bit count of input.
+  uint8_t buffer[64];
+};
+
+#define SHA1_DIGEST_SIZE 20
+
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const uint8_t* data, size_t len);
+void SHA1Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]);
 
 #endif  // WEBRTC_BASE_SHA1_H_
+
+}  // namespace rtc
diff --git a/base/sha1digest.cc b/base/sha1digest.cc
new file mode 100644
index 0000000..c090a06
--- /dev/null
+++ b/base/sha1digest.cc
@@ -0,0 +1,32 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/sha1digest.h"
+
+namespace rtc {
+
+size_t Sha1Digest::Size() const {
+  return kSize;
+}
+
+void Sha1Digest::Update(const void* buf, size_t len) {
+  SHA1Update(&ctx_, static_cast<const uint8_t*>(buf), len);
+}
+
+size_t Sha1Digest::Finish(void* buf, size_t len) {
+  if (len < kSize) {
+    return 0;
+  }
+  SHA1Final(&ctx_, static_cast<uint8_t*>(buf));
+  SHA1Init(&ctx_);  // Reset for next use.
+  return kSize;
+}
+
+}  // namespace rtc
diff --git a/base/sha1digest.h b/base/sha1digest.h
index e3b4ef8..d321cb8 100644
--- a/base/sha1digest.h
+++ b/base/sha1digest.h
@@ -11,9 +11,26 @@
 #ifndef WEBRTC_BASE_SHA1DIGEST_H_
 #define WEBRTC_BASE_SHA1DIGEST_H_
 
+#include "webrtc/base/messagedigest.h"
+#include "webrtc/base/sha1.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sha1digest.h"
+namespace rtc {
+
+// A simple wrapper for our SHA-1 implementation.
+class Sha1Digest : public MessageDigest {
+ public:
+  enum { kSize = SHA1_DIGEST_SIZE };
+  Sha1Digest() {
+    SHA1Init(&ctx_);
+  }
+  size_t Size() const override;
+  void Update(const void* buf, size_t len) override;
+  size_t Finish(void* buf, size_t len) override;
+
+ private:
+  SHA1_CTX ctx_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SHA1DIGEST_H_
diff --git a/base/sha1digest_unittest.cc b/base/sha1digest_unittest.cc
new file mode 100644
index 0000000..d3c2043
--- /dev/null
+++ b/base/sha1digest_unittest.cc
@@ -0,0 +1,82 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/sha1digest.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/stringencode.h"
+
+namespace rtc {
+
+std::string Sha1(const std::string& input) {
+  Sha1Digest sha1;
+  return ComputeDigest(&sha1, input);
+}
+
+TEST(Sha1DigestTest, TestSize) {
+  Sha1Digest sha1;
+  EXPECT_EQ(20, static_cast<int>(Sha1Digest::kSize));
+  EXPECT_EQ(20U, sha1.Size());
+}
+
+TEST(Sha1DigestTest, TestBasic) {
+  // Test vectors from sha1.c.
+  EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709", Sha1(""));
+  EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", Sha1("abc"));
+  EXPECT_EQ("84983e441c3bd26ebaae4aa1f95129e5e54670f1",
+            Sha1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"));
+  std::string a_million_as(1000000, 'a');
+  EXPECT_EQ("34aa973cd4c4daa4f61eeb2bdbad27316534016f", Sha1(a_million_as));
+}
+
+TEST(Sha1DigestTest, TestMultipleUpdates) {
+  Sha1Digest sha1;
+  std::string input =
+      "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+  char output[Sha1Digest::kSize];
+  for (size_t i = 0; i < input.size(); ++i) {
+    sha1.Update(&input[i], 1);
+  }
+  EXPECT_EQ(sha1.Size(), sha1.Finish(output, sizeof(output)));
+  EXPECT_EQ("84983e441c3bd26ebaae4aa1f95129e5e54670f1",
+            hex_encode(output, sizeof(output)));
+}
+
+TEST(Sha1DigestTest, TestReuse) {
+  Sha1Digest sha1;
+  std::string input = "abc";
+  EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d",
+            ComputeDigest(&sha1, input));
+  input = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+  EXPECT_EQ("84983e441c3bd26ebaae4aa1f95129e5e54670f1",
+            ComputeDigest(&sha1, input));
+}
+
+TEST(Sha1DigestTest, TestBufferTooSmall) {
+  Sha1Digest sha1;
+  std::string input = "abcdefghijklmnopqrstuvwxyz";
+  char output[Sha1Digest::kSize - 1];
+  sha1.Update(input.c_str(), input.size());
+  EXPECT_EQ(0U, sha1.Finish(output, sizeof(output)));
+}
+
+TEST(Sha1DigestTest, TestBufferConst) {
+  Sha1Digest sha1;
+  const int kLongSize = 1000000;
+  std::string input(kLongSize, '\0');
+  for (int i = 0; i < kLongSize; ++i) {
+    input[i] = static_cast<char>(i);
+  }
+  sha1.Update(input.c_str(), input.size());
+  for (int i = 0; i < kLongSize; ++i) {
+    EXPECT_EQ(static_cast<char>(i), input[i]);
+  }
+}
+
+}  // namespace rtc
diff --git a/base/signalthread.cc b/base/signalthread.cc
new file mode 100644
index 0000000..be2741e
--- /dev/null
+++ b/base/signalthread.cc
@@ -0,0 +1,154 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/signalthread.h"
+
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// SignalThread
+///////////////////////////////////////////////////////////////////////////////
+
+SignalThread::SignalThread(bool use_socket_server)
+    : main_(Thread::Current()),
+      worker_(this, use_socket_server),
+      state_(kInit),
+      refcount_(1) {
+  main_->SignalQueueDestroyed.connect(this,
+                                      &SignalThread::OnMainThreadDestroyed);
+  worker_.SetName("SignalThread", this);
+}
+
+SignalThread::~SignalThread() {
+  RTC_DCHECK(refcount_ == 0);
+}
+
+bool SignalThread::SetName(const std::string& name, const void* obj) {
+  EnterExit ee(this);
+  RTC_DCHECK(main_->IsCurrent());
+  RTC_DCHECK(kInit == state_);
+  return worker_.SetName(name, obj);
+}
+
+void SignalThread::Start() {
+  EnterExit ee(this);
+  RTC_DCHECK(main_->IsCurrent());
+  if (kInit == state_ || kComplete == state_) {
+    state_ = kRunning;
+    OnWorkStart();
+    worker_.Start();
+  } else {
+    RTC_NOTREACHED();
+  }
+}
+
+void SignalThread::Destroy(bool wait) {
+  EnterExit ee(this);
+  RTC_DCHECK(main_->IsCurrent());
+  if ((kInit == state_) || (kComplete == state_)) {
+    refcount_--;
+  } else if (kRunning == state_ || kReleasing == state_) {
+    state_ = kStopping;
+    // OnWorkStop() must follow Quit(), so that when the thread wakes up due to
+    // OWS(), ContinueWork() will return false.
+    worker_.Quit();
+    OnWorkStop();
+    if (wait) {
+      // Release the thread's lock so that it can return from ::Run.
+      cs_.Leave();
+      worker_.Stop();
+      cs_.Enter();
+      refcount_--;
+    }
+  } else {
+    RTC_NOTREACHED();
+  }
+}
+
+void SignalThread::Release() {
+  EnterExit ee(this);
+  RTC_DCHECK(main_->IsCurrent());
+  if (kComplete == state_) {
+    refcount_--;
+  } else if (kRunning == state_) {
+    state_ = kReleasing;
+  } else {
+    // if (kInit == state_) use Destroy()
+    RTC_NOTREACHED();
+  }
+}
+
+bool SignalThread::ContinueWork() {
+  EnterExit ee(this);
+  RTC_DCHECK(worker_.IsCurrent());
+  return worker_.ProcessMessages(0);
+}
+
+void SignalThread::OnMessage(Message *msg) {
+  EnterExit ee(this);
+  if (ST_MSG_WORKER_DONE == msg->message_id) {
+    RTC_DCHECK(main_->IsCurrent());
+    OnWorkDone();
+    bool do_delete = false;
+    if (kRunning == state_) {
+      state_ = kComplete;
+    } else {
+      do_delete = true;
+    }
+    if (kStopping != state_) {
+      // Before signaling that the work is done, make sure that the worker
+      // thread actually is done. We got here because DoWork() finished and
+      // Run() posted the ST_MSG_WORKER_DONE message. This means the worker
+      // thread is about to go away anyway, but sometimes it doesn't actually
+      // finish before SignalWorkDone is processed, and for a reusable
+      // SignalThread this makes an assert in thread.cc fire.
+      //
+      // Calling Stop() on the worker ensures that the OS thread that underlies
+      // the worker will finish, and will be set to null, enabling us to call
+      // Start() again.
+      worker_.Stop();
+      SignalWorkDone(this);
+    }
+    if (do_delete) {
+      refcount_--;
+    }
+  }
+}
+
+SignalThread::Worker::~Worker() {
+  Stop();
+}
+
+void SignalThread::Worker::Run() {
+  parent_->Run();
+}
+
+void SignalThread::Run() {
+  DoWork();
+  {
+    EnterExit ee(this);
+    if (main_) {
+      main_->Post(RTC_FROM_HERE, this, ST_MSG_WORKER_DONE);
+    }
+  }
+}
+
+void SignalThread::OnMainThreadDestroyed() {
+  EnterExit ee(this);
+  main_ = nullptr;
+}
+
+bool SignalThread::Worker::IsProcessingMessages() {
+  return false;
+}
+
+}  // namespace rtc
diff --git a/base/signalthread.h b/base/signalthread.h
index f5fcf2c..d42d6ef 100644
--- a/base/signalthread.h
+++ b/base/signalthread.h
@@ -11,9 +11,151 @@
 #ifndef WEBRTC_BASE_SIGNALTHREAD_H_
 #define WEBRTC_BASE_SIGNALTHREAD_H_
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/signalthread.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/nullsocketserver.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// SignalThread - Base class for worker threads.  The main thread should call
+//  Start() to begin work, and then follow one of these models:
+//   Normal: Wait for SignalWorkDone, and then call Release to destroy.
+//   Cancellation: Call Release(true), to abort the worker thread.
+//   Fire-and-forget: Call Release(false), which allows the thread to run to
+//    completion, and then self-destruct without further notification.
+//   Periodic tasks: Wait for SignalWorkDone, then eventually call Start()
+//    again to repeat the task. When the instance isn't needed anymore,
+//    call Release. DoWork, OnWorkStart and OnWorkStop are called again,
+//    on a new thread.
+//  The subclass should override DoWork() to perform the background task.  By
+//   periodically calling ContinueWork(), it can check for cancellation.
+//   OnWorkStart and OnWorkDone can be overridden to do pre- or post-work
+//   tasks in the context of the main thread.
+///////////////////////////////////////////////////////////////////////////////
+
+class SignalThread
+    : public sigslot::has_slots<>,
+      protected MessageHandler {
+ public:
+  explicit SignalThread(bool use_socket_server = true);
+
+  // Context: Main Thread.  Call before Start to change the worker's name.
+  bool SetName(const std::string& name, const void* obj);
+
+  // Context: Main Thread.  Call to begin the worker thread.
+  void Start();
+
+  // Context: Main Thread.  If the worker thread is not running, deletes the
+  // object immediately.  Otherwise, asks the worker thread to abort processing,
+  // and schedules the object to be deleted once the worker exits.
+  // SignalWorkDone will not be signalled.  If wait is true, does not return
+  // until the thread is deleted.
+  void Destroy(bool wait);
+
+  // Context: Main Thread.  If the worker thread is complete, deletes the
+  // object immediately.  Otherwise, schedules the object to be deleted once
+  // the worker thread completes.  SignalWorkDone will be signalled.
+  void Release();
+
+  // Context: Main Thread.  Signalled when work is complete.
+  sigslot::signal1<SignalThread *> SignalWorkDone;
+
+  enum { ST_MSG_WORKER_DONE, ST_MSG_FIRST_AVAILABLE };
+
+ protected:
+  ~SignalThread() override;
+
+  Thread* worker() { return &worker_; }
+
+  // Context: Main Thread.  Subclass should override to do pre-work setup.
+  virtual void OnWorkStart() { }
+
+  // Context: Worker Thread.  Subclass should override to do work.
+  virtual void DoWork() = 0;
+
+  // Context: Worker Thread.  Subclass should call periodically to
+  // dispatch messages and determine if the thread should terminate.
+  bool ContinueWork();
+
+  // Context: Worker Thread.  Subclass should override when extra work is
+  // needed to abort the worker thread.
+  virtual void OnWorkStop() { }
+
+  // Context: Main Thread.  Subclass should override to do post-work cleanup.
+  virtual void OnWorkDone() { }
+
+  // Context: Any Thread.  If subclass overrides, be sure to call the base
+  // implementation.  Do not use (message_id < ST_MSG_FIRST_AVAILABLE)
+  void OnMessage(Message* msg) override;
+
+ private:
+  enum State {
+    kInit,            // Initialized, but not started
+    kRunning,         // Started and doing work
+    kReleasing,       // Same as running, but to be deleted when work is done
+    kComplete,        // Work is done
+    kStopping,        // Work is being interrupted
+  };
+
+  class Worker : public Thread {
+   public:
+    explicit Worker(SignalThread* parent, bool use_socket_server)
+        : Thread(use_socket_server
+                     ? SocketServer::CreateDefault()
+                     : std::unique_ptr<SocketServer>(new NullSocketServer())),
+          parent_(parent) {}
+    ~Worker() override;
+    void Run() override;
+    bool IsProcessingMessages() override;
+
+   private:
+    SignalThread* parent_;
+
+    RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Worker);
+  };
+
+  class SCOPED_LOCKABLE EnterExit {
+   public:
+    explicit EnterExit(SignalThread* t) EXCLUSIVE_LOCK_FUNCTION(t->cs_)
+        : t_(t) {
+      t_->cs_.Enter();
+      // If refcount_ is zero then the object has already been deleted and we
+      // will be double-deleting it in ~EnterExit()! (shouldn't happen)
+      RTC_DCHECK_NE(0, t_->refcount_);
+      ++t_->refcount_;
+    }
+    ~EnterExit() UNLOCK_FUNCTION() {
+      bool d = (0 == --t_->refcount_);
+      t_->cs_.Leave();
+      if (d)
+        delete t_;
+    }
+
+   private:
+    SignalThread* t_;
+
+    RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(EnterExit);
+  };
+
+  void Run();
+  void OnMainThreadDestroyed();
+
+  Thread* main_;
+  Worker worker_;
+  CriticalSection cs_;
+  State state_;
+  int refcount_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SignalThread);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SIGNALTHREAD_H_
diff --git a/base/signalthread_unittest.cc b/base/signalthread_unittest.cc
new file mode 100644
index 0000000..15f665a
--- /dev/null
+++ b/base/signalthread_unittest.cc
@@ -0,0 +1,205 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/signalthread.h"
+#include "webrtc/base/thread.h"
+
+using namespace rtc;
+
+// 10 seconds.
+static const int kTimeout = 10000;
+
+class SignalThreadTest : public testing::Test, public sigslot::has_slots<> {
+ public:
+  class SlowSignalThread : public SignalThread {
+   public:
+    SlowSignalThread(SignalThreadTest* harness) : harness_(harness) {
+    }
+
+    virtual ~SlowSignalThread() {
+      EXPECT_EQ(harness_->main_thread_, Thread::Current());
+      ++harness_->thread_deleted_;
+    }
+
+    const SignalThreadTest* harness() { return harness_; }
+
+   protected:
+    virtual void OnWorkStart() {
+      ASSERT_TRUE(harness_ != nullptr);
+      ++harness_->thread_started_;
+      EXPECT_EQ(harness_->main_thread_, Thread::Current());
+      EXPECT_FALSE(worker()->RunningForTest());  // not started yet
+    }
+
+    virtual void OnWorkStop() {
+      ++harness_->thread_stopped_;
+      EXPECT_EQ(harness_->main_thread_, Thread::Current());
+      EXPECT_TRUE(worker()->RunningForTest());  // not stopped yet
+    }
+
+    virtual void OnWorkDone() {
+      ++harness_->thread_done_;
+      EXPECT_EQ(harness_->main_thread_, Thread::Current());
+      EXPECT_TRUE(worker()->RunningForTest());  // not stopped yet
+    }
+
+    virtual void DoWork() {
+      EXPECT_NE(harness_->main_thread_, Thread::Current());
+      EXPECT_EQ(worker(), Thread::Current());
+      Thread::Current()->socketserver()->Wait(250, false);
+    }
+
+   private:
+    SignalThreadTest* harness_;
+    RTC_DISALLOW_COPY_AND_ASSIGN(SlowSignalThread);
+  };
+
+  void OnWorkComplete(rtc::SignalThread* thread) {
+    SlowSignalThread* t = static_cast<SlowSignalThread*>(thread);
+    EXPECT_EQ(t->harness(), this);
+    EXPECT_EQ(main_thread_, Thread::Current());
+
+    ++thread_completed_;
+    if (!called_release_) {
+      thread->Release();
+    }
+  }
+
+  virtual void SetUp() {
+    main_thread_ = Thread::Current();
+    thread_ = new SlowSignalThread(this);
+    thread_->SignalWorkDone.connect(this, &SignalThreadTest::OnWorkComplete);
+    called_release_ = false;
+    thread_started_ = 0;
+    thread_done_ = 0;
+    thread_completed_ = 0;
+    thread_stopped_ = 0;
+    thread_deleted_ = 0;
+  }
+
+  virtual void TearDown() {
+  }
+
+  Thread* main_thread_;
+  SlowSignalThread* thread_;
+  bool called_release_;
+
+  int thread_started_;
+  int thread_done_;
+  int thread_completed_;
+  int thread_stopped_;
+  int thread_deleted_;
+};
+
+class OwnerThread : public Thread, public sigslot::has_slots<> {
+ public:
+  explicit OwnerThread(SignalThreadTest* harness)
+      : harness_(harness),
+        has_run_(false) {
+  }
+
+  virtual ~OwnerThread() {
+    Stop();
+  }
+
+  virtual void Run() {
+    SignalThreadTest::SlowSignalThread* signal_thread =
+        new SignalThreadTest::SlowSignalThread(harness_);
+    signal_thread->SignalWorkDone.connect(this, &OwnerThread::OnWorkDone);
+    signal_thread->Start();
+    Thread::Current()->socketserver()->Wait(100, false);
+    signal_thread->Release();
+    // Delete |signal_thread|.
+    signal_thread->Destroy(true);
+    has_run_ = true;
+  }
+
+  bool has_run() { return has_run_; }
+  void OnWorkDone(SignalThread* signal_thread) {
+    FAIL() << " This shouldn't get called.";
+  }
+
+ private:
+  SignalThreadTest* harness_;
+  bool has_run_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(OwnerThread);
+};
+
+// Test for when the main thread goes away while the
+// signal thread is still working.  This may happen
+// when shutting down the process.
+TEST_F(SignalThreadTest, OwnerThreadGoesAway) {
+  // We don't use |thread_| for this test, so destroy it.
+  thread_->Destroy(true);
+
+  {
+    std::unique_ptr<OwnerThread> owner(new OwnerThread(this));
+    main_thread_ = owner.get();
+    owner->Start();
+    while (!owner->has_run()) {
+      Thread::Current()->socketserver()->Wait(10, false);
+    }
+  }
+  // At this point the main thread has gone away.
+  // Give the SignalThread a little time to do its callback,
+  // which will crash if the signal thread doesn't handle
+  // this situation well.
+  Thread::Current()->socketserver()->Wait(500, false);
+}
+
+#define EXPECT_STATE(started, done, completed, stopped, deleted) \
+  EXPECT_EQ(started, thread_started_); \
+  EXPECT_EQ(done, thread_done_); \
+  EXPECT_EQ(completed, thread_completed_); \
+  EXPECT_EQ(stopped, thread_stopped_); \
+  EXPECT_EQ(deleted, thread_deleted_);
+
+#define EXPECT_STATE_WAIT(started, done, completed, stopped, deleted, timeout) \
+  EXPECT_EQ_WAIT(started, thread_started_, timeout); \
+  EXPECT_EQ_WAIT(done, thread_done_, timeout); \
+  EXPECT_EQ_WAIT(completed, thread_completed_, timeout); \
+  EXPECT_EQ_WAIT(stopped, thread_stopped_, timeout); \
+  EXPECT_EQ_WAIT(deleted, thread_deleted_, timeout);
+
+TEST_F(SignalThreadTest, ThreadFinishes) {
+  thread_->Start();
+  EXPECT_STATE(1, 0, 0, 0, 0);
+  EXPECT_STATE_WAIT(1, 1, 1, 0, 1, kTimeout);
+}
+
+TEST_F(SignalThreadTest, ReleasedThreadFinishes) {
+  thread_->Start();
+  EXPECT_STATE(1, 0, 0, 0, 0);
+  thread_->Release();
+  called_release_ = true;
+  EXPECT_STATE(1, 0, 0, 0, 0);
+  EXPECT_STATE_WAIT(1, 1, 1, 0, 1, kTimeout);
+}
+
+TEST_F(SignalThreadTest, DestroyedThreadCleansUp) {
+  thread_->Start();
+  EXPECT_STATE(1, 0, 0, 0, 0);
+  thread_->Destroy(true);
+  EXPECT_STATE(1, 0, 0, 1, 1);
+  Thread::Current()->ProcessMessages(0);
+  EXPECT_STATE(1, 0, 0, 1, 1);
+}
+
+TEST_F(SignalThreadTest, DeferredDestroyedThreadCleansUp) {
+  thread_->Start();
+  EXPECT_STATE(1, 0, 0, 0, 0);
+  thread_->Destroy(false);
+  EXPECT_STATE(1, 0, 0, 1, 0);
+  EXPECT_STATE_WAIT(1, 1, 0, 1, 1, kTimeout);
+}
diff --git a/base/sigslot.cc b/base/sigslot.cc
new file mode 100644
index 0000000..2fdb214
--- /dev/null
+++ b/base/sigslot.cc
@@ -0,0 +1,22 @@
+// sigslot.h: Signal/Slot classes
+//
+// Written by Sarah Thompson (sarah@telergy.com) 2002.
+//
+// License: Public domain. You are free to use this code however you like, with
+// the proviso that the author takes on no responsibility or liability for any
+// use.
+
+#include "webrtc/base/sigslot.h"
+
+namespace sigslot {
+
+#ifdef _SIGSLOT_HAS_POSIX_THREADS
+
+pthread_mutex_t* multi_threaded_global::get_mutex() {
+  static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
+  return &g_mutex;
+}
+
+#endif  // _SIGSLOT_HAS_POSIX_THREADS
+
+}  // namespace sigslot
diff --git a/base/sigslot.h b/base/sigslot.h
index 9d31441..4649f51 100644
--- a/base/sigslot.h
+++ b/base/sigslot.h
@@ -93,12 +93,555 @@
 // If signalx is single threaded the user must ensure that disconnect, connect
 // or signal is not happening concurrently or data race may occur.
 
-#ifndef WEBRTC_BASE_SIGSLOT_H_
-#define WEBRTC_BASE_SIGSLOT_H_
+#ifndef WEBRTC_BASE_SIGSLOT_H__
+#define WEBRTC_BASE_SIGSLOT_H__
 
+#include <stdlib.h>
+#include <cstring>
+#include <list>
+#include <set>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sigslot.h"
+// On our copy of sigslot.h, we set single threading as default.
+#define SIGSLOT_DEFAULT_MT_POLICY single_threaded
 
-#endif  // WEBRTC_BASE_SIGSLOT_H_
+#if defined(SIGSLOT_PURE_ISO) ||                   \
+    (!defined(WEBRTC_WIN) && !defined(__GNUG__) && \
+     !defined(SIGSLOT_USE_POSIX_THREADS))
+#define _SIGSLOT_SINGLE_THREADED
+#elif defined(WEBRTC_WIN)
+#define _SIGSLOT_HAS_WIN32_THREADS
+#if !defined(WIN32_LEAN_AND_MEAN)
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include "webrtc/base/win32.h"
+#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS)
+#define _SIGSLOT_HAS_POSIX_THREADS
+#include <pthread.h>
+#else
+#define _SIGSLOT_SINGLE_THREADED
+#endif
+
+#ifndef SIGSLOT_DEFAULT_MT_POLICY
+#ifdef _SIGSLOT_SINGLE_THREADED
+#define SIGSLOT_DEFAULT_MT_POLICY single_threaded
+#else
+#define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
+#endif
+#endif
+
+// TODO: change this namespace to rtc?
+namespace sigslot {
+
+class single_threaded {
+ public:
+  void lock() {}
+  void unlock() {}
+};
+
+#ifdef _SIGSLOT_HAS_WIN32_THREADS
+// The multi threading policies only get compiled in if they are enabled.
+class multi_threaded_global {
+ public:
+  multi_threaded_global() {
+    static bool isinitialised = false;
+
+    if (!isinitialised) {
+      InitializeCriticalSection(get_critsec());
+      isinitialised = true;
+    }
+  }
+
+  void lock() { EnterCriticalSection(get_critsec()); }
+
+  void unlock() { LeaveCriticalSection(get_critsec()); }
+
+ private:
+  CRITICAL_SECTION* get_critsec() {
+    static CRITICAL_SECTION g_critsec;
+    return &g_critsec;
+  }
+};
+
+class multi_threaded_local {
+ public:
+  multi_threaded_local() { InitializeCriticalSection(&m_critsec); }
+
+  multi_threaded_local(const multi_threaded_local&) {
+    InitializeCriticalSection(&m_critsec);
+  }
+
+  ~multi_threaded_local() { DeleteCriticalSection(&m_critsec); }
+
+  void lock() { EnterCriticalSection(&m_critsec); }
+
+  void unlock() { LeaveCriticalSection(&m_critsec); }
+
+ private:
+  CRITICAL_SECTION m_critsec;
+};
+#endif  // _SIGSLOT_HAS_WIN32_THREADS
+
+#ifdef _SIGSLOT_HAS_POSIX_THREADS
+// The multi threading policies only get compiled in if they are enabled.
+class multi_threaded_global {
+ public:
+  void lock() { pthread_mutex_lock(get_mutex()); }
+  void unlock() { pthread_mutex_unlock(get_mutex()); }
+
+ private:
+  static pthread_mutex_t* get_mutex();
+};
+
+class multi_threaded_local {
+ public:
+  multi_threaded_local() { pthread_mutex_init(&m_mutex, nullptr); }
+  multi_threaded_local(const multi_threaded_local&) {
+    pthread_mutex_init(&m_mutex, nullptr);
+  }
+  ~multi_threaded_local() { pthread_mutex_destroy(&m_mutex); }
+  void lock() { pthread_mutex_lock(&m_mutex); }
+  void unlock() { pthread_mutex_unlock(&m_mutex); }
+
+ private:
+  pthread_mutex_t m_mutex;
+};
+#endif  // _SIGSLOT_HAS_POSIX_THREADS
+
+template <class mt_policy>
+class lock_block {
+ public:
+  mt_policy* m_mutex;
+
+  lock_block(mt_policy* mtx) : m_mutex(mtx) { m_mutex->lock(); }
+
+  ~lock_block() { m_mutex->unlock(); }
+};
+
+class _signal_base_interface;
+
+class has_slots_interface {
+ private:
+  typedef void (*signal_connect_t)(has_slots_interface* self,
+                                   _signal_base_interface* sender);
+  typedef void (*signal_disconnect_t)(has_slots_interface* self,
+                                      _signal_base_interface* sender);
+  typedef void (*disconnect_all_t)(has_slots_interface* self);
+
+  const signal_connect_t m_signal_connect;
+  const signal_disconnect_t m_signal_disconnect;
+  const disconnect_all_t m_disconnect_all;
+
+ protected:
+  has_slots_interface(signal_connect_t conn,
+                      signal_disconnect_t disc,
+                      disconnect_all_t disc_all)
+      : m_signal_connect(conn),
+        m_signal_disconnect(disc),
+        m_disconnect_all(disc_all) {}
+
+  // Doesn't really need to be virtual, but is for backwards compatibility
+  // (it was virtual in a previous version of sigslot).
+  virtual ~has_slots_interface() {}
+
+ public:
+  void signal_connect(_signal_base_interface* sender) {
+    m_signal_connect(this, sender);
+  }
+
+  void signal_disconnect(_signal_base_interface* sender) {
+    m_signal_disconnect(this, sender);
+  }
+
+  void disconnect_all() { m_disconnect_all(this); }
+};
+
+class _signal_base_interface {
+ private:
+  typedef void (*slot_disconnect_t)(_signal_base_interface* self,
+                                    has_slots_interface* pslot);
+  typedef void (*slot_duplicate_t)(_signal_base_interface* self,
+                                   const has_slots_interface* poldslot,
+                                   has_slots_interface* pnewslot);
+
+  const slot_disconnect_t m_slot_disconnect;
+  const slot_duplicate_t m_slot_duplicate;
+
+ protected:
+  _signal_base_interface(slot_disconnect_t disc, slot_duplicate_t dupl)
+      : m_slot_disconnect(disc), m_slot_duplicate(dupl) {}
+
+  ~_signal_base_interface() {}
+
+ public:
+  void slot_disconnect(has_slots_interface* pslot) {
+    m_slot_disconnect(this, pslot);
+  }
+
+  void slot_duplicate(const has_slots_interface* poldslot,
+                      has_slots_interface* pnewslot) {
+    m_slot_duplicate(this, poldslot, pnewslot);
+  }
+};
+
+class _opaque_connection {
+ private:
+  typedef void (*emit_t)(const _opaque_connection*);
+  template <typename FromT, typename ToT>
+  union union_caster {
+    FromT from;
+    ToT to;
+  };
+
+  emit_t pemit;
+  has_slots_interface* pdest;
+  // Pointers to member functions may be up to 16 bytes for virtual classes,
+  // so make sure we have enough space to store it.
+  unsigned char pmethod[16];
+
+ public:
+  template <typename DestT, typename... Args>
+  _opaque_connection(DestT* pd, void (DestT::*pm)(Args...)) : pdest(pd) {
+    typedef void (DestT::*pm_t)(Args...);
+    static_assert(sizeof(pm_t) <= sizeof(pmethod),
+                  "Size of slot function pointer too large.");
+
+    std::memcpy(pmethod, &pm, sizeof(pm_t));
+
+    typedef void (*em_t)(const _opaque_connection* self, Args...);
+    union_caster<em_t, emit_t> caster2;
+    caster2.from = &_opaque_connection::emitter<DestT, Args...>;
+    pemit = caster2.to;
+  }
+
+  has_slots_interface* getdest() const { return pdest; }
+
+  _opaque_connection duplicate(has_slots_interface* newtarget) const {
+    _opaque_connection res = *this;
+    res.pdest = newtarget;
+    return res;
+  }
+
+  // Just calls the stored "emitter" function pointer stored at construction
+  // time.
+  template <typename... Args>
+  void emit(Args... args) const {
+    typedef void (*em_t)(const _opaque_connection*, Args...);
+    union_caster<emit_t, em_t> caster;
+    caster.from = pemit;
+    (caster.to)(this, args...);
+  }
+
+ private:
+  template <typename DestT, typename... Args>
+  static void emitter(const _opaque_connection* self, Args... args) {
+    typedef void (DestT::*pm_t)(Args...);
+    pm_t pm;
+    std::memcpy(&pm, self->pmethod, sizeof(pm_t));
+    (static_cast<DestT*>(self->pdest)->*(pm))(args...);
+  }
+};
+
+template <class mt_policy>
+class _signal_base : public _signal_base_interface, public mt_policy {
+ protected:
+  typedef std::list<_opaque_connection> connections_list;
+
+  _signal_base()
+      : _signal_base_interface(&_signal_base::do_slot_disconnect,
+                               &_signal_base::do_slot_duplicate),
+        m_current_iterator(m_connected_slots.end()) {}
+
+  ~_signal_base() { disconnect_all(); }
+
+ private:
+  _signal_base& operator=(_signal_base const& that);
+
+ public:
+  _signal_base(const _signal_base& o)
+      : _signal_base_interface(&_signal_base::do_slot_disconnect,
+                               &_signal_base::do_slot_duplicate),
+        m_current_iterator(m_connected_slots.end()) {
+    lock_block<mt_policy> lock(this);
+    for (const auto& connection : o.m_connected_slots) {
+      connection.getdest()->signal_connect(this);
+      m_connected_slots.push_back(connection);
+    }
+  }
+
+  bool is_empty() {
+    lock_block<mt_policy> lock(this);
+    return m_connected_slots.empty();
+  }
+
+  void disconnect_all() {
+    lock_block<mt_policy> lock(this);
+
+    while (!m_connected_slots.empty()) {
+      has_slots_interface* pdest = m_connected_slots.front().getdest();
+      m_connected_slots.pop_front();
+      pdest->signal_disconnect(static_cast<_signal_base_interface*>(this));
+    }
+    // If disconnect_all is called while the signal is firing, advance the
+    // current slot iterator to the end to avoid an invalidated iterator from
+    // being dereferenced.
+    m_current_iterator = m_connected_slots.end();
+  }
+
+#if !defined(NDEBUG)
+  bool connected(has_slots_interface* pclass) {
+    lock_block<mt_policy> lock(this);
+    connections_list::const_iterator it = m_connected_slots.begin();
+    connections_list::const_iterator itEnd = m_connected_slots.end();
+    while (it != itEnd) {
+      if (it->getdest() == pclass)
+        return true;
+      ++it;
+    }
+    return false;
+  }
+#endif
+
+  void disconnect(has_slots_interface* pclass) {
+    lock_block<mt_policy> lock(this);
+    connections_list::iterator it = m_connected_slots.begin();
+    connections_list::iterator itEnd = m_connected_slots.end();
+
+    while (it != itEnd) {
+      if (it->getdest() == pclass) {
+        // If we're currently using this iterator because the signal is firing,
+        // advance it to avoid it being invalidated.
+        if (m_current_iterator == it) {
+          m_current_iterator = m_connected_slots.erase(it);
+        } else {
+          m_connected_slots.erase(it);
+        }
+        pclass->signal_disconnect(static_cast<_signal_base_interface*>(this));
+        return;
+      }
+      ++it;
+    }
+  }
+
+ private:
+  static void do_slot_disconnect(_signal_base_interface* p,
+                                 has_slots_interface* pslot) {
+    _signal_base* const self = static_cast<_signal_base*>(p);
+    lock_block<mt_policy> lock(self);
+    connections_list::iterator it = self->m_connected_slots.begin();
+    connections_list::iterator itEnd = self->m_connected_slots.end();
+
+    while (it != itEnd) {
+      connections_list::iterator itNext = it;
+      ++itNext;
+
+      if (it->getdest() == pslot) {
+        // If we're currently using this iterator because the signal is firing,
+        // advance it to avoid it being invalidated.
+        if (self->m_current_iterator == it) {
+          self->m_current_iterator = self->m_connected_slots.erase(it);
+        } else {
+          self->m_connected_slots.erase(it);
+        }
+      }
+
+      it = itNext;
+    }
+  }
+
+  static void do_slot_duplicate(_signal_base_interface* p,
+                                const has_slots_interface* oldtarget,
+                                has_slots_interface* newtarget) {
+    _signal_base* const self = static_cast<_signal_base*>(p);
+    lock_block<mt_policy> lock(self);
+    connections_list::iterator it = self->m_connected_slots.begin();
+    connections_list::iterator itEnd = self->m_connected_slots.end();
+
+    while (it != itEnd) {
+      if (it->getdest() == oldtarget) {
+        self->m_connected_slots.push_back(it->duplicate(newtarget));
+      }
+
+      ++it;
+    }
+  }
+
+ protected:
+  connections_list m_connected_slots;
+
+  // Used to handle a slot being disconnected while a signal is
+  // firing (iterating m_connected_slots).
+  connections_list::iterator m_current_iterator;
+  bool m_erase_current_iterator = false;
+};
+
+template <class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+class has_slots : public has_slots_interface, public mt_policy {
+ private:
+  typedef std::set<_signal_base_interface*> sender_set;
+  typedef sender_set::const_iterator const_iterator;
+
+ public:
+  has_slots()
+      : has_slots_interface(&has_slots::do_signal_connect,
+                            &has_slots::do_signal_disconnect,
+                            &has_slots::do_disconnect_all) {}
+
+  has_slots(has_slots const& o)
+      : has_slots_interface(&has_slots::do_signal_connect,
+                            &has_slots::do_signal_disconnect,
+                            &has_slots::do_disconnect_all) {
+    lock_block<mt_policy> lock(this);
+    for (auto* sender : o.m_senders) {
+      sender->slot_duplicate(&o, this);
+      m_senders.insert(sender);
+    }
+  }
+
+  ~has_slots() { this->disconnect_all(); }
+
+ private:
+  has_slots& operator=(has_slots const&);
+
+  static void do_signal_connect(has_slots_interface* p,
+                                _signal_base_interface* sender) {
+    has_slots* const self = static_cast<has_slots*>(p);
+    lock_block<mt_policy> lock(self);
+    self->m_senders.insert(sender);
+  }
+
+  static void do_signal_disconnect(has_slots_interface* p,
+                                   _signal_base_interface* sender) {
+    has_slots* const self = static_cast<has_slots*>(p);
+    lock_block<mt_policy> lock(self);
+    self->m_senders.erase(sender);
+  }
+
+  static void do_disconnect_all(has_slots_interface* p) {
+    has_slots* const self = static_cast<has_slots*>(p);
+    lock_block<mt_policy> lock(self);
+    while (!self->m_senders.empty()) {
+      std::set<_signal_base_interface*> senders;
+      senders.swap(self->m_senders);
+      const_iterator it = senders.begin();
+      const_iterator itEnd = senders.end();
+
+      while (it != itEnd) {
+        _signal_base_interface* s = *it;
+        ++it;
+        s->slot_disconnect(p);
+      }
+    }
+  }
+
+ private:
+  sender_set m_senders;
+};
+
+template <class mt_policy, typename... Args>
+class signal_with_thread_policy : public _signal_base<mt_policy> {
+ private:
+  typedef _signal_base<mt_policy> base;
+
+ protected:
+  typedef typename base::connections_list connections_list;
+
+ public:
+  signal_with_thread_policy() {}
+
+  template <class desttype>
+  void connect(desttype* pclass, void (desttype::*pmemfun)(Args...)) {
+    lock_block<mt_policy> lock(this);
+    this->m_connected_slots.push_back(_opaque_connection(pclass, pmemfun));
+    pclass->signal_connect(static_cast<_signal_base_interface*>(this));
+  }
+
+  void emit(Args... args) {
+    lock_block<mt_policy> lock(this);
+    this->m_current_iterator = this->m_connected_slots.begin();
+    while (this->m_current_iterator != this->m_connected_slots.end()) {
+      _opaque_connection const& conn = *this->m_current_iterator;
+      ++(this->m_current_iterator);
+      conn.emit<Args...>(args...);
+    }
+  }
+
+  void operator()(Args... args) { emit(args...); }
+};
+
+// Alias with default thread policy. Needed because both default arguments
+// and variadic template arguments must go at the end of the list, so we
+// can't have both at once.
+template <typename... Args>
+using signal = signal_with_thread_policy<SIGSLOT_DEFAULT_MT_POLICY, Args...>;
+
+// The previous verion of sigslot didn't use variadic templates, so you would
+// need to write "sigslot::signal2<Arg1, Arg2>", for example.
+// Now you can just write "sigslot::signal<Arg1, Arg2>", but these aliases
+// exist for backwards compatibility.
+template <typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal0 = signal_with_thread_policy<mt_policy>;
+
+template <typename A1, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal1 = signal_with_thread_policy<mt_policy, A1>;
+
+template <typename A1,
+          typename A2,
+          typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal2 = signal_with_thread_policy<mt_policy, A1, A2>;
+
+template <typename A1,
+          typename A2,
+          typename A3,
+          typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal3 = signal_with_thread_policy<mt_policy, A1, A2, A3>;
+
+template <typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal4 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4>;
+
+template <typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal5 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5>;
+
+template <typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal6 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6>;
+
+template <typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7,
+          typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal7 =
+    signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7>;
+
+template <typename A1,
+          typename A2,
+          typename A3,
+          typename A4,
+          typename A5,
+          typename A6,
+          typename A7,
+          typename A8,
+          typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal8 =
+    signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7, A8>;
+
+}  // namespace sigslot
+
+#endif  // WEBRTC_BASE_SIGSLOT_H__
diff --git a/base/sigslot_unittest.cc b/base/sigslot_unittest.cc
new file mode 100644
index 0000000..6d21d84
--- /dev/null
+++ b/base/sigslot_unittest.cc
@@ -0,0 +1,357 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/sigslot.h"
+
+#include "webrtc/base/gunit.h"
+
+// This function, when passed a has_slots or signalx, will break the build if
+// its threading requirement is not single threaded
+static bool TemplateIsST(const sigslot::single_threaded* p) {
+  return true;
+}
+// This function, when passed a has_slots or signalx, will break the build if
+// its threading requirement is not multi threaded
+static bool TemplateIsMT(const sigslot::multi_threaded_local* p) {
+  return true;
+}
+
+class SigslotDefault : public testing::Test, public sigslot::has_slots<> {
+ protected:
+  sigslot::signal0<> signal_;
+};
+
+template<class slot_policy = sigslot::single_threaded,
+         class signal_policy = sigslot::single_threaded>
+class SigslotReceiver : public sigslot::has_slots<slot_policy> {
+ public:
+  SigslotReceiver() : signal_(nullptr), signal_count_(0) {}
+  ~SigslotReceiver() {
+  }
+
+  // Provide copy constructor so that tests can exercise the has_slots copy
+  // constructor.
+  SigslotReceiver(const SigslotReceiver&) = default;
+
+  void Connect(sigslot::signal0<signal_policy>* signal) {
+    if (!signal) return;
+    Disconnect();
+    signal_ = signal;
+    signal->connect(this,
+                    &SigslotReceiver<slot_policy, signal_policy>::OnSignal);
+  }
+  void Disconnect() {
+    if (!signal_) return;
+    signal_->disconnect(this);
+    signal_ = nullptr;
+  }
+  void OnSignal() {
+    ++signal_count_;
+  }
+  int signal_count() { return signal_count_; }
+
+ private:
+  sigslot::signal0<signal_policy>* signal_;
+  int signal_count_;
+};
+
+template<class slot_policy = sigslot::single_threaded,
+         class mt_signal_policy = sigslot::multi_threaded_local>
+class SigslotSlotTest : public testing::Test {
+ protected:
+  SigslotSlotTest() {
+    mt_signal_policy mt_policy;
+    TemplateIsMT(&mt_policy);
+  }
+
+  virtual void SetUp() {
+    Connect();
+  }
+  virtual void TearDown() {
+    Disconnect();
+  }
+
+  void Disconnect() {
+    st_receiver_.Disconnect();
+    mt_receiver_.Disconnect();
+  }
+
+  void Connect() {
+    st_receiver_.Connect(&SignalSTLoopback);
+    mt_receiver_.Connect(&SignalMTLoopback);
+  }
+
+  int st_loop_back_count() { return st_receiver_.signal_count(); }
+  int mt_loop_back_count() { return mt_receiver_.signal_count(); }
+
+  sigslot::signal0<> SignalSTLoopback;
+  SigslotReceiver<slot_policy, sigslot::single_threaded> st_receiver_;
+  sigslot::signal0<mt_signal_policy> SignalMTLoopback;
+  SigslotReceiver<slot_policy, mt_signal_policy> mt_receiver_;
+};
+
+typedef SigslotSlotTest<> SigslotSTSlotTest;
+typedef SigslotSlotTest<sigslot::multi_threaded_local,
+                        sigslot::multi_threaded_local> SigslotMTSlotTest;
+
+class multi_threaded_local_fake : public sigslot::multi_threaded_local {
+ public:
+  multi_threaded_local_fake() : lock_count_(0), unlock_count_(0) {
+  }
+
+  void lock() { ++lock_count_; }
+  void unlock() { ++unlock_count_; }
+
+  int lock_count() { return lock_count_; }
+
+  bool InCriticalSection() { return lock_count_ != unlock_count_; }
+
+ protected:
+  int lock_count_;
+  int unlock_count_;
+};
+
+typedef SigslotSlotTest<multi_threaded_local_fake,
+                        multi_threaded_local_fake> SigslotMTLockBase;
+
+class SigslotMTLockTest : public SigslotMTLockBase {
+ protected:
+  SigslotMTLockTest() {}
+
+  virtual void SetUp() {
+    EXPECT_EQ(0, SlotLockCount());
+    SigslotMTLockBase::SetUp();
+    // Connects to two signals (ST and MT). However,
+    // SlotLockCount() only gets the count for the
+    // MT signal (there are two separate SigslotReceiver which
+    // keep track of their own count).
+    EXPECT_EQ(1, SlotLockCount());
+  }
+  virtual void TearDown() {
+    const int previous_lock_count = SlotLockCount();
+    SigslotMTLockBase::TearDown();
+    // Disconnects from two signals. Note analogous to SetUp().
+    EXPECT_EQ(previous_lock_count + 1, SlotLockCount());
+  }
+
+  int SlotLockCount() { return mt_receiver_.lock_count(); }
+  void Signal() { SignalMTLoopback(); }
+  int SignalLockCount() { return SignalMTLoopback.lock_count(); }
+  int signal_count() { return mt_loop_back_count(); }
+  bool InCriticalSection() { return SignalMTLoopback.InCriticalSection(); }
+};
+
+// This test will always succeed. However, if the default template instantiation
+// changes from single threaded to multi threaded it will break the build here.
+TEST_F(SigslotDefault, DefaultIsST) {
+  EXPECT_TRUE(TemplateIsST(this));
+  EXPECT_TRUE(TemplateIsST(&signal_));
+}
+
+// ST slot, ST signal
+TEST_F(SigslotSTSlotTest, STLoopbackTest) {
+  SignalSTLoopback();
+  EXPECT_EQ(1, st_loop_back_count());
+  EXPECT_EQ(0, mt_loop_back_count());
+}
+
+// ST slot, MT signal
+TEST_F(SigslotSTSlotTest, MTLoopbackTest) {
+  SignalMTLoopback();
+  EXPECT_EQ(1, mt_loop_back_count());
+  EXPECT_EQ(0, st_loop_back_count());
+}
+
+// ST slot, both ST and MT (separate) signal
+TEST_F(SigslotSTSlotTest, AllLoopbackTest) {
+  SignalSTLoopback();
+  SignalMTLoopback();
+  EXPECT_EQ(1, mt_loop_back_count());
+  EXPECT_EQ(1, st_loop_back_count());
+}
+
+TEST_F(SigslotSTSlotTest, Reconnect) {
+  SignalSTLoopback();
+  SignalMTLoopback();
+  EXPECT_EQ(1, mt_loop_back_count());
+  EXPECT_EQ(1, st_loop_back_count());
+  Disconnect();
+  SignalSTLoopback();
+  SignalMTLoopback();
+  EXPECT_EQ(1, mt_loop_back_count());
+  EXPECT_EQ(1, st_loop_back_count());
+  Connect();
+  SignalSTLoopback();
+  SignalMTLoopback();
+  EXPECT_EQ(2, mt_loop_back_count());
+  EXPECT_EQ(2, st_loop_back_count());
+}
+
+// MT slot, ST signal
+TEST_F(SigslotMTSlotTest, STLoopbackTest) {
+  SignalSTLoopback();
+  EXPECT_EQ(1, st_loop_back_count());
+  EXPECT_EQ(0, mt_loop_back_count());
+}
+
+// MT slot, MT signal
+TEST_F(SigslotMTSlotTest, MTLoopbackTest) {
+  SignalMTLoopback();
+  EXPECT_EQ(1, mt_loop_back_count());
+  EXPECT_EQ(0, st_loop_back_count());
+}
+
+// MT slot, both ST and MT (separate) signal
+TEST_F(SigslotMTSlotTest, AllLoopbackTest) {
+  SignalMTLoopback();
+  SignalSTLoopback();
+  EXPECT_EQ(1, st_loop_back_count());
+  EXPECT_EQ(1, mt_loop_back_count());
+}
+
+// Test that locks are acquired and released correctly.
+TEST_F(SigslotMTLockTest, LockSanity) {
+  const int lock_count = SignalLockCount();
+  Signal();
+  EXPECT_FALSE(InCriticalSection());
+  EXPECT_EQ(lock_count + 1, SignalLockCount());
+  EXPECT_EQ(1, signal_count());
+}
+
+// Destroy signal and slot in different orders.
+TEST(SigslotDestructionOrder, SignalFirst) {
+  sigslot::signal0<>* signal = new sigslot::signal0<>;
+  SigslotReceiver<>* receiver = new SigslotReceiver<>();
+  receiver->Connect(signal);
+  (*signal)();
+  EXPECT_EQ(1, receiver->signal_count());
+  delete signal;
+  delete receiver;
+}
+
+TEST(SigslotDestructionOrder, SlotFirst) {
+  sigslot::signal0<>* signal = new sigslot::signal0<>;
+  SigslotReceiver<>* receiver = new SigslotReceiver<>();
+  receiver->Connect(signal);
+  (*signal)();
+  EXPECT_EQ(1, receiver->signal_count());
+
+  delete receiver;
+  (*signal)();
+  delete signal;
+}
+
+// Test that if a signal is copied, its slot connections are copied as well.
+TEST(SigslotTest, CopyConnectedSignal) {
+  sigslot::signal<> signal;
+  SigslotReceiver<> receiver;
+  receiver.Connect(&signal);
+
+  // Fire the copied signal, expecting the receiver to be notified.
+  sigslot::signal<> copied_signal(signal);
+  copied_signal();
+  EXPECT_EQ(1, receiver.signal_count());
+}
+
+// Test that if a slot is copied, its signal connections are copied as well.
+TEST(SigslotTest, CopyConnectedSlot) {
+  sigslot::signal<> signal;
+  SigslotReceiver<> receiver;
+  receiver.Connect(&signal);
+
+  // Fire the signal after copying the receiver, expecting the copied receiver
+  // to be notified.
+  SigslotReceiver<> copied_receiver(receiver);
+  signal();
+  EXPECT_EQ(1, copied_receiver.signal_count());
+}
+
+// Just used for the test below.
+class Disconnector : public sigslot::has_slots<> {
+ public:
+  Disconnector(SigslotReceiver<>* receiver1, SigslotReceiver<>* receiver2)
+      : receiver1_(receiver1), receiver2_(receiver2) {}
+
+  void Connect(sigslot::signal<>* signal) {
+    signal_ = signal;
+    signal->connect(this, &Disconnector::Disconnect);
+  }
+
+ private:
+  void Disconnect() {
+    receiver1_->Disconnect();
+    receiver2_->Disconnect();
+    signal_->disconnect(this);
+  }
+
+  sigslot::signal<>* signal_;
+  SigslotReceiver<>* receiver1_;
+  SigslotReceiver<>* receiver2_;
+};
+
+// Test that things work as expected if a signal is disconnected from a slot
+// while it's firing.
+TEST(SigslotTest, DisconnectFromSignalWhileFiring) {
+  sigslot::signal<> signal;
+  SigslotReceiver<> receiver1;
+  SigslotReceiver<> receiver2;
+  SigslotReceiver<> receiver3;
+  Disconnector disconnector(&receiver1, &receiver2);
+
+  // From this ordering, receiver1 should receive the signal, then the
+  // disconnector will be invoked, causing receiver2 to be disconnected before
+  // it receives the signal. And receiver3 should also receive the signal,
+  // since it was never disconnected.
+  receiver1.Connect(&signal);
+  disconnector.Connect(&signal);
+  receiver2.Connect(&signal);
+  receiver3.Connect(&signal);
+  signal();
+
+  EXPECT_EQ(1, receiver1.signal_count());
+  EXPECT_EQ(0, receiver2.signal_count());
+  EXPECT_EQ(1, receiver3.signal_count());
+}
+
+// Uses disconnect_all instead of disconnect.
+class Disconnector2 : public sigslot::has_slots<> {
+ public:
+  void Connect(sigslot::signal<>* signal) {
+    signal_ = signal;
+    signal->connect(this, &Disconnector2::Disconnect);
+  }
+
+ private:
+  void Disconnect() {
+    signal_->disconnect_all();
+  }
+
+  sigslot::signal<>* signal_;
+};
+
+// Test that things work as expected if a signal is disconnected from a slot
+// while it's firing using disconnect_all.
+TEST(SigslotTest, CallDisconnectAllWhileSignalFiring) {
+  sigslot::signal<> signal;
+  SigslotReceiver<> receiver1;
+  SigslotReceiver<> receiver2;
+  Disconnector2 disconnector;
+
+  // From this ordering, receiver1 should receive the signal, then the
+  // disconnector will be invoked, causing receiver2 to be disconnected before
+  // it receives the signal.
+  receiver1.Connect(&signal);
+  disconnector.Connect(&signal);
+  receiver2.Connect(&signal);
+  signal();
+
+  EXPECT_EQ(1, receiver1.signal_count());
+  EXPECT_EQ(0, receiver2.signal_count());
+}
diff --git a/base/sigslottester.h b/base/sigslottester.h
old mode 100644
new mode 100755
index 545bf9e..18ddde3
--- a/base/sigslottester.h
+++ b/base/sigslottester.h
@@ -15,9 +15,202 @@
 #ifndef WEBRTC_BASE_SIGSLOTTESTER_H_
 #define WEBRTC_BASE_SIGSLOTTESTER_H_
 
+// To generate sigslottester.h from sigslottester.h.pump, execute:
+// /home/build/google3/third_party/gtest/scripts/pump.py sigslottester.h.pump
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sigslottester.h"
+
+// SigslotTester(s) are utility classes to check if signals owned by an
+// object are being invoked at the right time and with the right arguments.
+// They are meant to be used in tests. Tests must provide "capture" pointers
+// (i.e. address of variables) where the arguments from the signal callback
+// can be stored.
+//
+// Example:
+//   /* Some signal */
+//   sigslot::signal1<const std::string&> foo;
+//
+//   /* We want to monitor foo in some test. Note how signal argument is
+//      const std::string&, but capture-type is std::string. Capture type
+//      must be type that can be assigned to. */
+//   std::string capture;
+//   SigslotTester1<const std::string&, std::string> slot(&foo, &capture);
+//   foo.emit("hello");
+//   EXPECT_EQ(1, slot.callback_count());
+//   EXPECT_EQ("hello", capture);
+//   /* See unit-tests for more examples */
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/sigslot.h"
+
+namespace rtc {
+
+// Base version for testing signals that passes no arguments.
+class SigslotTester0 : public sigslot::has_slots<> {
+ public:
+  explicit SigslotTester0(sigslot::signal0<>* signal) : callback_count_(0) {
+    signal->connect(this, &SigslotTester0::OnSignalCallback);
+  }
+
+  int callback_count() const { return callback_count_; }
+
+ private:
+  void OnSignalCallback() { callback_count_++; }
+  int callback_count_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester0);
+};
+
+// Versions below are for testing signals that pass arguments. For all the
+// templates below:
+// - A1-A5 is the type of the argument i in the callback. Signals may and often
+//   do use const-references here for efficiency.
+// - C1-C5 is the type of the variable to capture argument i. These should be
+//   non-const value types suitable for use as lvalues.
+
+template <class A1, class C1>
+class SigslotTester1 : public sigslot::has_slots<> {
+ public:
+  SigslotTester1(sigslot::signal1<A1>* signal,
+                C1* capture1)
+      : callback_count_(0),
+      capture1_(capture1) {
+    signal->connect(this, &SigslotTester1::OnSignalCallback);
+  }
+
+  int callback_count() const { return callback_count_; }
+
+ private:
+  void OnSignalCallback(A1 arg1) {
+    callback_count_++;
+    *capture1_ = arg1;
+  }
+
+  int callback_count_;
+  C1* capture1_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester1);
+};
+
+template <class A1, class A2, class C1, class C2>
+class SigslotTester2 : public sigslot::has_slots<> {
+ public:
+  SigslotTester2(sigslot::signal2<A1, A2>* signal,
+                C1* capture1, C2* capture2)
+      : callback_count_(0),
+      capture1_(capture1), capture2_(capture2) {
+    signal->connect(this, &SigslotTester2::OnSignalCallback);
+  }
+
+  int callback_count() const { return callback_count_; }
+
+ private:
+  void OnSignalCallback(A1 arg1, A2 arg2) {
+    callback_count_++;
+    *capture1_ = arg1;
+    *capture2_ = arg2;
+  }
+
+  int callback_count_;
+  C1* capture1_;
+  C2* capture2_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester2);
+};
+
+template <class A1, class A2, class A3, class C1, class C2, class C3>
+class SigslotTester3 : public sigslot::has_slots<> {
+ public:
+  SigslotTester3(sigslot::signal3<A1, A2, A3>* signal,
+                C1* capture1, C2* capture2, C3* capture3)
+      : callback_count_(0),
+      capture1_(capture1), capture2_(capture2), capture3_(capture3) {
+    signal->connect(this, &SigslotTester3::OnSignalCallback);
+  }
+
+  int callback_count() const { return callback_count_; }
+
+ private:
+  void OnSignalCallback(A1 arg1, A2 arg2, A3 arg3) {
+    callback_count_++;
+    *capture1_ = arg1;
+    *capture2_ = arg2;
+    *capture3_ = arg3;
+  }
+
+  int callback_count_;
+  C1* capture1_;
+  C2* capture2_;
+  C3* capture3_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester3);
+};
+
+template <class A1, class A2, class A3, class A4, class C1, class C2, class C3,
+    class C4>
+class SigslotTester4 : public sigslot::has_slots<> {
+ public:
+  SigslotTester4(sigslot::signal4<A1, A2, A3, A4>* signal,
+                C1* capture1, C2* capture2, C3* capture3, C4* capture4)
+      : callback_count_(0),
+      capture1_(capture1), capture2_(capture2), capture3_(capture3),
+          capture4_(capture4) {
+    signal->connect(this, &SigslotTester4::OnSignalCallback);
+  }
+
+  int callback_count() const { return callback_count_; }
+
+ private:
+  void OnSignalCallback(A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
+    callback_count_++;
+    *capture1_ = arg1;
+    *capture2_ = arg2;
+    *capture3_ = arg3;
+    *capture4_ = arg4;
+  }
+
+  int callback_count_;
+  C1* capture1_;
+  C2* capture2_;
+  C3* capture3_;
+  C4* capture4_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester4);
+};
+
+template <class A1, class A2, class A3, class A4, class A5, class C1, class C2,
+    class C3, class C4, class C5>
+class SigslotTester5 : public sigslot::has_slots<> {
+ public:
+  SigslotTester5(sigslot::signal5<A1, A2, A3, A4, A5>* signal,
+                C1* capture1, C2* capture2, C3* capture3, C4* capture4,
+                    C5* capture5)
+      : callback_count_(0),
+      capture1_(capture1), capture2_(capture2), capture3_(capture3),
+          capture4_(capture4), capture5_(capture5) {
+    signal->connect(this, &SigslotTester5::OnSignalCallback);
+  }
+
+  int callback_count() const { return callback_count_; }
+
+ private:
+  void OnSignalCallback(A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 arg5) {
+    callback_count_++;
+    *capture1_ = arg1;
+    *capture2_ = arg2;
+    *capture3_ = arg3;
+    *capture4_ = arg4;
+    *capture5_ = arg5;
+  }
+
+  int callback_count_;
+  C1* capture1_;
+  C2* capture2_;
+  C3* capture3_;
+  C4* capture4_;
+  C5* capture5_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester5);
+};
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SIGSLOTTESTER_H_
diff --git a/base/sigslottester.h.pump b/base/sigslottester.h.pump
new file mode 100755
index 0000000..8be9d7c
--- /dev/null
+++ b/base/sigslottester.h.pump
@@ -0,0 +1,102 @@
+/*
+ *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_BASE_SIGSLOTTESTER_H_
+#define WEBRTC_BASE_SIGSLOTTESTER_H_
+
+// To generate sigslottester.h from sigslottester.h.pump, execute:
+// /home/build/google3/third_party/gtest/scripts/pump.py sigslottester.h.pump
+
+
+// SigslotTester(s) are utility classes to check if signals owned by an
+// object are being invoked at the right time and with the right arguments.
+// They are meant to be used in tests. Tests must provide "capture" pointers
+// (i.e. address of variables) where the arguments from the signal callback
+// can be stored.
+//
+// Example:
+//   /* Some signal */
+//   sigslot::signal1<const std::string&> foo;
+//
+//   /* We want to monitor foo in some test. Note how signal argument is
+//      const std::string&, but capture-type is std::string. Capture type
+//      must be type that can be assigned to. */
+//   std::string capture;
+//   SigslotTester1<const std::string&, std::string> slot(&foo, &capture);
+//   foo.emit("hello");
+//   EXPECT_EQ(1, slot.callback_count());
+//   EXPECT_EQ("hello", capture);
+//   /* See unit-tests for more examples */
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/sigslot.h"
+
+namespace rtc {
+
+// Base version for testing signals that passes no arguments.
+class SigslotTester0 : public sigslot::has_slots<> {
+ public:
+  explicit SigslotTester0(sigslot::signal0<>* signal) : callback_count_(0) {
+    signal->connect(this, &SigslotTester0::OnSignalCallback);
+  }
+
+  int callback_count() const { return callback_count_; }
+
+ private:
+  void OnSignalCallback() { callback_count_++; }
+  int callback_count_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester0);
+};
+
+// Versions below are for testing signals that pass arguments. For all the
+// templates below:
+// - A1-A5 is the type of the argument i in the callback. Signals may and often
+//   do use const-references here for efficiency.
+// - C1-C5 is the type of the variable to capture argument i. These should be
+//   non-const value types suitable for use as lvalues.
+
+$var n = 5
+$range i 1..n
+$for i [[
+$range j 1..i
+
+template <$for j , [[class A$j]], $for j , [[class C$j]]>
+class SigslotTester$i : public sigslot::has_slots<> {
+ public:
+  SigslotTester$i(sigslot::signal$i<$for j , [[A$j]]>* signal,
+                $for j , [[C$j* capture$j]])
+      : callback_count_(0),
+      $for j , [[capture$j[[]]_(capture$j)]] {
+    signal->connect(this, &SigslotTester$i::OnSignalCallback);
+  }
+
+  int callback_count() const { return callback_count_; }
+
+ private:
+  void OnSignalCallback($for j , [[A$j arg$j]]) {
+    callback_count_++;$for j [[
+
+    *capture$j[[]]_ = arg$j;]]
+
+  }
+
+  int callback_count_;$for j [[
+
+  C$j* capture$j[[]]_;]]
+
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SigslotTester$i);
+};
+
+]]
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_SIGSLOTTESTER_H_
diff --git a/base/sigslottester_unittest.cc b/base/sigslottester_unittest.cc
new file mode 100755
index 0000000..00b2577
--- /dev/null
+++ b/base/sigslottester_unittest.cc
@@ -0,0 +1,86 @@
+/*
+ *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/sigslottester.h"
+
+#include <string>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/sigslot.h"
+
+namespace rtc {
+
+TEST(SigslotTester, TestSignal1Arg) {
+  sigslot::signal1<int> source1;
+  int capture1;
+  SigslotTester1<int, int> slot1(&source1, &capture1);
+  EXPECT_EQ(0, slot1.callback_count());
+
+  source1.emit(10);
+  EXPECT_EQ(1, slot1.callback_count());
+  EXPECT_EQ(10, capture1);
+
+  source1.emit(20);
+  EXPECT_EQ(2, slot1.callback_count());
+  EXPECT_EQ(20, capture1);
+}
+
+TEST(SigslotTester, TestSignal2Args) {
+  sigslot::signal2<int, char> source2;
+  int capture1;
+  char capture2;
+  SigslotTester2<int, char, int, char> slot2(&source2, &capture1, &capture2);
+  EXPECT_EQ(0, slot2.callback_count());
+
+  source2.emit(10, 'x');
+  EXPECT_EQ(1, slot2.callback_count());
+  EXPECT_EQ(10, capture1);
+  EXPECT_EQ('x', capture2);
+
+  source2.emit(20, 'y');
+  EXPECT_EQ(2, slot2.callback_count());
+  EXPECT_EQ(20, capture1);
+  EXPECT_EQ('y', capture2);
+}
+
+// Since it applies for 1 and 2 args, we assume it will work for up to 5 args.
+
+TEST(SigslotTester, TestSignalWithConstReferenceArgs) {
+  sigslot::signal1<const std::string&> source1;
+  std::string capture1;
+  SigslotTester1<const std::string&, std::string> slot1(&source1, &capture1);
+  EXPECT_EQ(0, slot1.callback_count());
+  source1.emit("hello");
+  EXPECT_EQ(1, slot1.callback_count());
+  EXPECT_EQ("hello", capture1);
+}
+
+TEST(SigslotTester, TestSignalWithPointerToConstArgs) {
+  sigslot::signal1<const std::string*> source1;
+  const std::string* capture1;
+  SigslotTester1<const std::string*, const std::string*> slot1(&source1,
+                                                               &capture1);
+  EXPECT_EQ(0, slot1.callback_count());
+  source1.emit(nullptr);
+  EXPECT_EQ(1, slot1.callback_count());
+  EXPECT_EQ(nullptr, capture1);
+}
+
+TEST(SigslotTester, TestSignalWithConstPointerArgs) {
+  sigslot::signal1<std::string* const> source1;
+  std::string* capture1;
+  SigslotTester1<std::string* const, std::string*> slot1(&source1, &capture1);
+  EXPECT_EQ(0, slot1.callback_count());
+  source1.emit(nullptr);
+  EXPECT_EQ(1, slot1.callback_count());
+  EXPECT_EQ(nullptr, capture1);
+}
+
+}  // namespace rtc
diff --git a/base/socket.h b/base/socket.h
index 19ea7a0..38a51f8 100644
--- a/base/socket.h
+++ b/base/socket.h
@@ -8,12 +8,190 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_SOCKET_H_
-#define WEBRTC_BASE_SOCKET_H_
+#ifndef WEBRTC_BASE_SOCKET_H__
+#define WEBRTC_BASE_SOCKET_H__
 
+#include <errno.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/socket.h"
+#if defined(WEBRTC_POSIX)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#define SOCKET_EACCES EACCES
+#endif
 
-#endif  // WEBRTC_BASE_SOCKET_H_
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#endif
+
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/socketaddress.h"
+
+// Rather than converting errors into a private namespace,
+// Reuse the POSIX socket api errors. Note this depends on
+// Win32 compatibility.
+
+#if defined(WEBRTC_WIN)
+#undef EWOULDBLOCK  // Remove errno.h's definition for each macro below.
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#undef EINPROGRESS
+#define EINPROGRESS WSAEINPROGRESS
+#undef EALREADY
+#define EALREADY WSAEALREADY
+#undef ENOTSOCK
+#define ENOTSOCK WSAENOTSOCK
+#undef EDESTADDRREQ
+#define EDESTADDRREQ WSAEDESTADDRREQ
+#undef EMSGSIZE
+#define EMSGSIZE WSAEMSGSIZE
+#undef EPROTOTYPE
+#define EPROTOTYPE WSAEPROTOTYPE
+#undef ENOPROTOOPT
+#define ENOPROTOOPT WSAENOPROTOOPT
+#undef EPROTONOSUPPORT
+#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
+#undef ESOCKTNOSUPPORT
+#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
+#undef EOPNOTSUPP
+#define EOPNOTSUPP WSAEOPNOTSUPP
+#undef EPFNOSUPPORT
+#define EPFNOSUPPORT WSAEPFNOSUPPORT
+#undef EAFNOSUPPORT
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#undef EADDRINUSE
+#define EADDRINUSE WSAEADDRINUSE
+#undef EADDRNOTAVAIL
+#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#undef ENETDOWN
+#define ENETDOWN WSAENETDOWN
+#undef ENETUNREACH
+#define ENETUNREACH WSAENETUNREACH
+#undef ENETRESET
+#define ENETRESET WSAENETRESET
+#undef ECONNABORTED
+#define ECONNABORTED WSAECONNABORTED
+#undef ECONNRESET
+#define ECONNRESET WSAECONNRESET
+#undef ENOBUFS
+#define ENOBUFS WSAENOBUFS
+#undef EISCONN
+#define EISCONN WSAEISCONN
+#undef ENOTCONN
+#define ENOTCONN WSAENOTCONN
+#undef ESHUTDOWN
+#define ESHUTDOWN WSAESHUTDOWN
+#undef ETOOMANYREFS
+#define ETOOMANYREFS WSAETOOMANYREFS
+#undef ETIMEDOUT
+#define ETIMEDOUT WSAETIMEDOUT
+#undef ECONNREFUSED
+#define ECONNREFUSED WSAECONNREFUSED
+#undef ELOOP
+#define ELOOP WSAELOOP
+#undef ENAMETOOLONG
+#define ENAMETOOLONG WSAENAMETOOLONG
+#undef EHOSTDOWN
+#define EHOSTDOWN WSAEHOSTDOWN
+#undef EHOSTUNREACH
+#define EHOSTUNREACH WSAEHOSTUNREACH
+#undef ENOTEMPTY
+#define ENOTEMPTY WSAENOTEMPTY
+#undef EPROCLIM
+#define EPROCLIM WSAEPROCLIM
+#undef EUSERS
+#define EUSERS WSAEUSERS
+#undef EDQUOT
+#define EDQUOT WSAEDQUOT
+#undef ESTALE
+#define ESTALE WSAESTALE
+#undef EREMOTE
+#define EREMOTE WSAEREMOTE
+#undef EACCES
+#define SOCKET_EACCES WSAEACCES
+#endif  // WEBRTC_WIN
+
+#if defined(WEBRTC_POSIX)
+#define INVALID_SOCKET (-1)
+#define SOCKET_ERROR (-1)
+#define closesocket(s) close(s)
+#endif  // WEBRTC_POSIX
+
+namespace rtc {
+
+inline bool IsBlockingError(int e) {
+  return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS);
+}
+
+struct SentPacket {
+  SentPacket() : packet_id(-1), send_time_ms(-1) {}
+  SentPacket(int packet_id, int64_t send_time_ms)
+      : packet_id(packet_id), send_time_ms(send_time_ms) {}
+
+  int packet_id;
+  int64_t send_time_ms;
+};
+
+// General interface for the socket implementations of various networks.  The
+// methods match those of normal UNIX sockets very closely.
+class Socket {
+ public:
+  virtual ~Socket() {}
+
+  // Returns the address to which the socket is bound.  If the socket is not
+  // bound, then the any-address is returned.
+  virtual SocketAddress GetLocalAddress() const = 0;
+
+  // Returns the address to which the socket is connected.  If the socket is
+  // not connected, then the any-address is returned.
+  virtual SocketAddress GetRemoteAddress() const = 0;
+
+  virtual int Bind(const SocketAddress& addr) = 0;
+  virtual int Connect(const SocketAddress& addr) = 0;
+  virtual int Send(const void *pv, size_t cb) = 0;
+  virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) = 0;
+  // |timestamp| is in units of microseconds.
+  virtual int Recv(void* pv, size_t cb, int64_t* timestamp) = 0;
+  virtual int RecvFrom(void* pv,
+                       size_t cb,
+                       SocketAddress* paddr,
+                       int64_t* timestamp) = 0;
+  virtual int Listen(int backlog) = 0;
+  virtual Socket *Accept(SocketAddress *paddr) = 0;
+  virtual int Close() = 0;
+  virtual int GetError() const = 0;
+  virtual void SetError(int error) = 0;
+  inline bool IsBlocking() const { return IsBlockingError(GetError()); }
+
+  enum ConnState {
+    CS_CLOSED,
+    CS_CONNECTING,
+    CS_CONNECTED
+  };
+  virtual ConnState GetState() const = 0;
+
+  enum Option {
+    OPT_DONTFRAGMENT,
+    OPT_RCVBUF,      // receive buffer size
+    OPT_SNDBUF,      // send buffer size
+    OPT_NODELAY,     // whether Nagle algorithm is enabled
+    OPT_IPV6_V6ONLY, // Whether the socket is IPv6 only.
+    OPT_DSCP,        // DSCP code
+    OPT_RTP_SENDTIME_EXTN_ID,  // This is a non-traditional socket option param.
+                               // This is specific to libjingle and will be used
+                               // if SendTime option is needed at socket level.
+  };
+  virtual int GetOption(Option opt, int* value) = 0;
+  virtual int SetOption(Option opt, int value) = 0;
+
+ protected:
+  Socket() {}
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(Socket);
+};
+
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_SOCKET_H__
diff --git a/base/socket_unittest.cc b/base/socket_unittest.cc
new file mode 100644
index 0000000..2342007
--- /dev/null
+++ b/base/socket_unittest.cc
@@ -0,0 +1,1046 @@
+/*
+ *  Copyright 2007 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/socket_unittest.h"
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/asyncudpsocket.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/nethelpers.h"
+#include "webrtc/base/ptr_util.h"
+#include "webrtc/base/socketserver.h"
+#include "webrtc/base/testclient.h"
+#include "webrtc/base/testutils.h"
+#include "webrtc/base/thread.h"
+
+namespace rtc {
+
+using webrtc::testing::SSE_CLOSE;
+using webrtc::testing::SSE_ERROR;
+using webrtc::testing::SSE_OPEN;
+using webrtc::testing::SSE_READ;
+using webrtc::testing::SSE_WRITE;
+using webrtc::testing::StreamSink;
+
+#define MAYBE_SKIP_IPV6                             \
+  if (!HasIPv6Enabled()) {                          \
+    LOG(LS_INFO) << "No IPv6... skipping";          \
+    return;                                         \
+  }
+
+// Data size to be used in TcpInternal tests.
+static const size_t kTcpInternalDataSize = 1024 * 1024;  // bytes
+
+void SocketTest::TestConnectIPv4() {
+  ConnectInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestConnectIPv6() {
+  MAYBE_SKIP_IPV6;
+  ConnectInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestConnectWithDnsLookupIPv4() {
+  ConnectWithDnsLookupInternal(kIPv4Loopback, "localhost");
+}
+
+void SocketTest::TestConnectWithDnsLookupIPv6() {
+  // TODO: Enable this when DNS resolution supports IPv6.
+  LOG(LS_INFO) << "Skipping IPv6 DNS test";
+  // ConnectWithDnsLookupInternal(kIPv6Loopback, "localhost6");
+}
+
+void SocketTest::TestConnectFailIPv4() {
+  ConnectFailInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestConnectFailIPv6() {
+  MAYBE_SKIP_IPV6;
+  ConnectFailInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestConnectWithDnsLookupFailIPv4() {
+  ConnectWithDnsLookupFailInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestConnectWithDnsLookupFailIPv6() {
+  MAYBE_SKIP_IPV6;
+  ConnectWithDnsLookupFailInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestConnectWithClosedSocketIPv4() {
+  ConnectWithClosedSocketInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestConnectWithClosedSocketIPv6() {
+  MAYBE_SKIP_IPV6;
+  ConnectWithClosedSocketInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestConnectWhileNotClosedIPv4() {
+  ConnectWhileNotClosedInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestConnectWhileNotClosedIPv6() {
+  MAYBE_SKIP_IPV6;
+  ConnectWhileNotClosedInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestServerCloseDuringConnectIPv4() {
+  ServerCloseDuringConnectInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestServerCloseDuringConnectIPv6() {
+  MAYBE_SKIP_IPV6;
+  ServerCloseDuringConnectInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestClientCloseDuringConnectIPv4() {
+  ClientCloseDuringConnectInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestClientCloseDuringConnectIPv6() {
+  MAYBE_SKIP_IPV6;
+  ClientCloseDuringConnectInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestServerCloseIPv4() {
+  ServerCloseInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestServerCloseIPv6() {
+  MAYBE_SKIP_IPV6;
+  ServerCloseInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestCloseInClosedCallbackIPv4() {
+  CloseInClosedCallbackInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestCloseInClosedCallbackIPv6() {
+  MAYBE_SKIP_IPV6;
+  CloseInClosedCallbackInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestSocketServerWaitIPv4() {
+  SocketServerWaitInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestSocketServerWaitIPv6() {
+  MAYBE_SKIP_IPV6;
+  SocketServerWaitInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestTcpIPv4() {
+  TcpInternal(kIPv4Loopback, kTcpInternalDataSize, -1);
+}
+
+void SocketTest::TestTcpIPv6() {
+  MAYBE_SKIP_IPV6;
+  TcpInternal(kIPv6Loopback, kTcpInternalDataSize, -1);
+}
+
+void SocketTest::TestSingleFlowControlCallbackIPv4() {
+  SingleFlowControlCallbackInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestSingleFlowControlCallbackIPv6() {
+  MAYBE_SKIP_IPV6;
+  SingleFlowControlCallbackInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestUdpIPv4() {
+  UdpInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestUdpIPv6() {
+  MAYBE_SKIP_IPV6;
+  UdpInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestUdpReadyToSendIPv4() {
+#if !defined(WEBRTC_MAC)
+  // TODO(ronghuawu): Enable this test on mac/ios.
+  UdpReadyToSend(kIPv4Loopback);
+#endif
+}
+
+void SocketTest::TestUdpReadyToSendIPv6() {
+#if defined(WEBRTC_WIN)
+  // TODO(ronghuawu): Enable this test (currently flakey) on mac and linux.
+  MAYBE_SKIP_IPV6;
+  UdpReadyToSend(kIPv6Loopback);
+#endif
+}
+
+void SocketTest::TestGetSetOptionsIPv4() {
+  GetSetOptionsInternal(kIPv4Loopback);
+}
+
+void SocketTest::TestGetSetOptionsIPv6() {
+  MAYBE_SKIP_IPV6;
+  GetSetOptionsInternal(kIPv6Loopback);
+}
+
+void SocketTest::TestSocketRecvTimestampIPv4() {
+  SocketRecvTimestamp(kIPv4Loopback);
+}
+
+void SocketTest::TestSocketRecvTimestampIPv6() {
+  MAYBE_SKIP_IPV6;
+  SocketRecvTimestamp(kIPv6Loopback);
+}
+
+// For unbound sockets, GetLocalAddress / GetRemoteAddress return AF_UNSPEC
+// values on Windows, but an empty address of the same family on Linux/MacOS X.
+bool IsUnspecOrEmptyIP(const IPAddress& address) {
+#if !defined(WEBRTC_WIN)
+  return IPIsAny(address);
+#else
+  return address.family() == AF_UNSPEC;
+#endif
+}
+
+void SocketTest::ConnectInternal(const IPAddress& loopback) {
+  StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create client.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+  EXPECT_EQ(AsyncSocket::CS_CLOSED, client->GetState());
+  EXPECT_PRED1(IsUnspecOrEmptyIP, client->GetLocalAddress().ipaddr());
+
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+  EXPECT_EQ(AsyncSocket::CS_CONNECTING, server->GetState());
+
+  // Ensure no pending server connections, since we haven't done anything yet.
+  EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
+  EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
+  EXPECT_TRUE(accept_addr.IsNil());
+
+  // Attempt connect to listening socket.
+  EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+  EXPECT_FALSE(client->GetLocalAddress().IsNil());
+  EXPECT_NE(server->GetLocalAddress(), client->GetLocalAddress());
+
+  // Client is connecting, outcome not yet determined.
+  EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState());
+  EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+
+  // Server has pending connection, accept it.
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
+  std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+  ASSERT_TRUE(accepted);
+  EXPECT_FALSE(accept_addr.IsNil());
+  EXPECT_EQ(accepted->GetRemoteAddress(), accept_addr);
+
+  // Connected from server perspective, check the addresses are correct.
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
+  EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress());
+  EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress());
+
+  // Connected from client perspective, check the addresses are correct.
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+  EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
+  EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+}
+
+void SocketTest::ConnectWithDnsLookupInternal(const IPAddress& loopback,
+                                              const std::string& host) {
+  StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create client.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+
+  // Attempt connect to listening socket.
+  SocketAddress dns_addr(server->GetLocalAddress());
+  dns_addr.SetIP(host);
+  EXPECT_EQ(0, client->Connect(dns_addr));
+  // TODO: Bind when doing DNS lookup.
+  //EXPECT_NE(kEmptyAddr, client->GetLocalAddress());  // Implicit Bind
+
+  // Client is connecting, outcome not yet determined.
+  EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState());
+  EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+
+  // Server has pending connection, accept it.
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
+  std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+  ASSERT_TRUE(accepted);
+  EXPECT_FALSE(accept_addr.IsNil());
+  EXPECT_EQ(accepted->GetRemoteAddress(), accept_addr);
+
+  // Connected from server perspective, check the addresses are correct.
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
+  EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress());
+  EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress());
+
+  // Connected from client perspective, check the addresses are correct.
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+  EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
+  EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+}
+
+void SocketTest::ConnectFailInternal(const IPAddress& loopback) {
+  StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create client.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+
+  // Create server, but don't listen yet.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+
+  // Attempt connect to a non-existent socket.
+  // We don't connect to the server socket created above, since on
+  // MacOS it takes about 75 seconds to get back an error!
+  SocketAddress bogus_addr(loopback, 65535);
+  EXPECT_EQ(0, client->Connect(bogus_addr));
+
+  // Wait for connection to fail (ECONNREFUSED).
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
+  EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_TRUE(sink.Check(client.get(), SSE_ERROR));
+  EXPECT_TRUE(client->GetRemoteAddress().IsNil());
+
+  // Should be no pending server connections.
+  EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
+  EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
+  EXPECT_EQ(IPAddress(), accept_addr.ipaddr());
+}
+
+void SocketTest::ConnectWithDnsLookupFailInternal(const IPAddress& loopback) {
+  StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create client.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+
+  // Create server, but don't listen yet.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+
+  // Attempt connect to a non-existent host.
+  // We don't connect to the server socket created above, since on
+  // MacOS it takes about 75 seconds to get back an error!
+  SocketAddress bogus_dns_addr("not-a-real-hostname", 65535);
+  EXPECT_EQ(0, client->Connect(bogus_dns_addr));
+
+  // Wait for connection to fail (EHOSTNOTFOUND).
+  bool dns_lookup_finished = false;
+  WAIT_(client->GetState() == AsyncSocket::CS_CLOSED, kTimeout,
+        dns_lookup_finished);
+  if (!dns_lookup_finished) {
+    LOG(LS_WARNING) << "Skipping test; DNS resolution took longer than 5 "
+                    << "seconds.";
+    return;
+  }
+
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
+  EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_TRUE(sink.Check(client.get(), SSE_ERROR));
+  EXPECT_TRUE(client->GetRemoteAddress().IsNil());
+  // Should be no pending server connections.
+  EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
+  EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
+  EXPECT_TRUE(accept_addr.IsNil());
+}
+
+void SocketTest::ConnectWithClosedSocketInternal(const IPAddress& loopback) {
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+
+  // Create a client and put in to CS_CLOSED state.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  EXPECT_EQ(0, client->Close());
+  EXPECT_EQ(AsyncSocket::CS_CLOSED, client->GetState());
+
+  // Connect() should reinitialize the socket, and put it in to CS_CONNECTING.
+  EXPECT_EQ(0, client->Connect(SocketAddress(server->GetLocalAddress())));
+  EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState());
+}
+
+void SocketTest::ConnectWhileNotClosedInternal(const IPAddress& loopback) {
+  // Create server and listen.
+  StreamSink sink;
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+  // Create client, connect.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  EXPECT_EQ(0, client->Connect(SocketAddress(server->GetLocalAddress())));
+  EXPECT_EQ(AsyncSocket::CS_CONNECTING, client->GetState());
+  // Try to connect again. Should fail, but not interfere with original attempt.
+  EXPECT_EQ(SOCKET_ERROR,
+            client->Connect(SocketAddress(server->GetLocalAddress())));
+
+  // Accept the original connection.
+  SocketAddress accept_addr;
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
+  std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+  ASSERT_TRUE(accepted);
+  EXPECT_FALSE(accept_addr.IsNil());
+
+  // Check the states and addresses.
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
+  EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress());
+  EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress());
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
+  EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
+  EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+
+  // Try to connect again, to an unresolved hostname.
+  // Shouldn't break anything.
+  EXPECT_EQ(SOCKET_ERROR,
+            client->Connect(SocketAddress("localhost",
+                                          server->GetLocalAddress().port())));
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, client->GetState());
+  EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
+  EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+}
+
+void SocketTest::ServerCloseDuringConnectInternal(const IPAddress& loopback) {
+  StreamSink sink;
+
+  // Create client.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+
+  // Attempt connect to listening socket.
+  EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+  // Close down the server while the socket is in the accept queue.
+  EXPECT_TRUE_WAIT(sink.Check(server.get(), SSE_READ), kTimeout);
+  server->Close();
+
+  // This should fail the connection for the client. Clean up.
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(client.get(), SSE_ERROR));
+  client->Close();
+}
+
+void SocketTest::ClientCloseDuringConnectInternal(const IPAddress& loopback) {
+  StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create client.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+
+  // Attempt connect to listening socket.
+  EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+  // Close down the client while the socket is in the accept queue.
+  EXPECT_TRUE_WAIT(sink.Check(server.get(), SSE_READ), kTimeout);
+  client->Close();
+
+  // The connection should still be able to be accepted.
+  std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+  ASSERT_TRUE(accepted);
+  sink.Monitor(accepted.get());
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
+
+  // The accepted socket should then close (possibly with err, timing-related)
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, accepted->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(accepted.get(), SSE_CLOSE) ||
+              sink.Check(accepted.get(), SSE_ERROR));
+
+  // The client should not get a close event.
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+}
+
+void SocketTest::ServerCloseInternal(const IPAddress& loopback) {
+  StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create client.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+
+  // Attempt connection.
+  EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+  // Accept connection.
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
+  std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+  ASSERT_TRUE(accepted);
+  sink.Monitor(accepted.get());
+
+  // Both sides are now connected.
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+  EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress());
+
+  // Send data to the client, and then close the connection.
+  EXPECT_EQ(1, accepted->Send("a", 1));
+  accepted->Close();
+  EXPECT_EQ(AsyncSocket::CS_CLOSED, accepted->GetState());
+
+  // Expect that the client is notified, and has not yet closed.
+  EXPECT_TRUE_WAIT(sink.Check(client.get(), SSE_READ), kTimeout);
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, client->GetState());
+
+  // Ensure the data can be read.
+  char buffer[10];
+  EXPECT_EQ(1, client->Recv(buffer, sizeof(buffer), nullptr));
+  EXPECT_EQ('a', buffer[0]);
+
+  // Now we should close, but the remote address will remain.
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(client.get(), SSE_CLOSE));
+  EXPECT_FALSE(client->GetRemoteAddress().IsAnyIP());
+
+  // The closer should not get a close signal.
+  EXPECT_FALSE(sink.Check(accepted.get(), SSE_CLOSE));
+  EXPECT_TRUE(accepted->GetRemoteAddress().IsNil());
+
+  // And the closee should only get a single signal.
+  Thread::Current()->ProcessMessages(0);
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+
+  // Close down the client and ensure all is good.
+  client->Close();
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+  EXPECT_TRUE(client->GetRemoteAddress().IsNil());
+}
+
+class SocketCloser : public sigslot::has_slots<> {
+ public:
+  void OnClose(AsyncSocket* socket, int error) {
+    socket->Close();  // Deleting here would blow up the vector of handlers
+                      // for the socket's signal.
+  }
+};
+
+void SocketTest::CloseInClosedCallbackInternal(const IPAddress& loopback) {
+  StreamSink sink;
+  SocketCloser closer;
+  SocketAddress accept_addr;
+
+  // Create client.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+  client->SignalCloseEvent.connect(&closer, &SocketCloser::OnClose);
+
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+
+  // Attempt connection.
+  EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+  // Accept connection.
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
+  std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+  ASSERT_TRUE(accepted);
+  sink.Monitor(accepted.get());
+
+  // Both sides are now connected.
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+  EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress());
+
+  // Send data to the client, and then close the connection.
+  accepted->Close();
+  EXPECT_EQ(AsyncSocket::CS_CLOSED, accepted->GetState());
+
+  // Expect that the client is notified, and has not yet closed.
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, client->GetState());
+
+  // Now we should be closed and invalidated
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(client.get(), SSE_CLOSE));
+  EXPECT_TRUE(Socket::CS_CLOSED == client->GetState());
+}
+
+class Sleeper : public MessageHandler {
+ public:
+  Sleeper() {}
+  void OnMessage(Message* msg) {
+    Thread::Current()->SleepMs(500);
+  }
+};
+
+void SocketTest::SocketServerWaitInternal(const IPAddress& loopback) {
+  StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create & connect server and client sockets.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+
+  EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
+
+  std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+  ASSERT_TRUE(accepted);
+  sink.Monitor(accepted.get());
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, accepted->GetState());
+  EXPECT_EQ(server->GetLocalAddress(), accepted->GetLocalAddress());
+  EXPECT_EQ(client->GetLocalAddress(), accepted->GetRemoteAddress());
+
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+  EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
+  EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+
+  // Do an i/o operation, triggering an eventual callback.
+  EXPECT_FALSE(sink.Check(accepted.get(), SSE_READ));
+  char buf[1024] = {0};
+
+  EXPECT_EQ(1024, client->Send(buf, 1024));
+  EXPECT_FALSE(sink.Check(accepted.get(), SSE_READ));
+
+  // Shouldn't signal when blocked in a thread Send, where process_io is false.
+  std::unique_ptr<Thread> thread(new Thread());
+  thread->Start();
+  Sleeper sleeper;
+  TypedMessageData<AsyncSocket*> data(client.get());
+  thread->Send(RTC_FROM_HERE, &sleeper, 0, &data);
+  EXPECT_FALSE(sink.Check(accepted.get(), SSE_READ));
+
+  // But should signal when process_io is true.
+  EXPECT_TRUE_WAIT((sink.Check(accepted.get(), SSE_READ)), kTimeout);
+  EXPECT_LT(0, accepted->Recv(buf, 1024, nullptr));
+}
+
+void SocketTest::TcpInternal(const IPAddress& loopback, size_t data_size,
+    ptrdiff_t max_send_size) {
+  StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create receiving client.
+  std::unique_ptr<AsyncSocket> receiver(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(receiver.get());
+
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+
+  // Attempt connection.
+  EXPECT_EQ(0, receiver->Connect(server->GetLocalAddress()));
+
+  // Accept connection which will be used for sending.
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
+  std::unique_ptr<AsyncSocket> sender(server->Accept(&accept_addr));
+  ASSERT_TRUE(sender);
+  sink.Monitor(sender.get());
+
+  // Both sides are now connected.
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, receiver->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(receiver.get(), SSE_OPEN));
+  EXPECT_EQ(receiver->GetRemoteAddress(), sender->GetLocalAddress());
+  EXPECT_EQ(sender->GetRemoteAddress(), receiver->GetLocalAddress());
+
+  // Create test data.
+  rtc::Buffer send_buffer(0, data_size);
+  rtc::Buffer recv_buffer(0, data_size);
+  for (size_t i = 0; i < data_size; ++i) {
+    char ch = static_cast<char>(i % 256);
+    send_buffer.AppendData(&ch, sizeof(ch));
+  }
+  rtc::Buffer recved_data(0, data_size);
+
+  // Send and receive a bunch of data.
+  size_t sent_size = 0;
+  bool writable = true;
+  bool send_called = false;
+  bool readable = false;
+  bool recv_called = false;
+  while (recv_buffer.size() < send_buffer.size()) {
+    // Send as much as we can while we're cleared to send.
+    while (writable && sent_size < send_buffer.size()) {
+      int unsent_size = static_cast<int>(send_buffer.size() - sent_size);
+      int sent = sender->Send(send_buffer.data() + sent_size, unsent_size);
+      if (!send_called) {
+        // The first Send() after connecting or getting writability should
+        // succeed and send some data.
+        EXPECT_GT(sent, 0);
+        send_called = true;
+      }
+      if (sent >= 0) {
+        EXPECT_LE(sent, unsent_size);
+        sent_size += sent;
+        if (max_send_size >= 0) {
+          EXPECT_LE(static_cast<ptrdiff_t>(sent), max_send_size);
+          if (sent < unsent_size) {
+            // If max_send_size is limiting the amount to send per call such
+            // that the sent amount is less than the unsent amount, we simulate
+            // that the socket is no longer writable.
+            writable = false;
+          }
+        }
+      } else {
+        ASSERT_TRUE(sender->IsBlocking());
+        writable = false;
+      }
+    }
+
+    // Read all the sent data.
+    while (recv_buffer.size() < sent_size) {
+      if (!readable) {
+        // Wait until data is available.
+        EXPECT_TRUE_WAIT(sink.Check(receiver.get(), SSE_READ), kTimeout);
+        readable = true;
+        recv_called = false;
+      }
+
+      // Receive as much as we can get in a single recv call.
+      int recved_size = receiver->Recv(recved_data.data(), data_size, nullptr);
+
+      if (!recv_called) {
+        // The first Recv() after getting readability should succeed and receive
+        // some data.
+        // TODO: The following line is disabled due to flakey pulse
+        //     builds.  Re-enable if/when possible.
+        // EXPECT_GT(recved_size, 0);
+        recv_called = true;
+      }
+      if (recved_size >= 0) {
+        EXPECT_LE(static_cast<size_t>(recved_size),
+            sent_size - recv_buffer.size());
+        recv_buffer.AppendData(recved_data.data(), recved_size);
+      } else {
+        ASSERT_TRUE(receiver->IsBlocking());
+        readable = false;
+      }
+    }
+
+    // Once all that we've sent has been received, expect to be able to send
+    // again.
+    if (!writable) {
+      ASSERT_TRUE_WAIT(sink.Check(sender.get(), SSE_WRITE), kTimeout);
+      writable = true;
+      send_called = false;
+    }
+  }
+
+  // The received data matches the sent data.
+  EXPECT_EQ(data_size, sent_size);
+  EXPECT_EQ(data_size, recv_buffer.size());
+  EXPECT_EQ(recv_buffer, send_buffer);
+
+  // Close down.
+  sender->Close();
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, receiver->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(receiver.get(), SSE_CLOSE));
+  receiver->Close();
+}
+
+void SocketTest::SingleFlowControlCallbackInternal(const IPAddress& loopback) {
+  StreamSink sink;
+  SocketAddress accept_addr;
+
+  // Create client.
+  std::unique_ptr<AsyncSocket> client(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(client.get());
+
+  // Create server and listen.
+  std::unique_ptr<AsyncSocket> server(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+  sink.Monitor(server.get());
+  EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+  EXPECT_EQ(0, server->Listen(5));
+
+  // Attempt connection.
+  EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+  // Accept connection.
+  EXPECT_TRUE_WAIT((sink.Check(server.get(), SSE_READ)), kTimeout);
+  std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+  ASSERT_TRUE(accepted);
+  sink.Monitor(accepted.get());
+
+  // Both sides are now connected.
+  EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
+  EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
+  EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+  EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress());
+
+  // Expect a writable callback from the connect.
+  EXPECT_TRUE_WAIT(sink.Check(accepted.get(), SSE_WRITE), kTimeout);
+
+  // Fill the socket buffer.
+  char buf[1024 * 16] = {0};
+  int sends = 0;
+  while (++sends && accepted->Send(&buf, arraysize(buf)) != -1) {}
+  EXPECT_TRUE(accepted->IsBlocking());
+
+  // Wait until data is available.
+  EXPECT_TRUE_WAIT(sink.Check(client.get(), SSE_READ), kTimeout);
+
+  // Pull data.
+  for (int i = 0; i < sends; ++i) {
+    client->Recv(buf, arraysize(buf), nullptr);
+  }
+
+  // Expect at least one additional writable callback.
+  EXPECT_TRUE_WAIT(sink.Check(accepted.get(), SSE_WRITE), kTimeout);
+
+  // Adding data in response to the writeable callback shouldn't cause infinite
+  // callbacks.
+  int extras = 0;
+  for (int i = 0; i < 100; ++i) {
+    accepted->Send(&buf, arraysize(buf));
+    rtc::Thread::Current()->ProcessMessages(1);
+    if (sink.Check(accepted.get(), SSE_WRITE)) {
+      extras++;
+    }
+  }
+  EXPECT_LT(extras, 2);
+
+  // Close down.
+  accepted->Close();
+  client->Close();
+}
+
+void SocketTest::UdpInternal(const IPAddress& loopback) {
+  SocketAddress empty = EmptySocketAddressWithFamily(loopback.family());
+  // Test basic bind and connect behavior.
+  AsyncSocket* socket =
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_DGRAM);
+  EXPECT_EQ(AsyncSocket::CS_CLOSED, socket->GetState());
+  EXPECT_EQ(0, socket->Bind(SocketAddress(loopback, 0)));
+  SocketAddress addr1 = socket->GetLocalAddress();
+  EXPECT_EQ(0, socket->Connect(addr1));
+  EXPECT_EQ(AsyncSocket::CS_CONNECTED, socket->GetState());
+  socket->Close();
+  EXPECT_EQ(AsyncSocket::CS_CLOSED, socket->GetState());
+  delete socket;
+
+  // Test send/receive behavior.
+  std::unique_ptr<TestClient> client1(
+      new TestClient(WrapUnique(AsyncUDPSocket::Create(ss_, addr1))));
+  std::unique_ptr<TestClient> client2(
+      new TestClient(WrapUnique(AsyncUDPSocket::Create(ss_, empty))));
+
+  SocketAddress addr2;
+  EXPECT_EQ(3, client2->SendTo("foo", 3, addr1));
+  EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &addr2));
+
+  SocketAddress addr3;
+  EXPECT_EQ(6, client1->SendTo("bizbaz", 6, addr2));
+  EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &addr3));
+  EXPECT_EQ(addr3, addr1);
+  // TODO: figure out what the intent is here
+  for (int i = 0; i < 10; ++i) {
+    client2.reset(
+        new TestClient(WrapUnique(AsyncUDPSocket::Create(ss_, empty))));
+
+    SocketAddress addr4;
+    EXPECT_EQ(3, client2->SendTo("foo", 3, addr1));
+    EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &addr4));
+    EXPECT_EQ(addr4.ipaddr(), addr2.ipaddr());
+
+    SocketAddress addr5;
+    EXPECT_EQ(6, client1->SendTo("bizbaz", 6, addr4));
+    EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &addr5));
+    EXPECT_EQ(addr5, addr1);
+
+    addr2 = addr4;
+  }
+}
+
+void SocketTest::UdpReadyToSend(const IPAddress& loopback) {
+  SocketAddress empty = EmptySocketAddressWithFamily(loopback.family());
+  // RFC 5737 - The blocks 192.0.2.0/24 (TEST-NET-1) ... are provided for use in
+  // documentation.
+  // RFC 3849 - 2001:DB8::/32 as a documentation-only prefix.
+  std::string dest = (loopback.family() == AF_INET6) ?
+      "2001:db8::1" : "192.0.2.0";
+  SocketAddress test_addr(dest, 2345);
+
+  // Test send
+  std::unique_ptr<TestClient> client(
+      new TestClient(WrapUnique(AsyncUDPSocket::Create(ss_, empty))));
+  int test_packet_size = 1200;
+  std::unique_ptr<char[]> test_packet(new char[test_packet_size]);
+  // Init the test packet just to avoid memcheck warning.
+  memset(test_packet.get(), 0, test_packet_size);
+  // Set the send buffer size to the same size as the test packet to have a
+  // better chance to get EWOULDBLOCK.
+  int send_buffer_size = test_packet_size;
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+  send_buffer_size /= 2;
+#endif
+  client->SetOption(rtc::Socket::OPT_SNDBUF, send_buffer_size);
+
+  int error = 0;
+  uint32_t start_ms = Time();
+  int sent_packet_num = 0;
+  int expected_error = EWOULDBLOCK;
+  while (start_ms + kTimeout > Time()) {
+    int ret = client->SendTo(test_packet.get(), test_packet_size, test_addr);
+    ++sent_packet_num;
+    if (ret != test_packet_size) {
+      error = client->GetError();
+      if (error == expected_error) {
+        LOG(LS_INFO) << "Got expected error code after sending "
+                     << sent_packet_num << " packets.";
+        break;
+      }
+    }
+  }
+  EXPECT_EQ(expected_error, error);
+  EXPECT_FALSE(client->ready_to_send());
+  EXPECT_TRUE_WAIT(client->ready_to_send(), kTimeout);
+  LOG(LS_INFO) << "Got SignalReadyToSend";
+}
+
+void SocketTest::GetSetOptionsInternal(const IPAddress& loopback) {
+  std::unique_ptr<AsyncSocket> socket(
+      ss_->CreateAsyncSocket(loopback.family(), SOCK_DGRAM));
+  socket->Bind(SocketAddress(loopback, 0));
+
+  // Check SNDBUF/RCVBUF.
+  const int desired_size = 12345;
+#if defined(WEBRTC_LINUX)
+  // Yes, really.  It's in the kernel source.
+  const int expected_size = desired_size * 2;
+#else   // !WEBRTC_LINUX
+  const int expected_size = desired_size;
+#endif  // !WEBRTC_LINUX
+  int recv_size = 0;
+  int send_size = 0;
+  // get the initial sizes
+  ASSERT_NE(-1, socket->GetOption(Socket::OPT_RCVBUF, &recv_size));
+  ASSERT_NE(-1, socket->GetOption(Socket::OPT_SNDBUF, &send_size));
+  // set our desired sizes
+  ASSERT_NE(-1, socket->SetOption(Socket::OPT_RCVBUF, desired_size));
+  ASSERT_NE(-1, socket->SetOption(Socket::OPT_SNDBUF, desired_size));
+  // get the sizes again
+  ASSERT_NE(-1, socket->GetOption(Socket::OPT_RCVBUF, &recv_size));
+  ASSERT_NE(-1, socket->GetOption(Socket::OPT_SNDBUF, &send_size));
+  // make sure they are right
+  ASSERT_EQ(expected_size, recv_size);
+  ASSERT_EQ(expected_size, send_size);
+
+  // Check that we can't set NODELAY on a UDP socket.
+  int current_nd, desired_nd = 1;
+  ASSERT_EQ(-1, socket->GetOption(Socket::OPT_NODELAY, &current_nd));
+  ASSERT_EQ(-1, socket->SetOption(Socket::OPT_NODELAY, desired_nd));
+}
+
+void SocketTest::SocketRecvTimestamp(const IPAddress& loopback) {
+  std::unique_ptr<Socket> socket(
+      ss_->CreateSocket(loopback.family(), SOCK_DGRAM));
+  EXPECT_EQ(0, socket->Bind(SocketAddress(loopback, 0)));
+  SocketAddress address = socket->GetLocalAddress();
+
+  int64_t send_time_1 = TimeMicros();
+  socket->SendTo("foo", 3, address);
+  int64_t recv_timestamp_1;
+  char buffer[3];
+  socket->RecvFrom(buffer, 3, nullptr, &recv_timestamp_1);
+  EXPECT_GT(recv_timestamp_1, -1);
+
+  const int64_t kTimeBetweenPacketsMs = 100;
+  Thread::SleepMs(kTimeBetweenPacketsMs);
+
+  int64_t send_time_2 = TimeMicros();
+  socket->SendTo("bar", 3, address);
+  int64_t recv_timestamp_2;
+  socket->RecvFrom(buffer, 3, nullptr, &recv_timestamp_2);
+
+  int64_t system_time_diff = send_time_2 - send_time_1;
+  int64_t recv_timestamp_diff = recv_timestamp_2 - recv_timestamp_1;
+  // Compare against the system time at the point of sending, because
+  // SleepMs may not sleep for exactly the requested time.
+  EXPECT_NEAR(system_time_diff, recv_timestamp_diff, 10000);
+}
+
+}  // namespace rtc
diff --git a/base/socket_unittest.h b/base/socket_unittest.h
index f6769f9..8172edd 100644
--- a/base/socket_unittest.h
+++ b/base/socket_unittest.h
@@ -11,9 +11,90 @@
 #ifndef WEBRTC_BASE_SOCKET_UNITTEST_H_
 #define WEBRTC_BASE_SOCKET_UNITTEST_H_
 
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/thread.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/socket_unittest.h"
+namespace rtc {
+
+// Generic socket tests, to be used when testing individual socketservers.
+// Derive your specific test class from SocketTest, install your
+// socketserver, and call the SocketTest test methods.
+class SocketTest : public testing::Test {
+ protected:
+  SocketTest() : kIPv4Loopback(INADDR_LOOPBACK),
+                 kIPv6Loopback(in6addr_loopback),
+                 ss_(nullptr) {}
+  virtual void SetUp() { ss_ = Thread::Current()->socketserver(); }
+  void TestConnectIPv4();
+  void TestConnectIPv6();
+  void TestConnectWithDnsLookupIPv4();
+  void TestConnectWithDnsLookupIPv6();
+  void TestConnectFailIPv4();
+  void TestConnectFailIPv6();
+  void TestConnectWithDnsLookupFailIPv4();
+  void TestConnectWithDnsLookupFailIPv6();
+  void TestConnectWithClosedSocketIPv4();
+  void TestConnectWithClosedSocketIPv6();
+  void TestConnectWhileNotClosedIPv4();
+  void TestConnectWhileNotClosedIPv6();
+  void TestServerCloseDuringConnectIPv4();
+  void TestServerCloseDuringConnectIPv6();
+  void TestClientCloseDuringConnectIPv4();
+  void TestClientCloseDuringConnectIPv6();
+  void TestServerCloseIPv4();
+  void TestServerCloseIPv6();
+  void TestCloseInClosedCallbackIPv4();
+  void TestCloseInClosedCallbackIPv6();
+  void TestSocketServerWaitIPv4();
+  void TestSocketServerWaitIPv6();
+  void TestTcpIPv4();
+  void TestTcpIPv6();
+  void TestSingleFlowControlCallbackIPv4();
+  void TestSingleFlowControlCallbackIPv6();
+  void TestUdpIPv4();
+  void TestUdpIPv6();
+  void TestUdpReadyToSendIPv4();
+  void TestUdpReadyToSendIPv6();
+  void TestGetSetOptionsIPv4();
+  void TestGetSetOptionsIPv6();
+  void TestSocketRecvTimestampIPv4();
+  void TestSocketRecvTimestampIPv6();
+
+  static const int kTimeout = 5000;  // ms
+  const IPAddress kIPv4Loopback;
+  const IPAddress kIPv6Loopback;
+
+ protected:
+  void TcpInternal(const IPAddress& loopback, size_t data_size,
+      ptrdiff_t max_send_size);
+
+ private:
+  void ConnectInternal(const IPAddress& loopback);
+  void ConnectWithDnsLookupInternal(const IPAddress& loopback,
+                                    const std::string& host);
+  void ConnectFailInternal(const IPAddress& loopback);
+
+  void ConnectWithDnsLookupFailInternal(const IPAddress& loopback);
+  void ConnectWithClosedSocketInternal(const IPAddress& loopback);
+  void ConnectWhileNotClosedInternal(const IPAddress& loopback);
+  void ServerCloseDuringConnectInternal(const IPAddress& loopback);
+  void ClientCloseDuringConnectInternal(const IPAddress& loopback);
+  void ServerCloseInternal(const IPAddress& loopback);
+  void CloseInClosedCallbackInternal(const IPAddress& loopback);
+  void SocketServerWaitInternal(const IPAddress& loopback);
+  void SingleFlowControlCallbackInternal(const IPAddress& loopback);
+  void UdpInternal(const IPAddress& loopback);
+  void UdpReadyToSend(const IPAddress& loopback);
+  void GetSetOptionsInternal(const IPAddress& loopback);
+  void SocketRecvTimestamp(const IPAddress& loopback);
+
+  SocketServer* ss_;
+};
+
+// For unbound sockets, GetLocalAddress / GetRemoteAddress return AF_UNSPEC
+// values on Windows, but an empty address of the same family on Linux/MacOS X.
+bool IsUnspecOrEmptyIP(const IPAddress& address);
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SOCKET_UNITTEST_H_
diff --git a/base/socketadapters.cc b/base/socketadapters.cc
new file mode 100644
index 0000000..060029c
--- /dev/null
+++ b/base/socketadapters.cc
@@ -0,0 +1,848 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include <time.h>
+#include <errno.h>
+
+#if defined(WEBRTC_WIN)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define SECURITY_WIN32
+#include <security.h>
+#endif
+
+#include <algorithm>
+
+#include "webrtc/base/bytebuffer.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/httpcommon.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/socketadapters.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+
+BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t size)
+    : AsyncSocketAdapter(socket), buffer_size_(size),
+      data_len_(0), buffering_(false) {
+  buffer_ = new char[buffer_size_];
+}
+
+BufferedReadAdapter::~BufferedReadAdapter() {
+  delete [] buffer_;
+}
+
+int BufferedReadAdapter::Send(const void *pv, size_t cb) {
+  if (buffering_) {
+    // TODO: Spoof error better; Signal Writeable
+    socket_->SetError(EWOULDBLOCK);
+    return -1;
+  }
+  return AsyncSocketAdapter::Send(pv, cb);
+}
+
+int BufferedReadAdapter::Recv(void* pv, size_t cb, int64_t* timestamp) {
+  if (buffering_) {
+    socket_->SetError(EWOULDBLOCK);
+    return -1;
+  }
+
+  size_t read = 0;
+
+  if (data_len_) {
+    read = std::min(cb, data_len_);
+    memcpy(pv, buffer_, read);
+    data_len_ -= read;
+    if (data_len_ > 0) {
+      memmove(buffer_, buffer_ + read, data_len_);
+    }
+    pv = static_cast<char *>(pv) + read;
+    cb -= read;
+  }
+
+  // FIX: If cb == 0, we won't generate another read event
+
+  int res = AsyncSocketAdapter::Recv(pv, cb, timestamp);
+  if (res >= 0) {
+    // Read from socket and possibly buffer; return combined length
+    return res + static_cast<int>(read);
+  }
+
+  if (read > 0) {
+    // Failed to read from socket, but still read something from buffer
+    return static_cast<int>(read);
+  }
+
+  // Didn't read anything; return error from socket
+  return res;
+}
+
+void BufferedReadAdapter::BufferInput(bool on) {
+  buffering_ = on;
+}
+
+void BufferedReadAdapter::OnReadEvent(AsyncSocket * socket) {
+  RTC_DCHECK(socket == socket_);
+
+  if (!buffering_) {
+    AsyncSocketAdapter::OnReadEvent(socket);
+    return;
+  }
+
+  if (data_len_ >= buffer_size_) {
+    LOG(INFO) << "Input buffer overflow";
+    RTC_NOTREACHED();
+    data_len_ = 0;
+  }
+
+  int len =
+      socket_->Recv(buffer_ + data_len_, buffer_size_ - data_len_, nullptr);
+  if (len < 0) {
+    // TODO: Do something better like forwarding the error to the user.
+    LOG_ERR(INFO) << "Recv";
+    return;
+  }
+
+  data_len_ += len;
+
+  ProcessInput(buffer_, &data_len_);
+}
+
+AsyncProxyServerSocket::AsyncProxyServerSocket(AsyncSocket* socket,
+                                               size_t buffer_size)
+    : BufferedReadAdapter(socket, buffer_size) {
+}
+
+AsyncProxyServerSocket::~AsyncProxyServerSocket() = default;
+
+///////////////////////////////////////////////////////////////////////////////
+
+// This is a SSL v2 CLIENT_HELLO message.
+// TODO: Should this have a session id? The response doesn't have a
+// certificate, so the hello should have a session id.
+static const uint8_t kSslClientHello[] = {
+    0x80, 0x46,                                            // msg len
+    0x01,                                                  // CLIENT_HELLO
+    0x03, 0x01,                                            // SSL 3.1
+    0x00, 0x2d,                                            // ciphersuite len
+    0x00, 0x00,                                            // session id len
+    0x00, 0x10,                                            // challenge len
+    0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x07, 0x00, 0xc0,  // ciphersuites
+    0x06, 0x00, 0x40, 0x02, 0x00, 0x80, 0x04, 0x00, 0x80,  //
+    0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x0a,  //
+    0x00, 0xfe, 0xfe, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64,  //
+    0x00, 0x00, 0x62, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06,  //
+    0x1f, 0x17, 0x0c, 0xa6, 0x2f, 0x00, 0x78, 0xfc,        // challenge
+    0x46, 0x55, 0x2e, 0xb1, 0x83, 0x39, 0xf1, 0xea         //
+};
+
+// This is a TLSv1 SERVER_HELLO message.
+static const uint8_t kSslServerHello[] = {
+    0x16,                                            // handshake message
+    0x03, 0x01,                                      // SSL 3.1
+    0x00, 0x4a,                                      // message len
+    0x02,                                            // SERVER_HELLO
+    0x00, 0x00, 0x46,                                // handshake len
+    0x03, 0x01,                                      // SSL 3.1
+    0x42, 0x85, 0x45, 0xa7, 0x27, 0xa9, 0x5d, 0xa0,  // server random
+    0xb3, 0xc5, 0xe7, 0x53, 0xda, 0x48, 0x2b, 0x3f,  //
+    0xc6, 0x5a, 0xca, 0x89, 0xc1, 0x58, 0x52, 0xa1,  //
+    0x78, 0x3c, 0x5b, 0x17, 0x46, 0x00, 0x85, 0x3f,  //
+    0x20,                                            // session id len
+    0x0e, 0xd3, 0x06, 0x72, 0x5b, 0x5b, 0x1b, 0x5f,  // session id
+    0x15, 0xac, 0x13, 0xf9, 0x88, 0x53, 0x9d, 0x9b,  //
+    0xe8, 0x3d, 0x7b, 0x0c, 0x30, 0x32, 0x6e, 0x38,  //
+    0x4d, 0xa2, 0x75, 0x57, 0x41, 0x6c, 0x34, 0x5c,  //
+    0x00, 0x04,                                      // RSA/RC4-128/MD5
+    0x00                                             // null compression
+};
+
+AsyncSSLSocket::AsyncSSLSocket(AsyncSocket* socket)
+    : BufferedReadAdapter(socket, 1024) {
+}
+
+int AsyncSSLSocket::Connect(const SocketAddress& addr) {
+  // Begin buffering before we connect, so that there isn't a race condition
+  // between potential senders and receiving the OnConnectEvent signal
+  BufferInput(true);
+  return BufferedReadAdapter::Connect(addr);
+}
+
+void AsyncSSLSocket::OnConnectEvent(AsyncSocket * socket) {
+  RTC_DCHECK(socket == socket_);
+  // TODO: we could buffer output too...
+  const int res = DirectSend(kSslClientHello, sizeof(kSslClientHello));
+  RTC_DCHECK_EQ(sizeof(kSslClientHello), res);
+}
+
+void AsyncSSLSocket::ProcessInput(char* data, size_t* len) {
+  if (*len < sizeof(kSslServerHello))
+    return;
+
+  if (memcmp(kSslServerHello, data, sizeof(kSslServerHello)) != 0) {
+    Close();
+    SignalCloseEvent(this, 0);  // TODO: error code?
+    return;
+  }
+
+  *len -= sizeof(kSslServerHello);
+  if (*len > 0) {
+    memmove(data, data + sizeof(kSslServerHello), *len);
+  }
+
+  bool remainder = (*len > 0);
+  BufferInput(false);
+  SignalConnectEvent(this);
+
+  // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
+  if (remainder)
+    SignalReadEvent(this);
+}
+
+AsyncSSLServerSocket::AsyncSSLServerSocket(AsyncSocket* socket)
+     : BufferedReadAdapter(socket, 1024) {
+  BufferInput(true);
+}
+
+void AsyncSSLServerSocket::ProcessInput(char* data, size_t* len) {
+  // We only accept client hello messages.
+  if (*len < sizeof(kSslClientHello)) {
+    return;
+  }
+
+  if (memcmp(kSslClientHello, data, sizeof(kSslClientHello)) != 0) {
+    Close();
+    SignalCloseEvent(this, 0);
+    return;
+  }
+
+  *len -= sizeof(kSslClientHello);
+
+  // Clients should not send more data until the handshake is completed.
+  RTC_DCHECK(*len == 0);
+
+  // Send a server hello back to the client.
+  DirectSend(kSslServerHello, sizeof(kSslServerHello));
+
+  // Handshake completed for us, redirect input to our parent.
+  BufferInput(false);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket,
+                                             const std::string& user_agent,
+                                             const SocketAddress& proxy,
+                                             const std::string& username,
+                                             const CryptString& password)
+  : BufferedReadAdapter(socket, 1024), proxy_(proxy), agent_(user_agent),
+    user_(username), pass_(password), force_connect_(false), state_(PS_ERROR),
+    context_(0) {
+}
+
+AsyncHttpsProxySocket::~AsyncHttpsProxySocket() {
+  delete context_;
+}
+
+int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) {
+  int ret;
+  LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect("
+                  << proxy_.ToSensitiveString() << ")";
+  dest_ = addr;
+  state_ = PS_INIT;
+  if (ShouldIssueConnect()) {
+    BufferInput(true);
+  }
+  ret = BufferedReadAdapter::Connect(proxy_);
+  // TODO: Set state_ appropriately if Connect fails.
+  return ret;
+}
+
+SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const {
+  return dest_;
+}
+
+int AsyncHttpsProxySocket::Close() {
+  headers_.clear();
+  state_ = PS_ERROR;
+  dest_.Clear();
+  delete context_;
+  context_ = nullptr;
+  return BufferedReadAdapter::Close();
+}
+
+Socket::ConnState AsyncHttpsProxySocket::GetState() const {
+  if (state_ < PS_TUNNEL) {
+    return CS_CONNECTING;
+  } else if (state_ == PS_TUNNEL) {
+    return CS_CONNECTED;
+  } else {
+    return CS_CLOSED;
+  }
+}
+
+void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket * socket) {
+  LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent";
+  if (!ShouldIssueConnect()) {
+    state_ = PS_TUNNEL;
+    BufferedReadAdapter::OnConnectEvent(socket);
+    return;
+  }
+  SendRequest();
+}
+
+void AsyncHttpsProxySocket::OnCloseEvent(AsyncSocket * socket, int err) {
+  LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnCloseEvent(" << err << ")";
+  if ((state_ == PS_WAIT_CLOSE) && (err == 0)) {
+    state_ = PS_ERROR;
+    Connect(dest_);
+  } else {
+    BufferedReadAdapter::OnCloseEvent(socket, err);
+  }
+}
+
+void AsyncHttpsProxySocket::ProcessInput(char* data, size_t* len) {
+  size_t start = 0;
+  for (size_t pos = start; state_ < PS_TUNNEL && pos < *len;) {
+    if (state_ == PS_SKIP_BODY) {
+      size_t consume = std::min(*len - pos, content_length_);
+      pos += consume;
+      start = pos;
+      content_length_ -= consume;
+      if (content_length_ == 0) {
+        EndResponse();
+      }
+      continue;
+    }
+
+    if (data[pos++] != '\n')
+      continue;
+
+    size_t len = pos - start - 1;
+    if ((len > 0) && (data[start + len - 1] == '\r'))
+      --len;
+
+    data[start + len] = 0;
+    ProcessLine(data + start, len);
+    start = pos;
+  }
+
+  *len -= start;
+  if (*len > 0) {
+    memmove(data, data + start, *len);
+  }
+
+  if (state_ != PS_TUNNEL)
+    return;
+
+  bool remainder = (*len > 0);
+  BufferInput(false);
+  SignalConnectEvent(this);
+
+  // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
+  if (remainder)
+    SignalReadEvent(this);  // TODO: signal this??
+}
+
+bool AsyncHttpsProxySocket::ShouldIssueConnect() const {
+  // TODO: Think about whether a more sophisticated test
+  // than dest port == 80 is needed.
+  return force_connect_ || (dest_.port() != 80);
+}
+
+void AsyncHttpsProxySocket::SendRequest() {
+  std::stringstream ss;
+  ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n";
+  ss << "User-Agent: " << agent_ << "\r\n";
+  ss << "Host: " << dest_.HostAsURIString() << "\r\n";
+  ss << "Content-Length: 0\r\n";
+  ss << "Proxy-Connection: Keep-Alive\r\n";
+  ss << headers_;
+  ss << "\r\n";
+  std::string str = ss.str();
+  DirectSend(str.c_str(), str.size());
+  state_ = PS_LEADER;
+  expect_close_ = true;
+  content_length_ = 0;
+  headers_.clear();
+
+  LOG(LS_VERBOSE) << "AsyncHttpsProxySocket >> " << str;
+}
+
+void AsyncHttpsProxySocket::ProcessLine(char * data, size_t len) {
+  LOG(LS_VERBOSE) << "AsyncHttpsProxySocket << " << data;
+
+  if (len == 0) {
+    if (state_ == PS_TUNNEL_HEADERS) {
+      state_ = PS_TUNNEL;
+    } else if (state_ == PS_ERROR_HEADERS) {
+      Error(defer_error_);
+      return;
+    } else if (state_ == PS_SKIP_HEADERS) {
+      if (content_length_) {
+        state_ = PS_SKIP_BODY;
+      } else {
+        EndResponse();
+        return;
+      }
+    } else {
+      static bool report = false;
+      if (!unknown_mechanisms_.empty() && !report) {
+        report = true;
+        std::string msg(
+          "Unable to connect to the Google Talk service due to an incompatibility "
+          "with your proxy.\r\nPlease help us resolve this issue by submitting the "
+          "following information to us using our technical issue submission form "
+          "at:\r\n\r\n"
+          "http://www.google.com/support/talk/bin/request.py\r\n\r\n"
+          "We apologize for the inconvenience.\r\n\r\n"
+          "Information to submit to Google: "
+          );
+        //std::string msg("Please report the following information to foo@bar.com:\r\nUnknown methods: ");
+        msg.append(unknown_mechanisms_);
+#if defined(WEBRTC_WIN)
+        MessageBoxA(0, msg.c_str(), "Oops!", MB_OK);
+#endif
+#if defined(WEBRTC_POSIX)
+        // TODO: Raise a signal so the UI can be separated.
+        LOG(LS_ERROR) << "Oops!\n\n" << msg;
+#endif
+      }
+      // Unexpected end of headers
+      Error(0);
+      return;
+    }
+  } else if (state_ == PS_LEADER) {
+    unsigned int code;
+    if (sscanf(data, "HTTP/%*u.%*u %u", &code) != 1) {
+      Error(0);
+      return;
+    }
+    switch (code) {
+    case 200:
+      // connection good!
+      state_ = PS_TUNNEL_HEADERS;
+      return;
+#if defined(HTTP_STATUS_PROXY_AUTH_REQ) && (HTTP_STATUS_PROXY_AUTH_REQ != 407)
+#error Wrong code for HTTP_STATUS_PROXY_AUTH_REQ
+#endif
+    case 407:  // HTTP_STATUS_PROXY_AUTH_REQ
+      state_ = PS_AUTHENTICATE;
+      return;
+    default:
+      defer_error_ = 0;
+      state_ = PS_ERROR_HEADERS;
+      return;
+    }
+  } else if ((state_ == PS_AUTHENTICATE)
+             && (_strnicmp(data, "Proxy-Authenticate:", 19) == 0)) {
+    std::string response, auth_method;
+    switch (HttpAuthenticate(data + 19, len - 19,
+                             proxy_, "CONNECT", "/",
+                             user_, pass_, context_, response, auth_method)) {
+    case HAR_IGNORE:
+      LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method;
+      if (!unknown_mechanisms_.empty())
+        unknown_mechanisms_.append(", ");
+      unknown_mechanisms_.append(auth_method);
+      break;
+    case HAR_RESPONSE:
+      headers_ = "Proxy-Authorization: ";
+      headers_.append(response);
+      headers_.append("\r\n");
+      state_ = PS_SKIP_HEADERS;
+      unknown_mechanisms_.clear();
+      break;
+    case HAR_CREDENTIALS:
+      defer_error_ = SOCKET_EACCES;
+      state_ = PS_ERROR_HEADERS;
+      unknown_mechanisms_.clear();
+      break;
+    case HAR_ERROR:
+      defer_error_ = 0;
+      state_ = PS_ERROR_HEADERS;
+      unknown_mechanisms_.clear();
+      break;
+    }
+  } else if (_strnicmp(data, "Content-Length:", 15) == 0) {
+    content_length_ = strtoul(data + 15, 0, 0);
+  } else if (_strnicmp(data, "Proxy-Connection: Keep-Alive", 28) == 0) {
+    expect_close_ = false;
+    /*
+  } else if (_strnicmp(data, "Connection: close", 17) == 0) {
+    expect_close_ = true;
+    */
+  }
+}
+
+void AsyncHttpsProxySocket::EndResponse() {
+  if (!expect_close_) {
+    SendRequest();
+    return;
+  }
+
+  // No point in waiting for the server to close... let's close now
+  // TODO: Refactor out PS_WAIT_CLOSE
+  state_ = PS_WAIT_CLOSE;
+  BufferedReadAdapter::Close();
+  OnCloseEvent(this, 0);
+}
+
+void AsyncHttpsProxySocket::Error(int error) {
+  BufferInput(false);
+  Close();
+  SetError(error);
+  SignalCloseEvent(this, error);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AsyncSocksProxySocket::AsyncSocksProxySocket(AsyncSocket* socket,
+                                             const SocketAddress& proxy,
+                                             const std::string& username,
+                                             const CryptString& password)
+    : BufferedReadAdapter(socket, 1024), state_(SS_ERROR), proxy_(proxy),
+      user_(username), pass_(password) {
+}
+
+AsyncSocksProxySocket::~AsyncSocksProxySocket() = default;
+
+int AsyncSocksProxySocket::Connect(const SocketAddress& addr) {
+  int ret;
+  dest_ = addr;
+  state_ = SS_INIT;
+  BufferInput(true);
+  ret = BufferedReadAdapter::Connect(proxy_);
+  // TODO: Set state_ appropriately if Connect fails.
+  return ret;
+}
+
+SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const {
+  return dest_;
+}
+
+int AsyncSocksProxySocket::Close() {
+  state_ = SS_ERROR;
+  dest_.Clear();
+  return BufferedReadAdapter::Close();
+}
+
+Socket::ConnState AsyncSocksProxySocket::GetState() const {
+  if (state_ < SS_TUNNEL) {
+    return CS_CONNECTING;
+  } else if (state_ == SS_TUNNEL) {
+    return CS_CONNECTED;
+  } else {
+    return CS_CLOSED;
+  }
+}
+
+void AsyncSocksProxySocket::OnConnectEvent(AsyncSocket* socket) {
+  SendHello();
+}
+
+void AsyncSocksProxySocket::ProcessInput(char* data, size_t* len) {
+  RTC_DCHECK(state_ < SS_TUNNEL);
+
+  ByteBufferReader response(data, *len);
+
+  if (state_ == SS_HELLO) {
+    uint8_t ver, method;
+    if (!response.ReadUInt8(&ver) ||
+        !response.ReadUInt8(&method))
+      return;
+
+    if (ver != 5) {
+      Error(0);
+      return;
+    }
+
+    if (method == 0) {
+      SendConnect();
+    } else if (method == 2) {
+      SendAuth();
+    } else {
+      Error(0);
+      return;
+    }
+  } else if (state_ == SS_AUTH) {
+    uint8_t ver, status;
+    if (!response.ReadUInt8(&ver) ||
+        !response.ReadUInt8(&status))
+      return;
+
+    if ((ver != 1) || (status != 0)) {
+      Error(SOCKET_EACCES);
+      return;
+    }
+
+    SendConnect();
+  } else if (state_ == SS_CONNECT) {
+    uint8_t ver, rep, rsv, atyp;
+    if (!response.ReadUInt8(&ver) ||
+        !response.ReadUInt8(&rep) ||
+        !response.ReadUInt8(&rsv) ||
+        !response.ReadUInt8(&atyp))
+      return;
+
+    if ((ver != 5) || (rep != 0)) {
+      Error(0);
+      return;
+    }
+
+    uint16_t port;
+    if (atyp == 1) {
+      uint32_t addr;
+      if (!response.ReadUInt32(&addr) ||
+          !response.ReadUInt16(&port))
+        return;
+      LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port;
+    } else if (atyp == 3) {
+      uint8_t len;
+      std::string addr;
+      if (!response.ReadUInt8(&len) ||
+          !response.ReadString(&addr, len) ||
+          !response.ReadUInt16(&port))
+        return;
+      LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port;
+    } else if (atyp == 4) {
+      std::string addr;
+      if (!response.ReadString(&addr, 16) ||
+          !response.ReadUInt16(&port))
+        return;
+      LOG(LS_VERBOSE) << "Bound on <IPV6>:" << port;
+    } else {
+      Error(0);
+      return;
+    }
+
+    state_ = SS_TUNNEL;
+  }
+
+  // Consume parsed data
+  *len = response.Length();
+  memmove(data, response.Data(), *len);
+
+  if (state_ != SS_TUNNEL)
+    return;
+
+  bool remainder = (*len > 0);
+  BufferInput(false);
+  SignalConnectEvent(this);
+
+  // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
+  if (remainder)
+    SignalReadEvent(this);  // TODO: signal this??
+}
+
+void AsyncSocksProxySocket::SendHello() {
+  ByteBufferWriter request;
+  request.WriteUInt8(5);    // Socks Version
+  if (user_.empty()) {
+    request.WriteUInt8(1);  // Authentication Mechanisms
+    request.WriteUInt8(0);  // No authentication
+  } else {
+    request.WriteUInt8(2);  // Authentication Mechanisms
+    request.WriteUInt8(0);  // No authentication
+    request.WriteUInt8(2);  // Username/Password
+  }
+  DirectSend(request.Data(), request.Length());
+  state_ = SS_HELLO;
+}
+
+void AsyncSocksProxySocket::SendAuth() {
+  ByteBufferWriter request;
+  request.WriteUInt8(1);           // Negotiation Version
+  request.WriteUInt8(static_cast<uint8_t>(user_.size()));
+  request.WriteString(user_);      // Username
+  request.WriteUInt8(static_cast<uint8_t>(pass_.GetLength()));
+  size_t len = pass_.GetLength() + 1;
+  char * sensitive = new char[len];
+  pass_.CopyTo(sensitive, true);
+  request.WriteString(sensitive);  // Password
+  memset(sensitive, 0, len);
+  delete [] sensitive;
+  DirectSend(request.Data(), request.Length());
+  state_ = SS_AUTH;
+}
+
+void AsyncSocksProxySocket::SendConnect() {
+  ByteBufferWriter request;
+  request.WriteUInt8(5);              // Socks Version
+  request.WriteUInt8(1);              // CONNECT
+  request.WriteUInt8(0);              // Reserved
+  if (dest_.IsUnresolvedIP()) {
+    std::string hostname = dest_.hostname();
+    request.WriteUInt8(3);            // DOMAINNAME
+    request.WriteUInt8(static_cast<uint8_t>(hostname.size()));
+    request.WriteString(hostname);    // Destination Hostname
+  } else {
+    request.WriteUInt8(1);            // IPV4
+    request.WriteUInt32(dest_.ip());  // Destination IP
+  }
+  request.WriteUInt16(dest_.port());  // Destination Port
+  DirectSend(request.Data(), request.Length());
+  state_ = SS_CONNECT;
+}
+
+void AsyncSocksProxySocket::Error(int error) {
+  state_ = SS_ERROR;
+  BufferInput(false);
+  Close();
+  SetError(SOCKET_EACCES);
+  SignalCloseEvent(this, error);
+}
+
+AsyncSocksProxyServerSocket::AsyncSocksProxyServerSocket(AsyncSocket* socket)
+    : AsyncProxyServerSocket(socket, kBufferSize), state_(SS_HELLO) {
+  BufferInput(true);
+}
+
+void AsyncSocksProxyServerSocket::ProcessInput(char* data, size_t* len) {
+  // TODO: See if the whole message has arrived
+  RTC_DCHECK(state_ < SS_CONNECT_PENDING);
+
+  ByteBufferReader response(data, *len);
+  if (state_ == SS_HELLO) {
+    HandleHello(&response);
+  } else if (state_ == SS_AUTH) {
+    HandleAuth(&response);
+  } else if (state_ == SS_CONNECT) {
+    HandleConnect(&response);
+  }
+
+  // Consume parsed data
+  *len = response.Length();
+  memmove(data, response.Data(), *len);
+}
+
+void AsyncSocksProxyServerSocket::DirectSend(const ByteBufferWriter& buf) {
+  BufferedReadAdapter::DirectSend(buf.Data(), buf.Length());
+}
+
+void AsyncSocksProxyServerSocket::HandleHello(ByteBufferReader* request) {
+  uint8_t ver, num_methods;
+  if (!request->ReadUInt8(&ver) ||
+      !request->ReadUInt8(&num_methods)) {
+    Error(0);
+    return;
+  }
+
+  if (ver != 5) {
+    Error(0);
+    return;
+  }
+
+  // Handle either no-auth (0) or user/pass auth (2)
+  uint8_t method = 0xFF;
+  if (num_methods > 0 && !request->ReadUInt8(&method)) {
+    Error(0);
+    return;
+  }
+
+  // TODO: Ask the server which method to use.
+  SendHelloReply(method);
+  if (method == 0) {
+    state_ = SS_CONNECT;
+  } else if (method == 2) {
+    state_ = SS_AUTH;
+  } else {
+    state_ = SS_ERROR;
+  }
+}
+
+void AsyncSocksProxyServerSocket::SendHelloReply(uint8_t method) {
+  ByteBufferWriter response;
+  response.WriteUInt8(5);  // Socks Version
+  response.WriteUInt8(method);  // Auth method
+  DirectSend(response);
+}
+
+void AsyncSocksProxyServerSocket::HandleAuth(ByteBufferReader* request) {
+  uint8_t ver, user_len, pass_len;
+  std::string user, pass;
+  if (!request->ReadUInt8(&ver) ||
+      !request->ReadUInt8(&user_len) ||
+      !request->ReadString(&user, user_len) ||
+      !request->ReadUInt8(&pass_len) ||
+      !request->ReadString(&pass, pass_len)) {
+    Error(0);
+    return;
+  }
+
+  // TODO: Allow for checking of credentials.
+  SendAuthReply(0);
+  state_ = SS_CONNECT;
+}
+
+void AsyncSocksProxyServerSocket::SendAuthReply(uint8_t result) {
+  ByteBufferWriter response;
+  response.WriteUInt8(1);  // Negotiation Version
+  response.WriteUInt8(result);
+  DirectSend(response);
+}
+
+void AsyncSocksProxyServerSocket::HandleConnect(ByteBufferReader* request) {
+  uint8_t ver, command, reserved, addr_type;
+  uint32_t ip;
+  uint16_t port;
+  if (!request->ReadUInt8(&ver) ||
+      !request->ReadUInt8(&command) ||
+      !request->ReadUInt8(&reserved) ||
+      !request->ReadUInt8(&addr_type) ||
+      !request->ReadUInt32(&ip) ||
+      !request->ReadUInt16(&port)) {
+      Error(0);
+      return;
+  }
+
+  if (ver != 5 || command != 1 ||
+      reserved != 0 || addr_type != 1) {
+      Error(0);
+      return;
+  }
+
+  SignalConnectRequest(this, SocketAddress(ip, port));
+  state_ = SS_CONNECT_PENDING;
+}
+
+void AsyncSocksProxyServerSocket::SendConnectResult(int result,
+                                                    const SocketAddress& addr) {
+  if (state_ != SS_CONNECT_PENDING)
+    return;
+
+  ByteBufferWriter response;
+  response.WriteUInt8(5);  // Socks version
+  response.WriteUInt8((result != 0));  // 0x01 is generic error
+  response.WriteUInt8(0);  // reserved
+  response.WriteUInt8(1);  // IPv4 address
+  response.WriteUInt32(addr.ip());
+  response.WriteUInt16(addr.port());
+  DirectSend(response);
+  BufferInput(false);
+  state_ = SS_TUNNEL;
+}
+
+void AsyncSocksProxyServerSocket::Error(int error) {
+  state_ = SS_ERROR;
+  BufferInput(false);
+  Close();
+  SetError(SOCKET_EACCES);
+  SignalCloseEvent(this, error);
+}
+
+}  // namespace rtc
diff --git a/base/socketadapters.h b/base/socketadapters.h
index 7df0f3a..3b5be10 100644
--- a/base/socketadapters.h
+++ b/base/socketadapters.h
@@ -11,9 +11,197 @@
 #ifndef WEBRTC_BASE_SOCKETADAPTERS_H_
 #define WEBRTC_BASE_SOCKETADAPTERS_H_
 
+#include <map>
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/socketadapters.h"
+#include "webrtc/base/asyncsocket.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/cryptstring.h"
+#include "webrtc/base/logging.h"
+
+namespace rtc {
+
+struct HttpAuthContext;
+class ByteBufferReader;
+class ByteBufferWriter;
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Implements a socket adapter that can buffer and process data internally,
+// as in the case of connecting to a proxy, where you must speak the proxy
+// protocol before commencing normal socket behavior.
+class BufferedReadAdapter : public AsyncSocketAdapter {
+ public:
+  BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size);
+  ~BufferedReadAdapter() override;
+
+  int Send(const void* pv, size_t cb) override;
+  int Recv(void* pv, size_t cb, int64_t* timestamp) override;
+
+ protected:
+  int DirectSend(const void* pv, size_t cb) {
+    return AsyncSocketAdapter::Send(pv, cb);
+  }
+
+  void BufferInput(bool on = true);
+  virtual void ProcessInput(char* data, size_t* len) = 0;
+
+  void OnReadEvent(AsyncSocket* socket) override;
+
+ private:
+  char * buffer_;
+  size_t buffer_size_, data_len_;
+  bool buffering_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(BufferedReadAdapter);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Interface for implementing proxy server sockets.
+class AsyncProxyServerSocket : public BufferedReadAdapter {
+ public:
+  AsyncProxyServerSocket(AsyncSocket* socket, size_t buffer_size);
+  ~AsyncProxyServerSocket() override;
+  sigslot::signal2<AsyncProxyServerSocket*,
+                   const SocketAddress&>  SignalConnectRequest;
+  virtual void SendConnectResult(int err, const SocketAddress& addr) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Implements a socket adapter that performs the client side of a
+// fake SSL handshake. Used for "ssltcp" P2P functionality.
+class AsyncSSLSocket : public BufferedReadAdapter {
+ public:
+  explicit AsyncSSLSocket(AsyncSocket* socket);
+
+  int Connect(const SocketAddress& addr) override;
+
+ protected:
+  void OnConnectEvent(AsyncSocket* socket) override;
+  void ProcessInput(char* data, size_t* len) override;
+  RTC_DISALLOW_COPY_AND_ASSIGN(AsyncSSLSocket);
+};
+
+// Implements a socket adapter that performs the server side of a
+// fake SSL handshake. Used when implementing a relay server that does "ssltcp".
+class AsyncSSLServerSocket : public BufferedReadAdapter {
+ public:
+  explicit AsyncSSLServerSocket(AsyncSocket* socket);
+
+ protected:
+  void ProcessInput(char* data, size_t* len) override;
+  RTC_DISALLOW_COPY_AND_ASSIGN(AsyncSSLServerSocket);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Implements a socket adapter that speaks the HTTP/S proxy protocol.
+class AsyncHttpsProxySocket : public BufferedReadAdapter {
+ public:
+  AsyncHttpsProxySocket(AsyncSocket* socket, const std::string& user_agent,
+    const SocketAddress& proxy,
+    const std::string& username, const CryptString& password);
+  ~AsyncHttpsProxySocket() override;
+
+  // If connect is forced, the adapter will always issue an HTTP CONNECT to the
+  // target address.  Otherwise, it will connect only if the destination port
+  // is not port 80.
+  void SetForceConnect(bool force) { force_connect_ = force; }
+
+  int Connect(const SocketAddress& addr) override;
+  SocketAddress GetRemoteAddress() const override;
+  int Close() override;
+  ConnState GetState() const override;
+
+ protected:
+  void OnConnectEvent(AsyncSocket* socket) override;
+  void OnCloseEvent(AsyncSocket* socket, int err) override;
+  void ProcessInput(char* data, size_t* len) override;
+
+  bool ShouldIssueConnect() const;
+  void SendRequest();
+  void ProcessLine(char* data, size_t len);
+  void EndResponse();
+  void Error(int error);
+
+ private:
+  SocketAddress proxy_, dest_;
+  std::string agent_, user_, headers_;
+  CryptString pass_;
+  bool force_connect_;
+  size_t content_length_;
+  int defer_error_;
+  bool expect_close_;
+  enum ProxyState {
+    PS_INIT, PS_LEADER, PS_AUTHENTICATE, PS_SKIP_HEADERS, PS_ERROR_HEADERS,
+    PS_TUNNEL_HEADERS, PS_SKIP_BODY, PS_TUNNEL, PS_WAIT_CLOSE, PS_ERROR
+  } state_;
+  HttpAuthContext * context_;
+  std::string unknown_mechanisms_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(AsyncHttpsProxySocket);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Implements a socket adapter that speaks the SOCKS proxy protocol.
+class AsyncSocksProxySocket : public BufferedReadAdapter {
+ public:
+  AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy,
+    const std::string& username, const CryptString& password);
+  ~AsyncSocksProxySocket() override;
+
+  int Connect(const SocketAddress& addr) override;
+  SocketAddress GetRemoteAddress() const override;
+  int Close() override;
+  ConnState GetState() const override;
+
+ protected:
+  void OnConnectEvent(AsyncSocket* socket) override;
+  void ProcessInput(char* data, size_t* len) override;
+
+  void SendHello();
+  void SendConnect();
+  void SendAuth();
+  void Error(int error);
+
+ private:
+  enum State {
+    SS_INIT, SS_HELLO, SS_AUTH, SS_CONNECT, SS_TUNNEL, SS_ERROR
+  };
+  State state_;
+  SocketAddress proxy_, dest_;
+  std::string user_;
+  CryptString pass_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(AsyncSocksProxySocket);
+};
+
+// Implements a proxy server socket for the SOCKS protocol.
+class AsyncSocksProxyServerSocket : public AsyncProxyServerSocket {
+ public:
+  explicit AsyncSocksProxyServerSocket(AsyncSocket* socket);
+
+ private:
+  void ProcessInput(char* data, size_t* len) override;
+  void DirectSend(const ByteBufferWriter& buf);
+
+  void HandleHello(ByteBufferReader* request);
+  void SendHelloReply(uint8_t method);
+  void HandleAuth(ByteBufferReader* request);
+  void SendAuthReply(uint8_t result);
+  void HandleConnect(ByteBufferReader* request);
+  void SendConnectResult(int result, const SocketAddress& addr) override;
+
+  void Error(int error);
+
+  static const int kBufferSize = 1024;
+  enum State {
+    SS_HELLO, SS_AUTH, SS_CONNECT, SS_CONNECT_PENDING, SS_TUNNEL, SS_ERROR
+  };
+  State state_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(AsyncSocksProxyServerSocket);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SOCKETADAPTERS_H_
diff --git a/base/socketaddress.cc b/base/socketaddress.cc
new file mode 100644
index 0000000..d931f72
--- /dev/null
+++ b/base/socketaddress.cc
@@ -0,0 +1,339 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/socketaddress.h"
+
+#if defined(WEBRTC_POSIX)
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#if defined(OPENBSD)
+#include <netinet/in_systm.h>
+#endif
+#if !defined(__native_client__)
+#include <netinet/ip.h>
+#endif
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#endif
+
+#include <sstream>
+
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/nethelpers.h"
+
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#endif
+
+namespace rtc {
+
+SocketAddress::SocketAddress() {
+  Clear();
+}
+
+SocketAddress::SocketAddress(const std::string& hostname, int port) {
+  SetIP(hostname);
+  SetPort(port);
+}
+
+SocketAddress::SocketAddress(uint32_t ip_as_host_order_integer, int port) {
+  SetIP(IPAddress(ip_as_host_order_integer));
+  SetPort(port);
+}
+
+SocketAddress::SocketAddress(const IPAddress& ip, int port) {
+  SetIP(ip);
+  SetPort(port);
+}
+
+SocketAddress::SocketAddress(const SocketAddress& addr) {
+  this->operator=(addr);
+}
+
+void SocketAddress::Clear() {
+  hostname_.clear();
+  literal_ = false;
+  ip_ = IPAddress();
+  port_ = 0;
+  scope_id_ = 0;
+}
+
+bool SocketAddress::IsNil() const {
+  return hostname_.empty() && IPIsUnspec(ip_) && 0 == port_;
+}
+
+bool SocketAddress::IsComplete() const {
+  return (!IPIsAny(ip_)) && (0 != port_);
+}
+
+SocketAddress& SocketAddress::operator=(const SocketAddress& addr) {
+  hostname_ = addr.hostname_;
+  ip_ = addr.ip_;
+  port_ = addr.port_;
+  literal_ = addr.literal_;
+  scope_id_ = addr.scope_id_;
+  return *this;
+}
+
+void SocketAddress::SetIP(uint32_t ip_as_host_order_integer) {
+  hostname_.clear();
+  literal_ = false;
+  ip_ = IPAddress(ip_as_host_order_integer);
+  scope_id_ = 0;
+}
+
+void SocketAddress::SetIP(const IPAddress& ip) {
+  hostname_.clear();
+  literal_ = false;
+  ip_ = ip;
+  scope_id_ = 0;
+}
+
+void SocketAddress::SetIP(const std::string& hostname) {
+  hostname_ = hostname;
+  literal_ = IPFromString(hostname, &ip_);
+  if (!literal_) {
+    ip_ = IPAddress();
+  }
+  scope_id_ = 0;
+}
+
+void SocketAddress::SetResolvedIP(uint32_t ip_as_host_order_integer) {
+  ip_ = IPAddress(ip_as_host_order_integer);
+  scope_id_ = 0;
+}
+
+void SocketAddress::SetResolvedIP(const IPAddress& ip) {
+  ip_ = ip;
+  scope_id_ = 0;
+}
+
+void SocketAddress::SetPort(int port) {
+  RTC_DCHECK((0 <= port) && (port < 65536));
+  port_ = static_cast<uint16_t>(port);
+}
+
+uint32_t SocketAddress::ip() const {
+  return ip_.v4AddressAsHostOrderInteger();
+}
+
+const IPAddress& SocketAddress::ipaddr() const {
+  return ip_;
+}
+
+uint16_t SocketAddress::port() const {
+  return port_;
+}
+
+std::string SocketAddress::HostAsURIString() const {
+  // If the hostname was a literal IP string, it may need to have square
+  // brackets added (for SocketAddress::ToString()).
+  if (!literal_ && !hostname_.empty())
+    return hostname_;
+  if (ip_.family() == AF_INET6) {
+    return "[" + ip_.ToString() + "]";
+  } else {
+    return ip_.ToString();
+  }
+}
+
+std::string SocketAddress::HostAsSensitiveURIString() const {
+  // If the hostname was a literal IP string, it may need to have square
+  // brackets added (for SocketAddress::ToString()).
+  if (!literal_ && !hostname_.empty())
+    return hostname_;
+  if (ip_.family() == AF_INET6) {
+    return "[" + ip_.ToSensitiveString() + "]";
+  } else {
+    return ip_.ToSensitiveString();
+  }
+}
+
+std::string SocketAddress::PortAsString() const {
+  std::ostringstream ost;
+  ost << port_;
+  return ost.str();
+}
+
+std::string SocketAddress::ToString() const {
+  std::ostringstream ost;
+  ost << *this;
+  return ost.str();
+}
+
+std::string SocketAddress::ToSensitiveString() const {
+  std::ostringstream ost;
+  ost << HostAsSensitiveURIString() << ":" << port();
+  return ost.str();
+}
+
+bool SocketAddress::FromString(const std::string& str) {
+  if (str.at(0) == '[') {
+    std::string::size_type closebracket = str.rfind(']');
+    if (closebracket != std::string::npos) {
+      std::string::size_type colon = str.find(':', closebracket);
+      if (colon != std::string::npos && colon > closebracket) {
+        SetPort(strtoul(str.substr(colon + 1).c_str(), nullptr, 10));
+        SetIP(str.substr(1, closebracket - 1));
+      } else {
+        return false;
+      }
+    }
+  } else {
+    std::string::size_type pos = str.find(':');
+    if (std::string::npos == pos)
+      return false;
+    SetPort(strtoul(str.substr(pos + 1).c_str(), nullptr, 10));
+    SetIP(str.substr(0, pos));
+  }
+  return true;
+}
+
+std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
+  os << addr.HostAsURIString() << ":" << addr.port();
+  return os;
+}
+
+bool SocketAddress::IsAnyIP() const {
+  return IPIsAny(ip_);
+}
+
+bool SocketAddress::IsLoopbackIP() const {
+  return IPIsLoopback(ip_) || (IPIsAny(ip_) &&
+                               0 == strcmp(hostname_.c_str(), "localhost"));
+}
+
+bool SocketAddress::IsPrivateIP() const {
+  return IPIsPrivate(ip_);
+}
+
+bool SocketAddress::IsUnresolvedIP() const {
+  return IPIsUnspec(ip_) && !literal_ && !hostname_.empty();
+}
+
+bool SocketAddress::operator==(const SocketAddress& addr) const {
+  return EqualIPs(addr) && EqualPorts(addr);
+}
+
+bool SocketAddress::operator<(const SocketAddress& addr) const {
+  if (ip_ != addr.ip_)
+    return ip_ < addr.ip_;
+
+  // We only check hostnames if both IPs are ANY or unspecified.  This matches
+  // EqualIPs().
+  if ((IPIsAny(ip_) || IPIsUnspec(ip_)) && hostname_ != addr.hostname_)
+    return hostname_ < addr.hostname_;
+
+  return port_ < addr.port_;
+}
+
+bool SocketAddress::EqualIPs(const SocketAddress& addr) const {
+  return (ip_ == addr.ip_) &&
+      ((!IPIsAny(ip_) && !IPIsUnspec(ip_)) || (hostname_ == addr.hostname_));
+}
+
+bool SocketAddress::EqualPorts(const SocketAddress& addr) const {
+  return (port_ == addr.port_);
+}
+
+size_t SocketAddress::Hash() const {
+  size_t h = 0;
+  h ^= HashIP(ip_);
+  h ^= port_ | (port_ << 16);
+  return h;
+}
+
+void SocketAddress::ToSockAddr(sockaddr_in* saddr) const {
+  memset(saddr, 0, sizeof(*saddr));
+  if (ip_.family() != AF_INET) {
+    saddr->sin_family = AF_UNSPEC;
+    return;
+  }
+  saddr->sin_family = AF_INET;
+  saddr->sin_port = HostToNetwork16(port_);
+  if (IPIsAny(ip_)) {
+    saddr->sin_addr.s_addr = INADDR_ANY;
+  } else {
+    saddr->sin_addr = ip_.ipv4_address();
+  }
+}
+
+bool SocketAddress::FromSockAddr(const sockaddr_in& saddr) {
+  if (saddr.sin_family != AF_INET)
+    return false;
+  SetIP(NetworkToHost32(saddr.sin_addr.s_addr));
+  SetPort(NetworkToHost16(saddr.sin_port));
+  literal_ = false;
+  return true;
+}
+
+static size_t ToSockAddrStorageHelper(sockaddr_storage* addr,
+                                      IPAddress ip,
+                                      uint16_t port,
+                                      int scope_id) {
+  memset(addr, 0, sizeof(sockaddr_storage));
+  addr->ss_family = static_cast<unsigned short>(ip.family());
+  if (addr->ss_family == AF_INET6) {
+    sockaddr_in6* saddr = reinterpret_cast<sockaddr_in6*>(addr);
+    saddr->sin6_addr = ip.ipv6_address();
+    saddr->sin6_port = HostToNetwork16(port);
+    saddr->sin6_scope_id = scope_id;
+    return sizeof(sockaddr_in6);
+  } else if (addr->ss_family == AF_INET) {
+    sockaddr_in* saddr = reinterpret_cast<sockaddr_in*>(addr);
+    saddr->sin_addr = ip.ipv4_address();
+    saddr->sin_port = HostToNetwork16(port);
+    return sizeof(sockaddr_in);
+  }
+  return 0;
+}
+
+size_t SocketAddress::ToDualStackSockAddrStorage(sockaddr_storage *addr) const {
+  return ToSockAddrStorageHelper(addr, ip_.AsIPv6Address(), port_, scope_id_);
+}
+
+size_t SocketAddress::ToSockAddrStorage(sockaddr_storage* addr) const {
+  return ToSockAddrStorageHelper(addr, ip_, port_, scope_id_);
+}
+
+bool SocketAddressFromSockAddrStorage(const sockaddr_storage& addr,
+                                      SocketAddress* out) {
+  if (!out) {
+    return false;
+  }
+  if (addr.ss_family == AF_INET) {
+    const sockaddr_in* saddr = reinterpret_cast<const sockaddr_in*>(&addr);
+    *out = SocketAddress(IPAddress(saddr->sin_addr),
+                         NetworkToHost16(saddr->sin_port));
+    return true;
+  } else if (addr.ss_family == AF_INET6) {
+    const sockaddr_in6* saddr = reinterpret_cast<const sockaddr_in6*>(&addr);
+    *out = SocketAddress(IPAddress(saddr->sin6_addr),
+                         NetworkToHost16(saddr->sin6_port));
+    out->SetScopeID(saddr->sin6_scope_id);
+    return true;
+  }
+  return false;
+}
+
+SocketAddress EmptySocketAddressWithFamily(int family) {
+  if (family == AF_INET) {
+    return SocketAddress(IPAddress(INADDR_ANY), 0);
+  } else if (family == AF_INET6) {
+    return SocketAddress(IPAddress(in6addr_any), 0);
+  }
+  return SocketAddress();
+}
+
+}  // namespace rtc
diff --git a/base/socketaddress.h b/base/socketaddress.h
index 20199ad..bcff390 100644
--- a/base/socketaddress.h
+++ b/base/socketaddress.h
@@ -11,9 +11,187 @@
 #ifndef WEBRTC_BASE_SOCKETADDRESS_H_
 #define WEBRTC_BASE_SOCKETADDRESS_H_
 
+#include <string>
+#include <vector>
+#include <iosfwd>
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/ipaddress.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/socketaddress.h"
+#undef SetPort
+
+struct sockaddr_in;
+struct sockaddr_storage;
+
+namespace rtc {
+
+// Records an IP address and port.
+class SocketAddress {
+ public:
+  // Creates a nil address.
+  SocketAddress();
+
+  // Creates the address with the given host and port. Host may be a
+  // literal IP string or a hostname to be resolved later.
+  // DCHECKs that port is in valid range (0 to 2^16-1).
+  SocketAddress(const std::string& hostname, int port);
+
+  // Creates the address with the given IP and port.
+  // IP is given as an integer in host byte order. V4 only, to be deprecated.
+  // DCHECKs that port is in valid range (0 to 2^16-1).
+  SocketAddress(uint32_t ip_as_host_order_integer, int port);
+
+  // Creates the address with the given IP and port.
+  // DCHECKs that port is in valid range (0 to 2^16-1).
+  SocketAddress(const IPAddress& ip, int port);
+
+  // Creates a copy of the given address.
+  SocketAddress(const SocketAddress& addr);
+
+  // Resets to the nil address.
+  void Clear();
+
+  // Determines if this is a nil address (empty hostname, any IP, null port)
+  bool IsNil() const;
+
+  // Returns true if ip and port are set.
+  bool IsComplete() const;
+
+  // Replaces our address with the given one.
+  SocketAddress& operator=(const SocketAddress& addr);
+
+  // Changes the IP of this address to the given one, and clears the hostname
+  // IP is given as an integer in host byte order. V4 only, to be deprecated..
+  void SetIP(uint32_t ip_as_host_order_integer);
+
+  // Changes the IP of this address to the given one, and clears the hostname.
+  void SetIP(const IPAddress& ip);
+
+  // Changes the hostname of this address to the given one.
+  // Does not resolve the address; use Resolve to do so.
+  void SetIP(const std::string& hostname);
+
+  // Sets the IP address while retaining the hostname.  Useful for bypassing
+  // DNS for a pre-resolved IP.
+  // IP is given as an integer in host byte order. V4 only, to be deprecated.
+  void SetResolvedIP(uint32_t ip_as_host_order_integer);
+
+  // Sets the IP address while retaining the hostname.  Useful for bypassing
+  // DNS for a pre-resolved IP.
+  void SetResolvedIP(const IPAddress& ip);
+
+  // Changes the port of this address to the given one.
+  // DCHECKs that port is in valid range (0 to 2^16-1).
+  void SetPort(int port);
+
+  // Returns the hostname.
+  const std::string& hostname() const { return hostname_; }
+
+  // Returns the IP address as a host byte order integer.
+  // Returns 0 for non-v4 addresses.
+  uint32_t ip() const;
+
+  const IPAddress& ipaddr() const;
+
+  int family() const {return ip_.family(); }
+
+  // Returns the port part of this address.
+  uint16_t port() const;
+
+  // Returns the scope ID associated with this address. Scope IDs are a
+  // necessary addition to IPv6 link-local addresses, with different network
+  // interfaces having different scope-ids for their link-local addresses.
+  // IPv4 address do not have scope_ids and sockaddr_in structures do not have
+  // a field for them.
+  int scope_id() const {return scope_id_; }
+  void SetScopeID(int id) { scope_id_ = id; }
+
+  // Returns the 'host' portion of the address (hostname or IP) in a form
+  // suitable for use in a URI. If both IP and hostname are present, hostname
+  // is preferred. IPv6 addresses are enclosed in square brackets ('[' and ']').
+  std::string HostAsURIString() const;
+
+  // Same as HostAsURIString but anonymizes IP addresses by hiding the last
+  // part.
+  std::string HostAsSensitiveURIString() const;
+
+  // Returns the port as a string.
+  std::string PortAsString() const;
+
+  // Returns hostname:port or [hostname]:port.
+  std::string ToString() const;
+
+  // Same as ToString but anonymizes it by hiding the last part.
+  std::string ToSensitiveString() const;
+
+  // Parses hostname:port and [hostname]:port.
+  bool FromString(const std::string& str);
+
+  friend std::ostream& operator<<(std::ostream& os, const SocketAddress& addr);
+
+  // Determines whether this represents a missing / any IP address.
+  // That is, 0.0.0.0 or ::.
+  // Hostname and/or port may be set.
+  bool IsAnyIP() const;
+
+  // Determines whether the IP address refers to a loopback address.
+  // For v4 addresses this means the address is in the range 127.0.0.0/8.
+  // For v6 addresses this means the address is ::1.
+  bool IsLoopbackIP() const;
+
+  // Determines whether the IP address is in one of the private ranges:
+  // For v4: 127.0.0.0/8 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12.
+  // For v6: FE80::/16 and ::1.
+  bool IsPrivateIP() const;
+
+  // Determines whether the hostname has been resolved to an IP.
+  bool IsUnresolvedIP() const;
+
+  // Determines whether this address is identical to the given one.
+  bool operator ==(const SocketAddress& addr) const;
+  inline bool operator !=(const SocketAddress& addr) const {
+    return !this->operator ==(addr);
+  }
+
+  // Compares based on IP and then port.
+  bool operator <(const SocketAddress& addr) const;
+
+  // Determines whether this address has the same IP as the one given.
+  bool EqualIPs(const SocketAddress& addr) const;
+
+  // Determines whether this address has the same port as the one given.
+  bool EqualPorts(const SocketAddress& addr) const;
+
+  // Hashes this address into a small number.
+  size_t Hash() const;
+
+  // Write this address to a sockaddr_in.
+  // If IPv6, will zero out the sockaddr_in and sets family to AF_UNSPEC.
+  void ToSockAddr(sockaddr_in* saddr) const;
+
+  // Read this address from a sockaddr_in.
+  bool FromSockAddr(const sockaddr_in& saddr);
+
+  // Read and write the address to/from a sockaddr_storage.
+  // Dual stack version always sets family to AF_INET6, and maps v4 addresses.
+  // The other version doesn't map, and outputs an AF_INET address for
+  // v4 or mapped addresses, and AF_INET6 addresses for others.
+  // Returns the size of the sockaddr_in or sockaddr_in6 structure that is
+  // written to the sockaddr_storage, or zero on failure.
+  size_t ToDualStackSockAddrStorage(sockaddr_storage* saddr) const;
+  size_t ToSockAddrStorage(sockaddr_storage* saddr) const;
+
+ private:
+  std::string hostname_;
+  IPAddress ip_;
+  uint16_t port_;
+  int scope_id_;
+  bool literal_;  // Indicates that 'hostname_' contains a literal IP string.
+};
+
+bool SocketAddressFromSockAddrStorage(const sockaddr_storage& saddr,
+                                      SocketAddress* out);
+SocketAddress EmptySocketAddressWithFamily(int family);
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SOCKETADDRESS_H_
diff --git a/base/socketaddress_unittest.cc b/base/socketaddress_unittest.cc
new file mode 100644
index 0000000..072918e
--- /dev/null
+++ b/base/socketaddress_unittest.cc
@@ -0,0 +1,351 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#if defined(WEBRTC_POSIX)
+#include <netinet/in.h>  // for sockaddr_in
+#endif
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/ipaddress.h"
+
+namespace rtc {
+
+const in6_addr kTestV6Addr =  { { {0x20, 0x01, 0x0d, 0xb8,
+                                   0x10, 0x20, 0x30, 0x40,
+                                   0x50, 0x60, 0x70, 0x80,
+                                   0x90, 0xA0, 0xB0, 0xC0} } };
+const in6_addr kMappedV4Addr = { { {0x00, 0x00, 0x00, 0x00,
+                                    0x00, 0x00, 0x00, 0x00,
+                                    0x00, 0x00, 0xFF, 0xFF,
+                                    0x01, 0x02, 0x03, 0x04} } };
+const std::string kTestV6AddrString = "2001:db8:1020:3040:5060:7080:90a0:b0c0";
+const std::string kTestV6AddrAnonymizedString = "2001:db8:1020:x:x:x:x:x";
+const std::string kTestV6AddrFullString =
+    "[2001:db8:1020:3040:5060:7080:90a0:b0c0]:5678";
+const std::string kTestV6AddrFullAnonymizedString =
+    "[2001:db8:1020:x:x:x:x:x]:5678";
+
+TEST(SocketAddressTest, TestDefaultCtor) {
+  SocketAddress addr;
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(), addr.ipaddr());
+  EXPECT_EQ(0, addr.port());
+  EXPECT_EQ("", addr.hostname());
+}
+
+TEST(SocketAddressTest, TestIPPortCtor) {
+  SocketAddress addr(IPAddress(0x01020304), 5678);
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("", addr.hostname());
+  EXPECT_EQ("1.2.3.4:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestIPv4StringPortCtor) {
+  SocketAddress addr("1.2.3.4", 5678);
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("1.2.3.4", addr.hostname());
+  EXPECT_EQ("1.2.3.4:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestIPv6StringPortCtor) {
+  SocketAddress addr2(kTestV6AddrString, 1234);
+  IPAddress tocheck(kTestV6Addr);
+
+  EXPECT_FALSE(addr2.IsUnresolvedIP());
+  EXPECT_EQ(tocheck, addr2.ipaddr());
+  EXPECT_EQ(1234, addr2.port());
+  EXPECT_EQ(kTestV6AddrString, addr2.hostname());
+  EXPECT_EQ("[" + kTestV6AddrString + "]:1234", addr2.ToString());
+}
+
+TEST(SocketAddressTest, TestSpecialStringPortCtor) {
+  // inet_addr doesn't handle this address properly.
+  SocketAddress addr("255.255.255.255", 5678);
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0xFFFFFFFFU), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("255.255.255.255", addr.hostname());
+  EXPECT_EQ("255.255.255.255:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestHostnamePortCtor) {
+  SocketAddress addr("a.b.com", 5678);
+  EXPECT_TRUE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("a.b.com", addr.hostname());
+  EXPECT_EQ("a.b.com:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestCopyCtor) {
+  SocketAddress from("1.2.3.4", 5678);
+  SocketAddress addr(from);
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("1.2.3.4", addr.hostname());
+  EXPECT_EQ("1.2.3.4:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestAssign) {
+  SocketAddress from("1.2.3.4", 5678);
+  SocketAddress addr(IPAddress(0x88888888), 9999);
+  addr = from;
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("1.2.3.4", addr.hostname());
+  EXPECT_EQ("1.2.3.4:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestSetIPPort) {
+  SocketAddress addr(IPAddress(0x88888888), 9999);
+  addr.SetIP(IPAddress(0x01020304));
+  addr.SetPort(5678);
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("", addr.hostname());
+  EXPECT_EQ("1.2.3.4:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestSetIPFromString) {
+  SocketAddress addr(IPAddress(0x88888888), 9999);
+  addr.SetIP("1.2.3.4");
+  addr.SetPort(5678);
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("1.2.3.4", addr.hostname());
+  EXPECT_EQ("1.2.3.4:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestSetIPFromHostname) {
+  SocketAddress addr(IPAddress(0x88888888), 9999);
+  addr.SetIP("a.b.com");
+  addr.SetPort(5678);
+  EXPECT_TRUE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("a.b.com", addr.hostname());
+  EXPECT_EQ("a.b.com:5678", addr.ToString());
+  addr.SetResolvedIP(IPAddress(0x01020304));
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ("a.b.com", addr.hostname());
+  EXPECT_EQ("a.b.com:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestFromIPv4String) {
+  SocketAddress addr;
+  EXPECT_TRUE(addr.FromString("1.2.3.4:5678"));
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("1.2.3.4", addr.hostname());
+  EXPECT_EQ("1.2.3.4:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestFromIPv6String) {
+  SocketAddress addr;
+  EXPECT_TRUE(addr.FromString(kTestV6AddrFullString));
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ(kTestV6AddrString, addr.hostname());
+  EXPECT_EQ(kTestV6AddrFullString, addr.ToString());
+}
+
+TEST(SocketAddressTest, TestFromHostname) {
+  SocketAddress addr;
+  EXPECT_TRUE(addr.FromString("a.b.com:5678"));
+  EXPECT_TRUE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("a.b.com", addr.hostname());
+  EXPECT_EQ("a.b.com:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestToFromSockAddr) {
+  SocketAddress from("1.2.3.4", 5678), addr;
+  sockaddr_in addr_in;
+  from.ToSockAddr(&addr_in);
+  EXPECT_TRUE(addr.FromSockAddr(addr_in));
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("", addr.hostname());
+  EXPECT_EQ("1.2.3.4:5678", addr.ToString());
+}
+
+TEST(SocketAddressTest, TestToFromSockAddrStorage) {
+  SocketAddress from("1.2.3.4", 5678), addr;
+  sockaddr_storage addr_storage;
+  from.ToSockAddrStorage(&addr_storage);
+  EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr));
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(0x01020304U), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("", addr.hostname());
+  EXPECT_EQ("1.2.3.4:5678", addr.ToString());
+
+  addr.Clear();
+  from.ToDualStackSockAddrStorage(&addr_storage);
+  EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr));
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(kMappedV4Addr), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("", addr.hostname());
+  EXPECT_EQ("[::ffff:1.2.3.4]:5678", addr.ToString());
+
+  addr.Clear();
+  memset(&addr_storage, 0, sizeof(sockaddr_storage));
+  from = SocketAddress(kTestV6AddrString, 5678);
+  from.SetScopeID(6);
+  from.ToSockAddrStorage(&addr_storage);
+  EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr));
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(kTestV6Addr), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("", addr.hostname());
+  EXPECT_EQ(kTestV6AddrFullString, addr.ToString());
+  EXPECT_EQ(6, addr.scope_id());
+
+  addr.Clear();
+  from.ToDualStackSockAddrStorage(&addr_storage);
+  EXPECT_TRUE(SocketAddressFromSockAddrStorage(addr_storage, &addr));
+  EXPECT_FALSE(addr.IsUnresolvedIP());
+  EXPECT_EQ(IPAddress(kTestV6Addr), addr.ipaddr());
+  EXPECT_EQ(5678, addr.port());
+  EXPECT_EQ("", addr.hostname());
+  EXPECT_EQ(kTestV6AddrFullString, addr.ToString());
+  EXPECT_EQ(6, addr.scope_id());
+
+  addr = from;
+  addr_storage.ss_family = AF_UNSPEC;
+  EXPECT_FALSE(SocketAddressFromSockAddrStorage(addr_storage, &addr));
+  EXPECT_EQ(from, addr);
+
+  EXPECT_FALSE(SocketAddressFromSockAddrStorage(addr_storage, nullptr));
+}
+
+bool AreEqual(const SocketAddress& addr1,
+              const SocketAddress& addr2) {
+  return addr1 == addr2 && addr2 == addr1 &&
+      !(addr1 != addr2) && !(addr2 != addr1);
+}
+
+bool AreUnequal(const SocketAddress& addr1,
+                const SocketAddress& addr2) {
+  return !(addr1 == addr2) && !(addr2 == addr1) &&
+      addr1 != addr2 && addr2 != addr1;
+}
+
+TEST(SocketAddressTest, TestEqualityOperators) {
+  SocketAddress addr1("1.2.3.4", 5678);
+  SocketAddress addr2("1.2.3.4", 5678);
+  EXPECT_PRED2(AreEqual, addr1, addr2);
+
+  addr2 = SocketAddress("0.0.0.1", 5678);
+  EXPECT_PRED2(AreUnequal, addr1, addr2);
+
+  addr2 = SocketAddress("1.2.3.4", 1234);
+  EXPECT_PRED2(AreUnequal, addr1, addr2);
+
+  addr2 = SocketAddress(kTestV6AddrString, 5678);
+  EXPECT_PRED2(AreUnequal, addr1, addr2);
+
+  addr1 = SocketAddress(kTestV6AddrString, 5678);
+  EXPECT_PRED2(AreEqual, addr1, addr2);
+
+  addr2 = SocketAddress(kTestV6AddrString, 1234);
+  EXPECT_PRED2(AreUnequal, addr1, addr2);
+
+  addr2 = SocketAddress("fe80::1", 5678);
+  EXPECT_PRED2(AreUnequal, addr1, addr2);
+
+  SocketAddress addr3("a.b.c.d", 1);
+  SocketAddress addr4("b.b.c.d", 1);
+  EXPECT_PRED2(AreUnequal, addr3, addr4);
+  EXPECT_PRED2(AreEqual, addr3, addr3);
+
+  addr3.SetIP(addr1.ip());
+  addr4.SetIP(addr1.ip());
+  EXPECT_PRED2(AreEqual,addr3, addr4);
+}
+
+bool IsLessThan(const SocketAddress& addr1, const SocketAddress& addr2) {
+  return addr1 < addr2 &&
+      !(addr2 < addr1) &&
+      !(addr1 == addr2);
+}
+
+TEST(SocketAddressTest, TestComparisonOperator) {
+  SocketAddress addr1("1.2.3.4", 5678);
+  SocketAddress addr2("1.2.3.4", 5678);
+
+  EXPECT_FALSE(addr1 < addr2);
+  EXPECT_FALSE(addr2 < addr1);
+
+  addr2 = SocketAddress("1.2.3.4", 5679);
+  EXPECT_PRED2(IsLessThan, addr1, addr2);
+
+  addr2 = SocketAddress("2.2.3.4", 49152);
+  EXPECT_PRED2(IsLessThan, addr1, addr2);
+
+  addr2 = SocketAddress(kTestV6AddrString, 5678);
+  EXPECT_PRED2(IsLessThan, addr1, addr2);
+
+  addr1 = SocketAddress("fe80::1", 5678);
+  EXPECT_PRED2(IsLessThan, addr2, addr1);
+
+  addr2 = SocketAddress("fe80::1", 5679);
+  EXPECT_PRED2(IsLessThan, addr1, addr2);
+
+  addr2 = SocketAddress("fe80::1", 5678);
+  EXPECT_FALSE(addr1 < addr2);
+  EXPECT_FALSE(addr2 < addr1);
+
+  SocketAddress addr3("a.b.c.d", 1);
+  SocketAddress addr4("b.b.c.d", 1);
+  EXPECT_PRED2(IsLessThan, addr3, addr4);
+}
+
+TEST(SocketAddressTest, TestToSensitiveString) {
+  SocketAddress addr_v4("1.2.3.4", 5678);
+  EXPECT_EQ("1.2.3.4", addr_v4.HostAsURIString());
+  EXPECT_EQ("1.2.3.4:5678", addr_v4.ToString());
+
+#if defined(NDEBUG)
+  EXPECT_EQ("1.2.3.x", addr_v4.HostAsSensitiveURIString());
+  EXPECT_EQ("1.2.3.x:5678", addr_v4.ToSensitiveString());
+#else
+  EXPECT_EQ("1.2.3.4", addr_v4.HostAsSensitiveURIString());
+  EXPECT_EQ("1.2.3.4:5678", addr_v4.ToSensitiveString());
+#endif  // defined(NDEBUG)
+
+  SocketAddress addr_v6(kTestV6AddrString, 5678);
+  EXPECT_EQ("[" + kTestV6AddrString + "]", addr_v6.HostAsURIString());
+  EXPECT_EQ(kTestV6AddrFullString, addr_v6.ToString());
+#if defined(NDEBUG)
+  EXPECT_EQ("[" + kTestV6AddrAnonymizedString + "]",
+            addr_v6.HostAsSensitiveURIString());
+  EXPECT_EQ(kTestV6AddrFullAnonymizedString, addr_v6.ToSensitiveString());
+#else
+  EXPECT_EQ("[" + kTestV6AddrString + "]", addr_v6.HostAsSensitiveURIString());
+  EXPECT_EQ(kTestV6AddrFullString, addr_v6.ToSensitiveString());
+#endif  // defined(NDEBUG)
+}
+
+}  // namespace rtc
diff --git a/base/socketaddresspair.cc b/base/socketaddresspair.cc
new file mode 100644
index 0000000..dfa8b25
--- /dev/null
+++ b/base/socketaddresspair.cc
@@ -0,0 +1,41 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/socketaddresspair.h"
+
+namespace rtc {
+
+SocketAddressPair::SocketAddressPair(
+    const SocketAddress& src, const SocketAddress& dest)
+    : src_(src), dest_(dest) {
+}
+
+
+bool SocketAddressPair::operator ==(const SocketAddressPair& p) const {
+  return (src_ == p.src_) && (dest_ == p.dest_);
+}
+
+bool SocketAddressPair::operator <(const SocketAddressPair& p) const {
+  if (src_ < p.src_)
+    return true;
+  if (p.src_ < src_)
+    return false;
+  if (dest_ < p.dest_)
+    return true;
+  if (p.dest_ < dest_)
+    return false;
+  return false;
+}
+
+size_t SocketAddressPair::Hash() const {
+  return src_.Hash() ^ dest_.Hash();
+}
+
+} // namespace rtc
diff --git a/base/socketaddresspair.h b/base/socketaddresspair.h
index 3f53f10..73a627f 100644
--- a/base/socketaddresspair.h
+++ b/base/socketaddresspair.h
@@ -8,12 +8,34 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_SOCKETADDRESSPAIR_H_
-#define WEBRTC_BASE_SOCKETADDRESSPAIR_H_
+#ifndef WEBRTC_BASE_SOCKETADDRESSPAIR_H__
+#define WEBRTC_BASE_SOCKETADDRESSPAIR_H__
 
+#include "webrtc/base/socketaddress.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/socketaddresspair.h"
+namespace rtc {
 
-#endif // WEBRTC_BASE_SOCKETADDRESSPAIR_H_
+// Records a pair (source,destination) of socket addresses.  The two addresses
+// identify a connection between two machines.  (For UDP, this "connection" is
+// not maintained explicitly in a socket.)
+class SocketAddressPair {
+public:
+  SocketAddressPair() {}
+  SocketAddressPair(const SocketAddress& srs, const SocketAddress& dest);
+
+  const SocketAddress& source() const { return src_; }
+  const SocketAddress& destination() const { return dest_; }
+
+  bool operator ==(const SocketAddressPair& r) const;
+  bool operator <(const SocketAddressPair& r) const;
+
+  size_t Hash() const;
+
+private:
+  SocketAddress src_;
+  SocketAddress dest_;
+};
+
+} // namespace rtc
+
+#endif // WEBRTC_BASE_SOCKETADDRESSPAIR_H__
diff --git a/base/socketfactory.h b/base/socketfactory.h
index 3a829ac..fe0f32b 100644
--- a/base/socketfactory.h
+++ b/base/socketfactory.h
@@ -8,12 +8,31 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_SOCKETFACTORY_H_
-#define WEBRTC_BASE_SOCKETFACTORY_H_
+#ifndef WEBRTC_BASE_SOCKETFACTORY_H__
+#define WEBRTC_BASE_SOCKETFACTORY_H__
 
+#include "webrtc/base/socket.h"
+#include "webrtc/base/asyncsocket.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/socketfactory.h"
+namespace rtc {
 
-#endif // WEBRTC_BASE_SOCKETFACTORY_H_
+class SocketFactory {
+public:
+  virtual ~SocketFactory() {}
+
+  // Returns a new socket for blocking communication.  The type can be
+  // SOCK_DGRAM and SOCK_STREAM.
+  // TODO: C++ inheritance rules mean that all users must have both
+  // CreateSocket(int) and CreateSocket(int,int). Will remove CreateSocket(int)
+  // (and CreateAsyncSocket(int) when all callers are changed.
+  virtual Socket* CreateSocket(int type) = 0;
+  virtual Socket* CreateSocket(int family, int type) = 0;
+  // Returns a new socket for nonblocking communication.  The type can be
+  // SOCK_DGRAM and SOCK_STREAM.
+  virtual AsyncSocket* CreateAsyncSocket(int type) = 0;
+  virtual AsyncSocket* CreateAsyncSocket(int family, int type) = 0;
+};
+
+} // namespace rtc
+
+#endif // WEBRTC_BASE_SOCKETFACTORY_H__
diff --git a/base/socketserver.h b/base/socketserver.h
index 55b427d..5eada4a 100644
--- a/base/socketserver.h
+++ b/base/socketserver.h
@@ -11,9 +11,52 @@
 #ifndef WEBRTC_BASE_SOCKETSERVER_H_
 #define WEBRTC_BASE_SOCKETSERVER_H_
 
+#include <memory>
+#include "webrtc/base/socketfactory.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/socketserver.h"
+namespace rtc {
+
+class MessageQueue;
+// Needs to be forward declared because there's a circular dependency between
+// NetworkMonitor and Thread.
+// TODO(deadbeef): Fix this.
+class NetworkBinderInterface;
+
+// Provides the ability to wait for activity on a set of sockets.  The Thread
+// class provides a nice wrapper on a socket server.
+//
+// The server is also a socket factory.  The sockets it creates will be
+// notified of asynchronous I/O from this server's Wait method.
+class SocketServer : public SocketFactory {
+ public:
+  static const int kForever = -1;
+
+  static std::unique_ptr<SocketServer> CreateDefault();
+  // When the socket server is installed into a Thread, this function is
+  // called to allow the socket server to use the thread's message queue for
+  // any messaging that it might need to perform.
+  virtual void SetMessageQueue(MessageQueue* queue) {}
+
+  // Sleeps until:
+  //  1) cms milliseconds have elapsed (unless cms == kForever)
+  //  2) WakeUp() is called
+  // While sleeping, I/O is performed if process_io is true.
+  virtual bool Wait(int cms, bool process_io) = 0;
+
+  // Causes the current wait (if one is in progress) to wake up.
+  virtual void WakeUp() = 0;
+
+  // A network binder will bind the created sockets to a network.
+  // It is only used in PhysicalSocketServer.
+  void set_network_binder(NetworkBinderInterface* binder) {
+    network_binder_ = binder;
+  }
+  NetworkBinderInterface* network_binder() const { return network_binder_; }
+
+ private:
+  NetworkBinderInterface* network_binder_ = nullptr;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SOCKETSERVER_H_
diff --git a/base/socketstream.cc b/base/socketstream.cc
new file mode 100644
index 0000000..a218dd8
--- /dev/null
+++ b/base/socketstream.cc
@@ -0,0 +1,123 @@
+/*
+ *  Copyright 2010 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/socketstream.h"
+
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+SocketStream::SocketStream(AsyncSocket* socket) : socket_(nullptr) {
+  Attach(socket);
+}
+
+SocketStream::~SocketStream() {
+  delete socket_;
+}
+
+void SocketStream::Attach(AsyncSocket* socket) {
+  if (socket_)
+    delete socket_;
+  socket_ = socket;
+  if (socket_) {
+    socket_->SignalConnectEvent.connect(this, &SocketStream::OnConnectEvent);
+    socket_->SignalReadEvent.connect(this,    &SocketStream::OnReadEvent);
+    socket_->SignalWriteEvent.connect(this,   &SocketStream::OnWriteEvent);
+    socket_->SignalCloseEvent.connect(this,   &SocketStream::OnCloseEvent);
+  }
+}
+
+AsyncSocket* SocketStream::Detach() {
+  AsyncSocket* socket = socket_;
+  if (socket_) {
+    socket_->SignalConnectEvent.disconnect(this);
+    socket_->SignalReadEvent.disconnect(this);
+    socket_->SignalWriteEvent.disconnect(this);
+    socket_->SignalCloseEvent.disconnect(this);
+    socket_ = nullptr;
+  }
+  return socket;
+}
+
+StreamState SocketStream::GetState() const {
+  RTC_DCHECK(socket_ != nullptr);
+  switch (socket_->GetState()) {
+    case Socket::CS_CONNECTED:
+      return SS_OPEN;
+    case Socket::CS_CONNECTING:
+      return SS_OPENING;
+    case Socket::CS_CLOSED:
+    default:
+      return SS_CLOSED;
+  }
+}
+
+StreamResult SocketStream::Read(void* buffer, size_t buffer_len,
+                                size_t* read, int* error) {
+  RTC_DCHECK(socket_ != nullptr);
+  int result = socket_->Recv(buffer, buffer_len, nullptr);
+  if (result < 0) {
+    if (socket_->IsBlocking())
+      return SR_BLOCK;
+    if (error)
+      *error = socket_->GetError();
+    return SR_ERROR;
+  }
+  if ((result > 0) || (buffer_len == 0)) {
+    if (read)
+      *read = result;
+    return SR_SUCCESS;
+  }
+  return SR_EOS;
+}
+
+StreamResult SocketStream::Write(const void* data, size_t data_len,
+                                 size_t* written, int* error) {
+  RTC_DCHECK(socket_ != nullptr);
+  int result = socket_->Send(data, data_len);
+  if (result < 0) {
+    if (socket_->IsBlocking())
+      return SR_BLOCK;
+    if (error)
+      *error = socket_->GetError();
+    return SR_ERROR;
+  }
+  if (written)
+    *written = result;
+  return SR_SUCCESS;
+}
+
+void SocketStream::Close() {
+  RTC_DCHECK(socket_ != nullptr);
+  socket_->Close();
+}
+
+void SocketStream::OnConnectEvent(AsyncSocket* socket) {
+  RTC_DCHECK(socket == socket_);
+  SignalEvent(this, SE_OPEN | SE_READ | SE_WRITE, 0);
+}
+
+void SocketStream::OnReadEvent(AsyncSocket* socket) {
+  RTC_DCHECK(socket == socket_);
+  SignalEvent(this, SE_READ, 0);
+}
+
+void SocketStream::OnWriteEvent(AsyncSocket* socket) {
+  RTC_DCHECK(socket == socket_);
+  SignalEvent(this, SE_WRITE, 0);
+}
+
+void SocketStream::OnCloseEvent(AsyncSocket* socket, int err) {
+  RTC_DCHECK(socket == socket_);
+  SignalEvent(this, SE_CLOSE, err);
+}
+
+
+}  // namespace rtc
diff --git a/base/socketstream.h b/base/socketstream.h
index a76ffb3..a1f8ad9 100644
--- a/base/socketstream.h
+++ b/base/socketstream.h
@@ -11,9 +11,51 @@
 #ifndef WEBRTC_BASE_SOCKETSTREAM_H_
 #define WEBRTC_BASE_SOCKETSTREAM_H_
 
+#include "webrtc/base/asyncsocket.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/stream.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/socketstream.h"
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SocketStream : public StreamInterface, public sigslot::has_slots<> {
+ public:
+  explicit SocketStream(AsyncSocket* socket);
+  ~SocketStream() override;
+
+  void Attach(AsyncSocket* socket);
+  AsyncSocket* Detach();
+
+  AsyncSocket* GetSocket() { return socket_; }
+
+  StreamState GetState() const override;
+
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override;
+
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+
+  void Close() override;
+
+ private:
+  void OnConnectEvent(AsyncSocket* socket);
+  void OnReadEvent(AsyncSocket* socket);
+  void OnWriteEvent(AsyncSocket* socket);
+  void OnCloseEvent(AsyncSocket* socket, int err);
+
+  AsyncSocket* socket_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SocketStream);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SOCKETSTREAM_H_
diff --git a/base/ssladapter.cc b/base/ssladapter.cc
new file mode 100644
index 0000000..06fce54
--- /dev/null
+++ b/base/ssladapter.cc
@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/ssladapter.h"
+
+#include "webrtc/base/openssladapter.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace rtc {
+
+SSLAdapter*
+SSLAdapter::Create(AsyncSocket* socket) {
+  return new OpenSSLAdapter(socket);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool InitializeSSL(VerificationCallback callback) {
+  return OpenSSLAdapter::InitializeSSL(callback);
+}
+
+bool InitializeSSLThread() {
+  return OpenSSLAdapter::InitializeSSLThread();
+}
+
+bool CleanupSSL() {
+  return OpenSSLAdapter::CleanupSSL();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
diff --git a/base/ssladapter.h b/base/ssladapter.h
index 3d432ec..0317544 100644
--- a/base/ssladapter.h
+++ b/base/ssladapter.h
@@ -11,9 +11,55 @@
 #ifndef WEBRTC_BASE_SSLADAPTER_H_
 #define WEBRTC_BASE_SSLADAPTER_H_
 
+#include "webrtc/base/asyncsocket.h"
+#include "webrtc/base/sslstreamadapter.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/ssladapter.h"
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SSLAdapter : public AsyncSocketAdapter {
+ public:
+  explicit SSLAdapter(AsyncSocket* socket)
+    : AsyncSocketAdapter(socket), ignore_bad_cert_(false) { }
+
+  bool ignore_bad_cert() const { return ignore_bad_cert_; }
+  void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; }
+
+  // Do DTLS or TLS (default is TLS, if unspecified)
+  virtual void SetMode(SSLMode mode) = 0;
+
+  // StartSSL returns 0 if successful.
+  // If StartSSL is called while the socket is closed or connecting, the SSL
+  // negotiation will begin as soon as the socket connects.
+  virtual int StartSSL(const char* hostname, bool restartable) = 0;
+
+  // Create the default SSL adapter for this platform. On failure, returns null
+  // and deletes |socket|. Otherwise, the returned SSLAdapter takes ownership
+  // of |socket|.
+  static SSLAdapter* Create(AsyncSocket* socket);
+
+ private:
+  // If true, the server certificate need not match the configured hostname.
+  bool ignore_bad_cert_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef bool (*VerificationCallback)(void* cert);
+
+// Call this on the main thread, before using SSL.
+// Call CleanupSSLThread when finished with SSL.
+bool InitializeSSL(VerificationCallback callback = nullptr);
+
+// Call to initialize additional threads.
+bool InitializeSSLThread();
+
+// Call to cleanup additional threads, and also the main thread.
+bool CleanupSSL();
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SSLADAPTER_H_
diff --git a/base/ssladapter_unittest.cc b/base/ssladapter_unittest.cc
new file mode 100644
index 0000000..0eaac17
--- /dev/null
+++ b/base/ssladapter_unittest.cc
@@ -0,0 +1,459 @@
+/*
+ *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <string>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/ipaddress.h"
+#include "webrtc/base/socketstream.h"
+#include "webrtc/base/ssladapter.h"
+#include "webrtc/base/sslidentity.h"
+#include "webrtc/base/sslstreamadapter.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/virtualsocketserver.h"
+
+static const int kTimeout = 5000;
+
+static rtc::AsyncSocket* CreateSocket(const rtc::SSLMode& ssl_mode) {
+  rtc::SocketAddress address(rtc::IPAddress(INADDR_ANY), 0);
+
+  rtc::AsyncSocket* socket = rtc::Thread::Current()->
+      socketserver()->CreateAsyncSocket(
+      address.family(), (ssl_mode == rtc::SSL_MODE_DTLS) ?
+      SOCK_DGRAM : SOCK_STREAM);
+  socket->Bind(address);
+
+  return socket;
+}
+
+static std::string GetSSLProtocolName(const rtc::SSLMode& ssl_mode) {
+  return (ssl_mode == rtc::SSL_MODE_DTLS) ? "DTLS" : "TLS";
+}
+
+class SSLAdapterTestDummyClient : public sigslot::has_slots<> {
+ public:
+  explicit SSLAdapterTestDummyClient(const rtc::SSLMode& ssl_mode)
+      : ssl_mode_(ssl_mode) {
+    rtc::AsyncSocket* socket = CreateSocket(ssl_mode_);
+
+    ssl_adapter_.reset(rtc::SSLAdapter::Create(socket));
+
+    ssl_adapter_->SetMode(ssl_mode_);
+
+    // Ignore any certificate errors for the purpose of testing.
+    // Note: We do this only because we don't have a real certificate.
+    // NEVER USE THIS IN PRODUCTION CODE!
+    ssl_adapter_->set_ignore_bad_cert(true);
+
+    ssl_adapter_->SignalReadEvent.connect(this,
+        &SSLAdapterTestDummyClient::OnSSLAdapterReadEvent);
+    ssl_adapter_->SignalCloseEvent.connect(this,
+        &SSLAdapterTestDummyClient::OnSSLAdapterCloseEvent);
+  }
+
+  rtc::SocketAddress GetAddress() const {
+    return ssl_adapter_->GetLocalAddress();
+  }
+
+  rtc::AsyncSocket::ConnState GetState() const {
+    return ssl_adapter_->GetState();
+  }
+
+  const std::string& GetReceivedData() const {
+    return data_;
+  }
+
+  int Connect(const std::string& hostname, const rtc::SocketAddress& address) {
+    LOG(LS_INFO) << "Initiating connection with " << address;
+
+    int rv = ssl_adapter_->Connect(address);
+
+    if (rv == 0) {
+      LOG(LS_INFO) << "Starting " << GetSSLProtocolName(ssl_mode_)
+          << " handshake with " << hostname;
+
+      if (ssl_adapter_->StartSSL(hostname.c_str(), false) != 0) {
+        return -1;
+      }
+    }
+
+    return rv;
+  }
+
+  int Close() {
+    return ssl_adapter_->Close();
+  }
+
+  int Send(const std::string& message) {
+    LOG(LS_INFO) << "Client sending '" << message << "'";
+
+    return ssl_adapter_->Send(message.data(), message.length());
+  }
+
+  void OnSSLAdapterReadEvent(rtc::AsyncSocket* socket) {
+    char buffer[4096] = "";
+
+    // Read data received from the server and store it in our internal buffer.
+    int read = socket->Recv(buffer, sizeof(buffer) - 1, nullptr);
+    if (read != -1) {
+      buffer[read] = '\0';
+
+      LOG(LS_INFO) << "Client received '" << buffer << "'";
+
+      data_ += buffer;
+    }
+  }
+
+  void OnSSLAdapterCloseEvent(rtc::AsyncSocket* socket, int error) {
+    // OpenSSLAdapter signals handshake failure with a close event, but without
+    // closing the socket! Let's close the socket here. This way GetState() can
+    // return CS_CLOSED after failure.
+    if (socket->GetState() != rtc::AsyncSocket::CS_CLOSED) {
+      socket->Close();
+    }
+  }
+
+ private:
+  const rtc::SSLMode ssl_mode_;
+
+  std::unique_ptr<rtc::SSLAdapter> ssl_adapter_;
+
+  std::string data_;
+};
+
+class SSLAdapterTestDummyServer : public sigslot::has_slots<> {
+ public:
+  explicit SSLAdapterTestDummyServer(const rtc::SSLMode& ssl_mode,
+                                     const rtc::KeyParams& key_params)
+      : ssl_mode_(ssl_mode) {
+    // Generate a key pair and a certificate for this host.
+    ssl_identity_.reset(rtc::SSLIdentity::Generate(GetHostname(), key_params));
+
+    server_socket_.reset(CreateSocket(ssl_mode_));
+
+    if (ssl_mode_ == rtc::SSL_MODE_TLS) {
+      server_socket_->SignalReadEvent.connect(this,
+          &SSLAdapterTestDummyServer::OnServerSocketReadEvent);
+
+      server_socket_->Listen(1);
+    }
+
+    LOG(LS_INFO) << ((ssl_mode_ == rtc::SSL_MODE_DTLS) ? "UDP" : "TCP")
+        << " server listening on " << server_socket_->GetLocalAddress();
+  }
+
+  rtc::SocketAddress GetAddress() const {
+    return server_socket_->GetLocalAddress();
+  }
+
+  std::string GetHostname() const {
+    // Since we don't have a real certificate anyway, the value here doesn't
+    // really matter.
+    return "example.com";
+  }
+
+  const std::string& GetReceivedData() const {
+    return data_;
+  }
+
+  int Send(const std::string& message) {
+    if (ssl_stream_adapter_ == nullptr ||
+        ssl_stream_adapter_->GetState() != rtc::SS_OPEN) {
+      // No connection yet.
+      return -1;
+    }
+
+    LOG(LS_INFO) << "Server sending '" << message << "'";
+
+    size_t written;
+    int error;
+
+    rtc::StreamResult r = ssl_stream_adapter_->Write(message.data(),
+        message.length(), &written, &error);
+    if (r == rtc::SR_SUCCESS) {
+      return written;
+    } else {
+      return -1;
+    }
+  }
+
+  void AcceptConnection(const rtc::SocketAddress& address) {
+    // Only a single connection is supported.
+    ASSERT_TRUE(ssl_stream_adapter_ == nullptr);
+
+    // This is only for DTLS.
+    ASSERT_EQ(rtc::SSL_MODE_DTLS, ssl_mode_);
+
+    // Transfer ownership of the socket to the SSLStreamAdapter object.
+    rtc::AsyncSocket* socket = server_socket_.release();
+
+    socket->Connect(address);
+
+    DoHandshake(socket);
+  }
+
+  void OnServerSocketReadEvent(rtc::AsyncSocket* socket) {
+    // Only a single connection is supported.
+    ASSERT_TRUE(ssl_stream_adapter_ == nullptr);
+
+    DoHandshake(server_socket_->Accept(nullptr));
+  }
+
+  void OnSSLStreamAdapterEvent(rtc::StreamInterface* stream, int sig, int err) {
+    if (sig & rtc::SE_READ) {
+      char buffer[4096] = "";
+      size_t read;
+      int error;
+
+      // Read data received from the client and store it in our internal
+      // buffer.
+      rtc::StreamResult r =
+          stream->Read(buffer, sizeof(buffer) - 1, &read, &error);
+      if (r == rtc::SR_SUCCESS) {
+        buffer[read] = '\0';
+        LOG(LS_INFO) << "Server received '" << buffer << "'";
+        data_ += buffer;
+      }
+    }
+  }
+
+ private:
+  void DoHandshake(rtc::AsyncSocket* socket) {
+    rtc::SocketStream* stream = new rtc::SocketStream(socket);
+
+    ssl_stream_adapter_.reset(rtc::SSLStreamAdapter::Create(stream));
+
+    ssl_stream_adapter_->SetMode(ssl_mode_);
+    ssl_stream_adapter_->SetServerRole();
+
+    // SSLStreamAdapter is normally used for peer-to-peer communication, but
+    // here we're testing communication between a client and a server
+    // (e.g. a WebRTC-based application and an RFC 5766 TURN server), where
+    // clients are not required to provide a certificate during handshake.
+    // Accordingly, we must disable client authentication here.
+    ssl_stream_adapter_->set_client_auth_enabled(false);
+
+    ssl_stream_adapter_->SetIdentity(ssl_identity_->GetReference());
+
+    // Set a bogus peer certificate digest.
+    unsigned char digest[20];
+    size_t digest_len = sizeof(digest);
+    ssl_stream_adapter_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, digest,
+        digest_len);
+
+    ssl_stream_adapter_->StartSSL();
+
+    ssl_stream_adapter_->SignalEvent.connect(this,
+        &SSLAdapterTestDummyServer::OnSSLStreamAdapterEvent);
+  }
+
+  const rtc::SSLMode ssl_mode_;
+
+  std::unique_ptr<rtc::AsyncSocket> server_socket_;
+  std::unique_ptr<rtc::SSLStreamAdapter> ssl_stream_adapter_;
+
+  std::unique_ptr<rtc::SSLIdentity> ssl_identity_;
+
+  std::string data_;
+};
+
+class SSLAdapterTestBase : public testing::Test,
+                           public sigslot::has_slots<> {
+ public:
+  explicit SSLAdapterTestBase(const rtc::SSLMode& ssl_mode,
+                              const rtc::KeyParams& key_params)
+      : ssl_mode_(ssl_mode),
+        vss_(new rtc::VirtualSocketServer()),
+        thread_(vss_.get()),
+        server_(new SSLAdapterTestDummyServer(ssl_mode_, key_params)),
+        client_(new SSLAdapterTestDummyClient(ssl_mode_)),
+        handshake_wait_(kTimeout) {}
+
+  void SetHandshakeWait(int wait) {
+    handshake_wait_ = wait;
+  }
+
+  void TestHandshake(bool expect_success) {
+    int rv;
+
+    // The initial state is CS_CLOSED
+    ASSERT_EQ(rtc::AsyncSocket::CS_CLOSED, client_->GetState());
+
+    rv = client_->Connect(server_->GetHostname(), server_->GetAddress());
+    ASSERT_EQ(0, rv);
+
+    // Now the state should be CS_CONNECTING
+    ASSERT_EQ(rtc::AsyncSocket::CS_CONNECTING, client_->GetState());
+
+    if (ssl_mode_ == rtc::SSL_MODE_DTLS) {
+      // For DTLS, call AcceptConnection() with the client's address.
+      server_->AcceptConnection(client_->GetAddress());
+    }
+
+    if (expect_success) {
+      // If expecting success, the client should end up in the CS_CONNECTED
+      // state after handshake.
+      EXPECT_EQ_WAIT(rtc::AsyncSocket::CS_CONNECTED, client_->GetState(),
+          handshake_wait_);
+
+      LOG(LS_INFO) << GetSSLProtocolName(ssl_mode_) << " handshake complete.";
+
+    } else {
+      // On handshake failure the client should end up in the CS_CLOSED state.
+      EXPECT_EQ_WAIT(rtc::AsyncSocket::CS_CLOSED, client_->GetState(),
+          handshake_wait_);
+
+      LOG(LS_INFO) << GetSSLProtocolName(ssl_mode_) << " handshake failed.";
+    }
+  }
+
+  void TestTransfer(const std::string& message) {
+    int rv;
+
+    rv = client_->Send(message);
+    ASSERT_EQ(static_cast<int>(message.length()), rv);
+
+    // The server should have received the client's message.
+    EXPECT_EQ_WAIT(message, server_->GetReceivedData(), kTimeout);
+
+    rv = server_->Send(message);
+    ASSERT_EQ(static_cast<int>(message.length()), rv);
+
+    // The client should have received the server's message.
+    EXPECT_EQ_WAIT(message, client_->GetReceivedData(), kTimeout);
+
+    LOG(LS_INFO) << "Transfer complete.";
+  }
+
+ protected:
+  const rtc::SSLMode ssl_mode_;
+
+  std::unique_ptr<rtc::VirtualSocketServer> vss_;
+  rtc::AutoSocketServerThread thread_;
+  std::unique_ptr<SSLAdapterTestDummyServer> server_;
+  std::unique_ptr<SSLAdapterTestDummyClient> client_;
+
+  int handshake_wait_;
+};
+
+class SSLAdapterTestTLS_RSA : public SSLAdapterTestBase {
+ public:
+  SSLAdapterTestTLS_RSA()
+      : SSLAdapterTestBase(rtc::SSL_MODE_TLS, rtc::KeyParams::RSA()) {}
+};
+
+class SSLAdapterTestTLS_ECDSA : public SSLAdapterTestBase {
+ public:
+  SSLAdapterTestTLS_ECDSA()
+      : SSLAdapterTestBase(rtc::SSL_MODE_TLS, rtc::KeyParams::ECDSA()) {}
+};
+
+class SSLAdapterTestDTLS_RSA : public SSLAdapterTestBase {
+ public:
+  SSLAdapterTestDTLS_RSA()
+      : SSLAdapterTestBase(rtc::SSL_MODE_DTLS, rtc::KeyParams::RSA()) {}
+};
+
+class SSLAdapterTestDTLS_ECDSA : public SSLAdapterTestBase {
+ public:
+  SSLAdapterTestDTLS_ECDSA()
+      : SSLAdapterTestBase(rtc::SSL_MODE_DTLS, rtc::KeyParams::ECDSA()) {}
+};
+
+// Basic tests: TLS
+
+// Test that handshake works, using RSA
+TEST_F(SSLAdapterTestTLS_RSA, TestTLSConnect) {
+  TestHandshake(true);
+}
+
+// Test that handshake works, using ECDSA
+TEST_F(SSLAdapterTestTLS_ECDSA, TestTLSConnect) {
+  TestHandshake(true);
+}
+
+// Test transfer between client and server, using RSA
+TEST_F(SSLAdapterTestTLS_RSA, TestTLSTransfer) {
+  TestHandshake(true);
+  TestTransfer("Hello, world!");
+}
+
+TEST_F(SSLAdapterTestTLS_RSA, TestTLSTransferWithBlockedSocket) {
+  TestHandshake(true);
+
+  // Tell the underlying socket to simulate being blocked.
+  vss_->SetSendingBlocked(true);
+
+  std::string expected;
+  int rv;
+  // Send messages until the SSL socket adapter starts applying backpressure.
+  // Note that this may not occur immediately since there may be some amount of
+  // intermediate buffering (either in our code or in BoringSSL).
+  for (int i = 0; i < 1024; ++i) {
+    std::string message = "Hello, world: " + rtc::ToString(i);
+    rv = client_->Send(message);
+    if (rv != static_cast<int>(message.size())) {
+      // This test assumes either the whole message or none of it is sent.
+      ASSERT_EQ(-1, rv);
+      break;
+    }
+    expected += message;
+  }
+  // Assert that the loop above exited due to Send returning -1.
+  ASSERT_EQ(-1, rv);
+
+  // Try sending another message while blocked. -1 should be returned again and
+  // it shouldn't end up received by the server later.
+  EXPECT_EQ(-1, client_->Send("Never sent"));
+
+  // Unblock the underlying socket. All of the buffered messages should be sent
+  // without any further action.
+  vss_->SetSendingBlocked(false);
+  EXPECT_EQ_WAIT(expected, server_->GetReceivedData(), kTimeout);
+
+  // Send another message. This previously wasn't working
+  std::string final_message = "Fin.";
+  expected += final_message;
+  EXPECT_EQ(static_cast<int>(final_message.size()),
+            client_->Send(final_message));
+  EXPECT_EQ_WAIT(expected, server_->GetReceivedData(), kTimeout);
+}
+
+// Test transfer between client and server, using ECDSA
+TEST_F(SSLAdapterTestTLS_ECDSA, TestTLSTransfer) {
+  TestHandshake(true);
+  TestTransfer("Hello, world!");
+}
+
+// Basic tests: DTLS
+
+// Test that handshake works, using RSA
+TEST_F(SSLAdapterTestDTLS_RSA, TestDTLSConnect) {
+  TestHandshake(true);
+}
+
+// Test that handshake works, using ECDSA
+TEST_F(SSLAdapterTestDTLS_ECDSA, TestDTLSConnect) {
+  TestHandshake(true);
+}
+
+// Test transfer between client and server, using RSA
+TEST_F(SSLAdapterTestDTLS_RSA, TestDTLSTransfer) {
+  TestHandshake(true);
+  TestTransfer("Hello, world!");
+}
+
+// Test transfer between client and server, using ECDSA
+TEST_F(SSLAdapterTestDTLS_ECDSA, TestDTLSTransfer) {
+  TestHandshake(true);
+  TestTransfer("Hello, world!");
+}
diff --git a/base/sslfingerprint.cc b/base/sslfingerprint.cc
new file mode 100644
index 0000000..bd15743
--- /dev/null
+++ b/base/sslfingerprint.cc
@@ -0,0 +1,112 @@
+/*
+ *  Copyright 2012 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/sslfingerprint.h"
+
+#include <ctype.h>
+#include <string>
+
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/messagedigest.h"
+#include "webrtc/base/stringencode.h"
+
+namespace rtc {
+
+SSLFingerprint* SSLFingerprint::Create(
+    const std::string& algorithm, const rtc::SSLIdentity* identity) {
+  if (!identity) {
+    return nullptr;
+  }
+
+  return Create(algorithm, &(identity->certificate()));
+}
+
+SSLFingerprint* SSLFingerprint::Create(
+    const std::string& algorithm, const rtc::SSLCertificate* cert) {
+  uint8_t digest_val[64];
+  size_t digest_len;
+  bool ret = cert->ComputeDigest(
+      algorithm, digest_val, sizeof(digest_val), &digest_len);
+  if (!ret) {
+    return nullptr;
+  }
+
+  return new SSLFingerprint(algorithm, digest_val, digest_len);
+}
+
+SSLFingerprint* SSLFingerprint::CreateFromRfc4572(
+    const std::string& algorithm, const std::string& fingerprint) {
+  if (algorithm.empty() || !rtc::IsFips180DigestAlgorithm(algorithm))
+    return nullptr;
+
+  if (fingerprint.empty())
+    return nullptr;
+
+  size_t value_len;
+  char value[rtc::MessageDigest::kMaxSize];
+  value_len = rtc::hex_decode_with_delimiter(value, sizeof(value),
+                                                   fingerprint.c_str(),
+                                                   fingerprint.length(),
+                                                   ':');
+  if (!value_len)
+    return nullptr;
+
+  return new SSLFingerprint(algorithm, reinterpret_cast<uint8_t*>(value),
+                            value_len);
+}
+
+SSLFingerprint* SSLFingerprint::CreateFromCertificate(
+    const RTCCertificate* cert) {
+  std::string digest_alg;
+  if (!cert->ssl_certificate().GetSignatureDigestAlgorithm(&digest_alg)) {
+    LOG(LS_ERROR) << "Failed to retrieve the certificate's digest algorithm";
+    return nullptr;
+  }
+
+  SSLFingerprint* fingerprint = Create(digest_alg, cert->identity());
+  if (!fingerprint) {
+    LOG(LS_ERROR) << "Failed to create identity fingerprint, alg="
+                  << digest_alg;
+  }
+  return fingerprint;
+}
+
+SSLFingerprint::SSLFingerprint(const std::string& algorithm,
+                               const uint8_t* digest_in,
+                               size_t digest_len)
+    : algorithm(algorithm) {
+  digest.SetData(digest_in, digest_len);
+}
+
+SSLFingerprint::SSLFingerprint(const SSLFingerprint& from)
+    : algorithm(from.algorithm), digest(from.digest) {}
+
+bool SSLFingerprint::operator==(const SSLFingerprint& other) const {
+  return algorithm == other.algorithm &&
+         digest == other.digest;
+}
+
+std::string SSLFingerprint::GetRfc4572Fingerprint() const {
+  std::string fingerprint =
+      rtc::hex_encode_with_delimiter(digest.data<char>(), digest.size(), ':');
+  std::transform(fingerprint.begin(), fingerprint.end(),
+                 fingerprint.begin(), ::toupper);
+  return fingerprint;
+}
+
+std::string SSLFingerprint::ToString() const {
+  std::string fp_str = algorithm;
+  fp_str.append(" ");
+  fp_str.append(GetRfc4572Fingerprint());
+  return fp_str;
+}
+
+}  // namespace rtc
diff --git a/base/sslfingerprint.h b/base/sslfingerprint.h
index 6be82fd..62b4bc8 100644
--- a/base/sslfingerprint.h
+++ b/base/sslfingerprint.h
@@ -11,9 +11,47 @@
 #ifndef WEBRTC_BASE_SSLFINGERPRINT_H_
 #define WEBRTC_BASE_SSLFINGERPRINT_H_
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sslfingerprint.h"
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/copyonwritebuffer.h"
+#include "webrtc/base/rtccertificate.h"
+#include "webrtc/base/sslidentity.h"
+
+namespace rtc {
+
+class SSLCertificate;
+
+struct SSLFingerprint {
+  static SSLFingerprint* Create(const std::string& algorithm,
+                                const rtc::SSLIdentity* identity);
+
+  static SSLFingerprint* Create(const std::string& algorithm,
+                                const rtc::SSLCertificate* cert);
+
+  static SSLFingerprint* CreateFromRfc4572(const std::string& algorithm,
+                                           const std::string& fingerprint);
+
+  // Creates a fingerprint from a certificate, using the same digest algorithm
+  // as the certificate's signature.
+  static SSLFingerprint* CreateFromCertificate(const RTCCertificate* cert);
+
+  SSLFingerprint(const std::string& algorithm,
+                 const uint8_t* digest_in,
+                 size_t digest_len);
+
+  SSLFingerprint(const SSLFingerprint& from);
+
+  bool operator==(const SSLFingerprint& other) const;
+
+  std::string GetRfc4572Fingerprint() const;
+
+  std::string ToString() const;
+
+  std::string algorithm;
+  rtc::CopyOnWriteBuffer digest;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SSLFINGERPRINT_H_
diff --git a/base/sslidentity.cc b/base/sslidentity.cc
new file mode 100644
index 0000000..a5dd7b9
--- /dev/null
+++ b/base/sslidentity.cc
@@ -0,0 +1,325 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Handling of certificates and keypairs for SSLStreamAdapter's peer mode.
+#include "webrtc/base/sslidentity.h"
+
+#include <ctime>
+#include <string>
+
+#include "webrtc/base/base64.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/opensslidentity.h"
+#include "webrtc/base/sslfingerprint.h"
+
+namespace rtc {
+
+const char kPemTypeCertificate[] = "CERTIFICATE";
+const char kPemTypeRsaPrivateKey[] = "RSA PRIVATE KEY";
+const char kPemTypeEcPrivateKey[] = "EC PRIVATE KEY";
+
+SSLCertificateStats::SSLCertificateStats(
+    std::string&& fingerprint,
+    std::string&& fingerprint_algorithm,
+    std::string&& base64_certificate,
+    std::unique_ptr<SSLCertificateStats>&& issuer)
+    : fingerprint(std::move(fingerprint)),
+      fingerprint_algorithm(std::move(fingerprint_algorithm)),
+      base64_certificate(std::move(base64_certificate)),
+      issuer(std::move(issuer)) {
+}
+
+SSLCertificateStats::~SSLCertificateStats() {
+}
+
+std::unique_ptr<SSLCertificateStats> SSLCertificate::GetStats() const {
+  // We have a certificate and optionally a chain of certificates. This forms a
+  // linked list, starting with |this|, then the first element of |chain| and
+  // ending with the last element of |chain|. The "issuer" of a certificate is
+  // the next certificate in the chain. Stats are produced for each certificate
+  // in the list. Here, the "issuer" is the issuer's stats.
+  std::unique_ptr<SSLCertChain> chain = GetChain();
+  std::unique_ptr<SSLCertificateStats> issuer;
+  if (chain) {
+    // The loop runs in reverse so that the |issuer| is known before the
+    // |cert|'s stats.
+    for (ptrdiff_t i = chain->GetSize() - 1; i >= 0; --i) {
+      const SSLCertificate* cert = &chain->Get(i);
+      issuer = cert->GetStats(std::move(issuer));
+    }
+  }
+  return GetStats(std::move(issuer));
+}
+
+std::unique_ptr<SSLCertificateStats> SSLCertificate::GetStats(
+    std::unique_ptr<SSLCertificateStats> issuer) const {
+  // TODO(bemasc): Move this computation to a helper class that caches these
+  // values to reduce CPU use in |StatsCollector::GetStats|. This will require
+  // adding a fast |SSLCertificate::Equals| to detect certificate changes.
+  std::string digest_algorithm;
+  if (!GetSignatureDigestAlgorithm(&digest_algorithm))
+    return nullptr;
+
+  // |SSLFingerprint::Create| can fail if the algorithm returned by
+  // |SSLCertificate::GetSignatureDigestAlgorithm| is not supported by the
+  // implementation of |SSLCertificate::ComputeDigest|. This currently happens
+  // with MD5- and SHA-224-signed certificates when linked to libNSS.
+  std::unique_ptr<SSLFingerprint> ssl_fingerprint(
+      SSLFingerprint::Create(digest_algorithm, this));
+  if (!ssl_fingerprint)
+    return nullptr;
+  std::string fingerprint = ssl_fingerprint->GetRfc4572Fingerprint();
+
+  Buffer der_buffer;
+  ToDER(&der_buffer);
+  std::string der_base64;
+  Base64::EncodeFromArray(der_buffer.data(), der_buffer.size(), &der_base64);
+
+  return std::unique_ptr<SSLCertificateStats>(new SSLCertificateStats(
+      std::move(fingerprint),
+      std::move(digest_algorithm),
+      std::move(der_base64),
+      std::move(issuer)));
+}
+
+KeyParams::KeyParams(KeyType key_type) {
+  if (key_type == KT_ECDSA) {
+    type_ = KT_ECDSA;
+    params_.curve = EC_NIST_P256;
+  } else if (key_type == KT_RSA) {
+    type_ = KT_RSA;
+    params_.rsa.mod_size = kRsaDefaultModSize;
+    params_.rsa.pub_exp = kRsaDefaultExponent;
+  } else {
+    RTC_NOTREACHED();
+  }
+}
+
+// static
+KeyParams KeyParams::RSA(int mod_size, int pub_exp) {
+  KeyParams kt(KT_RSA);
+  kt.params_.rsa.mod_size = mod_size;
+  kt.params_.rsa.pub_exp = pub_exp;
+  return kt;
+}
+
+// static
+KeyParams KeyParams::ECDSA(ECCurve curve) {
+  KeyParams kt(KT_ECDSA);
+  kt.params_.curve = curve;
+  return kt;
+}
+
+bool KeyParams::IsValid() const {
+  if (type_ == KT_RSA) {
+    return (params_.rsa.mod_size >= kRsaMinModSize &&
+            params_.rsa.mod_size <= kRsaMaxModSize &&
+            params_.rsa.pub_exp > params_.rsa.mod_size);
+  } else if (type_ == KT_ECDSA) {
+    return (params_.curve == EC_NIST_P256);
+  }
+  return false;
+}
+
+RSAParams KeyParams::rsa_params() const {
+  RTC_DCHECK(type_ == KT_RSA);
+  return params_.rsa;
+}
+
+ECCurve KeyParams::ec_curve() const {
+  RTC_DCHECK(type_ == KT_ECDSA);
+  return params_.curve;
+}
+
+KeyType IntKeyTypeFamilyToKeyType(int key_type_family) {
+  return static_cast<KeyType>(key_type_family);
+}
+
+bool SSLIdentity::PemToDer(const std::string& pem_type,
+                           const std::string& pem_string,
+                           std::string* der) {
+  // Find the inner body. We need this to fulfill the contract of
+  // returning pem_length.
+  size_t header = pem_string.find("-----BEGIN " + pem_type + "-----");
+  if (header == std::string::npos)
+    return false;
+
+  size_t body = pem_string.find("\n", header);
+  if (body == std::string::npos)
+    return false;
+
+  size_t trailer = pem_string.find("-----END " + pem_type + "-----");
+  if (trailer == std::string::npos)
+    return false;
+
+  std::string inner = pem_string.substr(body + 1, trailer - (body + 1));
+
+  *der = Base64::Decode(inner, Base64::DO_PARSE_WHITE |
+                        Base64::DO_PAD_ANY |
+                        Base64::DO_TERM_BUFFER);
+  return true;
+}
+
+std::string SSLIdentity::DerToPem(const std::string& pem_type,
+                                  const unsigned char* data,
+                                  size_t length) {
+  std::stringstream result;
+
+  result << "-----BEGIN " << pem_type << "-----\n";
+
+  std::string b64_encoded;
+  Base64::EncodeFromArray(data, length, &b64_encoded);
+
+  // Divide the Base-64 encoded data into 64-character chunks, as per
+  // 4.3.2.4 of RFC 1421.
+  static const size_t kChunkSize = 64;
+  size_t chunks = (b64_encoded.size() + (kChunkSize - 1)) / kChunkSize;
+  for (size_t i = 0, chunk_offset = 0; i < chunks;
+       ++i, chunk_offset += kChunkSize) {
+    result << b64_encoded.substr(chunk_offset, kChunkSize);
+    result << "\n";
+  }
+
+  result << "-----END " << pem_type << "-----\n";
+
+  return result.str();
+}
+
+SSLCertChain::SSLCertChain(const std::vector<SSLCertificate*>& certs) {
+  RTC_DCHECK(!certs.empty());
+  certs_.resize(certs.size());
+  std::transform(certs.begin(), certs.end(), certs_.begin(), DupCert);
+}
+
+SSLCertChain::SSLCertChain(const SSLCertificate* cert) {
+  certs_.push_back(cert->GetReference());
+}
+
+SSLCertChain::~SSLCertChain() {
+  std::for_each(certs_.begin(), certs_.end(), DeleteCert);
+}
+
+// static
+SSLCertificate* SSLCertificate::FromPEMString(const std::string& pem_string) {
+  return OpenSSLCertificate::FromPEMString(pem_string);
+}
+
+// static
+SSLIdentity* SSLIdentity::GenerateWithExpiration(const std::string& common_name,
+                                                 const KeyParams& key_params,
+                                                 time_t certificate_lifetime) {
+  return OpenSSLIdentity::GenerateWithExpiration(common_name, key_params,
+                                                 certificate_lifetime);
+}
+
+// static
+SSLIdentity* SSLIdentity::Generate(const std::string& common_name,
+                                   const KeyParams& key_params) {
+  return OpenSSLIdentity::GenerateWithExpiration(
+      common_name, key_params, kDefaultCertificateLifetimeInSeconds);
+}
+
+// static
+SSLIdentity* SSLIdentity::Generate(const std::string& common_name,
+                                   KeyType key_type) {
+  return OpenSSLIdentity::GenerateWithExpiration(
+      common_name, KeyParams(key_type), kDefaultCertificateLifetimeInSeconds);
+}
+
+SSLIdentity* SSLIdentity::GenerateForTest(const SSLIdentityParams& params) {
+  return OpenSSLIdentity::GenerateForTest(params);
+}
+
+// static
+SSLIdentity* SSLIdentity::FromPEMStrings(const std::string& private_key,
+                                         const std::string& certificate) {
+  return OpenSSLIdentity::FromPEMStrings(private_key, certificate);
+}
+
+bool operator==(const SSLIdentity& a, const SSLIdentity& b) {
+  return static_cast<const OpenSSLIdentity&>(a) ==
+         static_cast<const OpenSSLIdentity&>(b);
+}
+bool operator!=(const SSLIdentity& a, const SSLIdentity& b) {
+  return !(a == b);
+}
+
+// Read |n| bytes from ASN1 number string at *|pp| and return the numeric value.
+// Update *|pp| and *|np| to reflect number of read bytes.
+static inline int ASN1ReadInt(const unsigned char** pp, size_t* np, size_t n) {
+  const unsigned char* p = *pp;
+  int x = 0;
+  for (size_t i = 0; i < n; i++)
+    x = 10 * x + p[i] - '0';
+  *pp = p + n;
+  *np = *np - n;
+  return x;
+}
+
+int64_t ASN1TimeToSec(const unsigned char* s, size_t length, bool long_format) {
+  size_t bytes_left = length;
+
+  // Make sure the string ends with Z.  Doing it here protects the strspn call
+  // from running off the end of the string in Z's absense.
+  if (length == 0 || s[length - 1] != 'Z')
+    return -1;
+
+  // Make sure we only have ASCII digits so that we don't need to clutter the
+  // code below and ASN1ReadInt with error checking.
+  size_t n = strspn(reinterpret_cast<const char*>(s), "0123456789");
+  if (n + 1 != length)
+    return -1;
+
+  int year;
+
+  // Read out ASN1 year, in either 2-char "UTCTIME" or 4-char "GENERALIZEDTIME"
+  // format.  Both format use UTC in this context.
+  if (long_format) {
+    // ASN1 format: yyyymmddhh[mm[ss[.fff]]]Z where the Z is literal, but
+    // RFC 5280 requires us to only support exactly yyyymmddhhmmssZ.
+
+    if (bytes_left < 11)
+      return -1;
+
+    year = ASN1ReadInt(&s, &bytes_left, 4);
+    year -= 1900;
+  } else {
+    // ASN1 format: yymmddhhmm[ss]Z where the Z is literal, but RFC 5280
+    // requires us to only support exactly yymmddhhmmssZ.
+
+    if (bytes_left < 9)
+      return -1;
+
+    year = ASN1ReadInt(&s, &bytes_left, 2);
+    if (year < 50)  // Per RFC 5280 4.1.2.5.1
+      year += 100;
+  }
+
+  std::tm tm;
+  tm.tm_year = year;
+
+  // Read out remaining ASN1 time data and store it in |tm| in documented
+  // std::tm format.
+  tm.tm_mon = ASN1ReadInt(&s, &bytes_left, 2) - 1;
+  tm.tm_mday = ASN1ReadInt(&s, &bytes_left, 2);
+  tm.tm_hour = ASN1ReadInt(&s, &bytes_left, 2);
+  tm.tm_min = ASN1ReadInt(&s, &bytes_left, 2);
+  tm.tm_sec = ASN1ReadInt(&s, &bytes_left, 2);
+
+  if (bytes_left != 1) {
+    // Now just Z should remain.  Its existence was asserted above.
+    return -1;
+  }
+
+  return TmToSeconds(tm);
+}
+
+}  // namespace rtc
diff --git a/base/sslidentity.h b/base/sslidentity.h
index 1cedfa0..263d0dc 100644
--- a/base/sslidentity.h
+++ b/base/sslidentity.h
@@ -13,9 +13,262 @@
 #ifndef WEBRTC_BASE_SSLIDENTITY_H_
 #define WEBRTC_BASE_SSLIDENTITY_H_
 
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sslidentity.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/messagedigest.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+
+// Forward declaration due to circular dependency with SSLCertificate.
+class SSLCertChain;
+
+struct SSLCertificateStats {
+  SSLCertificateStats(std::string&& fingerprint,
+                      std::string&& fingerprint_algorithm,
+                      std::string&& base64_certificate,
+                      std::unique_ptr<SSLCertificateStats>&& issuer);
+  ~SSLCertificateStats();
+  std::string fingerprint;
+  std::string fingerprint_algorithm;
+  std::string base64_certificate;
+  std::unique_ptr<SSLCertificateStats> issuer;
+};
+
+// Abstract interface overridden by SSL library specific
+// implementations.
+
+// A somewhat opaque type used to encapsulate a certificate.
+// Wraps the SSL library's notion of a certificate, with reference counting.
+// The SSLCertificate object is pretty much immutable once created.
+// (The OpenSSL implementation only does reference counting and
+// possibly caching of intermediate results.)
+class SSLCertificate {
+ public:
+  // Parses and builds a certificate from a PEM encoded string.
+  // Returns null on failure.
+  // The length of the string representation of the certificate is
+  // stored in *pem_length if it is non-null, and only if
+  // parsing was successful.
+  // Caller is responsible for freeing the returned object.
+  static SSLCertificate* FromPEMString(const std::string& pem_string);
+  virtual ~SSLCertificate() {}
+
+  // Returns a new SSLCertificate object instance wrapping the same
+  // underlying certificate, including its chain if present.
+  // Caller is responsible for freeing the returned object.
+  virtual SSLCertificate* GetReference() const = 0;
+
+  // Provides the cert chain, or null. The chain includes a copy of each
+  // certificate, excluding the leaf.
+  virtual std::unique_ptr<SSLCertChain> GetChain() const = 0;
+
+  // Returns a PEM encoded string representation of the certificate.
+  virtual std::string ToPEMString() const = 0;
+
+  // Provides a DER encoded binary representation of the certificate.
+  virtual void ToDER(Buffer* der_buffer) const = 0;
+
+  // Gets the name of the digest algorithm that was used to compute this
+  // certificate's signature.
+  virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const = 0;
+
+  // Compute the digest of the certificate given algorithm
+  virtual bool ComputeDigest(const std::string& algorithm,
+                             unsigned char* digest,
+                             size_t size,
+                             size_t* length) const = 0;
+
+  // Returns the time in seconds relative to epoch, 1970-01-01T00:00:00Z (UTC),
+  // or -1 if an expiration time could not be retrieved.
+  virtual int64_t CertificateExpirationTime() const = 0;
+
+  // Gets information (fingerprint, etc.) about this certificate and its chain
+  // (if it has a certificate chain). This is used for certificate stats, see
+  // https://w3c.github.io/webrtc-stats/#certificatestats-dict*.
+  std::unique_ptr<SSLCertificateStats> GetStats() const;
+
+ private:
+  std::unique_ptr<SSLCertificateStats> GetStats(
+    std::unique_ptr<SSLCertificateStats> issuer) const;
+};
+
+// SSLCertChain is a simple wrapper for a vector of SSLCertificates. It serves
+// primarily to ensure proper memory management (especially deletion) of the
+// SSLCertificate pointers.
+class SSLCertChain {
+ public:
+  // These constructors copy the provided SSLCertificate(s), so the caller
+  // retains ownership.
+  explicit SSLCertChain(const std::vector<SSLCertificate*>& certs);
+  explicit SSLCertChain(const SSLCertificate* cert);
+  ~SSLCertChain();
+
+  // Vector access methods.
+  size_t GetSize() const { return certs_.size(); }
+
+  // Returns a temporary reference, only valid until the chain is destroyed.
+  const SSLCertificate& Get(size_t pos) const { return *(certs_[pos]); }
+
+  // Returns a new SSLCertChain object instance wrapping the same underlying
+  // certificate chain.  Caller is responsible for freeing the returned object.
+  SSLCertChain* Copy() const {
+    return new SSLCertChain(certs_);
+  }
+
+ private:
+  // Helper function for duplicating a vector of certificates.
+  static SSLCertificate* DupCert(const SSLCertificate* cert) {
+    return cert->GetReference();
+  }
+
+  // Helper function for deleting a vector of certificates.
+  static void DeleteCert(SSLCertificate* cert) { delete cert; }
+
+  std::vector<SSLCertificate*> certs_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SSLCertChain);
+};
+
+// KT_LAST is intended for vector declarations and loops over all key types;
+// it does not represent any key type in itself.
+// KT_DEFAULT is used as the default KeyType for KeyParams.
+enum KeyType { KT_RSA, KT_ECDSA, KT_LAST, KT_DEFAULT = KT_ECDSA };
+
+static const int kRsaDefaultModSize = 1024;
+static const int kRsaDefaultExponent = 0x10001;  // = 2^16+1 = 65537
+static const int kRsaMinModSize = 1024;
+static const int kRsaMaxModSize = 8192;
+
+// Certificate default validity lifetime.
+static const int kDefaultCertificateLifetimeInSeconds =
+    60 * 60 * 24 * 30;  // 30 days
+// Certificate validity window.
+// This is to compensate for slightly incorrect system clocks.
+static const int kCertificateWindowInSeconds = -60 * 60 * 24;
+
+struct RSAParams {
+  unsigned int mod_size;
+  unsigned int pub_exp;
+};
+
+enum ECCurve { EC_NIST_P256, /* EC_FANCY, */ EC_LAST };
+
+class KeyParams {
+ public:
+  // Generate a KeyParams object from a simple KeyType, using default params.
+  explicit KeyParams(KeyType key_type = KT_DEFAULT);
+
+  // Generate a a KeyParams for RSA with explicit parameters.
+  static KeyParams RSA(int mod_size = kRsaDefaultModSize,
+                       int pub_exp = kRsaDefaultExponent);
+
+  // Generate a a KeyParams for ECDSA specifying the curve.
+  static KeyParams ECDSA(ECCurve curve = EC_NIST_P256);
+
+  // Check validity of a KeyParams object. Since the factory functions have
+  // no way of returning errors, this function can be called after creation
+  // to make sure the parameters are OK.
+  bool IsValid() const;
+
+  RSAParams rsa_params() const;
+
+  ECCurve ec_curve() const;
+
+  KeyType type() const { return type_; }
+
+ private:
+  KeyType type_;
+  union {
+    RSAParams rsa;
+    ECCurve curve;
+  } params_;
+};
+
+// TODO(hbos): Remove once rtc::KeyType (to be modified) and
+// blink::WebRTCKeyType (to be landed) match. By using this function in Chromium
+// appropriately we can change KeyType enum -> class without breaking Chromium.
+KeyType IntKeyTypeFamilyToKeyType(int key_type_family);
+
+// Parameters for generating a certificate. If |common_name| is non-empty, it
+// will be used for the certificate's subject and issuer name, otherwise a
+// random string will be used.
+struct SSLIdentityParams {
+  std::string common_name;
+  time_t not_before;  // Absolute time since epoch in seconds.
+  time_t not_after;   // Absolute time since epoch in seconds.
+  KeyParams key_params;
+};
+
+// Our identity in an SSL negotiation: a keypair and certificate (both
+// with the same public key).
+// This too is pretty much immutable once created.
+class SSLIdentity {
+ public:
+  // Generates an identity (keypair and self-signed certificate). If
+  // |common_name| is non-empty, it will be used for the certificate's subject
+  // and issuer name, otherwise a random string will be used. The key type and
+  // parameters are defined in |key_param|. The certificate's lifetime in
+  // seconds from the current time is defined in |certificate_lifetime|; it
+  // should be a non-negative number.
+  // Returns null on failure.
+  // Caller is responsible for freeing the returned object.
+  static SSLIdentity* GenerateWithExpiration(const std::string& common_name,
+                                             const KeyParams& key_param,
+                                             time_t certificate_lifetime);
+  static SSLIdentity* Generate(const std::string& common_name,
+                               const KeyParams& key_param);
+  static SSLIdentity* Generate(const std::string& common_name,
+                               KeyType key_type);
+
+  // Generates an identity with the specified validity period.
+  // TODO(torbjorng): Now that Generate() accepts relevant params, make tests
+  // use that instead of this function.
+  static SSLIdentity* GenerateForTest(const SSLIdentityParams& params);
+
+  // Construct an identity from a private key and a certificate.
+  static SSLIdentity* FromPEMStrings(const std::string& private_key,
+                                     const std::string& certificate);
+
+  virtual ~SSLIdentity() {}
+
+  // Returns a new SSLIdentity object instance wrapping the same
+  // identity information.
+  // Caller is responsible for freeing the returned object.
+  // TODO(hbos,torbjorng): Rename to a less confusing name.
+  virtual SSLIdentity* GetReference() const = 0;
+
+  // Returns a temporary reference to the certificate.
+  virtual const SSLCertificate& certificate() const = 0;
+  virtual std::string PrivateKeyToPEMString() const = 0;
+  virtual std::string PublicKeyToPEMString() const = 0;
+
+  // Helpers for parsing converting between PEM and DER format.
+  static bool PemToDer(const std::string& pem_type,
+                       const std::string& pem_string,
+                       std::string* der);
+  static std::string DerToPem(const std::string& pem_type,
+                              const unsigned char* data,
+                              size_t length);
+};
+
+bool operator==(const SSLIdentity& a, const SSLIdentity& b);
+bool operator!=(const SSLIdentity& a, const SSLIdentity& b);
+
+// Convert from ASN1 time as restricted by RFC 5280 to seconds from 1970-01-01
+// 00.00 ("epoch").  If the ASN1 time cannot be read, return -1.  The data at
+// |s| is not 0-terminated; its char count is defined by |length|.
+int64_t ASN1TimeToSec(const unsigned char* s, size_t length, bool long_format);
+
+extern const char kPemTypeCertificate[];
+extern const char kPemTypeRsaPrivateKey[];
+extern const char kPemTypeEcPrivateKey[];
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SSLIDENTITY_H_
diff --git a/base/sslidentity_unittest.cc b/base/sslidentity_unittest.cc
new file mode 100644
index 0000000..5c881c2
--- /dev/null
+++ b/base/sslidentity_unittest.cc
@@ -0,0 +1,613 @@
+/*
+ *  Copyright 2011 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <string>
+
+#include "webrtc/base/fakesslidentity.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/ssladapter.h"
+#include "webrtc/base/sslfingerprint.h"
+#include "webrtc/base/sslidentity.h"
+#include "webrtc/base/stringutils.h"
+
+using rtc::SSLIdentity;
+
+const char kTestCertificate[] = "-----BEGIN CERTIFICATE-----\n"
+    "MIIB6TCCAVICAQYwDQYJKoZIhvcNAQEEBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV\n"
+    "BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMRswGQYD\n"
+    "VQQDExJUZXN0IENBICgxMDI0IGJpdCkwHhcNMDAxMDE2MjIzMTAzWhcNMDMwMTE0\n"
+    "MjIzMTAzWjBjMQswCQYDVQQGEwJBVTETMBEGA1UECBMKUXVlZW5zbGFuZDEaMBgG\n"
+    "A1UEChMRQ3J5cHRTb2Z0IFB0eSBMdGQxIzAhBgNVBAMTGlNlcnZlciB0ZXN0IGNl\n"
+    "cnQgKDUxMiBiaXQpMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ+zw4Qnlf8SMVIP\n"
+    "Fe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVDTGiXav6ooKXfX3j/7tdkuD8Ey2//\n"
+    "Kv7+ue0CAwEAATANBgkqhkiG9w0BAQQFAAOBgQCT0grFQeZaqYb5EYfk20XixZV4\n"
+    "GmyAbXMftG1Eo7qGiMhYzRwGNWxEYojf5PZkYZXvSqZ/ZXHXa4g59jK/rJNnaVGM\n"
+    "k+xIX8mxQvlV0n5O9PIha5BX5teZnkHKgL8aKKLKW1BK7YTngsfSzzaeame5iKfz\n"
+    "itAE+OjGF+PFKbwX8Q==\n"
+    "-----END CERTIFICATE-----\n";
+
+const unsigned char kTestCertSha1[] = {
+    0xA6, 0xC8, 0x59, 0xEA, 0xC3, 0x7E, 0x6D, 0x33,
+    0xCF, 0xE2, 0x69, 0x9D, 0x74, 0xE6, 0xF6, 0x8A,
+    0x9E, 0x47, 0xA7, 0xCA};
+const unsigned char kTestCertSha224[] = {
+    0xd4, 0xce, 0xc6, 0xcf, 0x28, 0xcb, 0xe9, 0x77,
+    0x38, 0x36, 0xcf, 0xb1, 0x3b, 0x4a, 0xd7, 0xbd,
+    0xae, 0x24, 0x21, 0x08, 0xcf, 0x6a, 0x44, 0x0d,
+    0x3f, 0x94, 0x2a, 0x5b};
+const unsigned char kTestCertSha256[] = {
+    0x41, 0x6b, 0xb4, 0x93, 0x47, 0x79, 0x77, 0x24,
+    0x77, 0x0b, 0x8b, 0x2e, 0xa6, 0x2b, 0xe0, 0xf9,
+    0x0a, 0xed, 0x1f, 0x31, 0xa6, 0xf7, 0x5c, 0xa1,
+    0x5a, 0xc4, 0xb0, 0xa2, 0xa4, 0x78, 0xb9, 0x76};
+const unsigned char kTestCertSha384[] = {
+    0x42, 0x31, 0x9a, 0x79, 0x1d, 0xd6, 0x08, 0xbf,
+    0x3b, 0xba, 0x36, 0xd8, 0x37, 0x4a, 0x9a, 0x75,
+    0xd3, 0x25, 0x6e, 0x28, 0x92, 0xbe, 0x06, 0xb7,
+    0xc5, 0xa0, 0x83, 0xe3, 0x86, 0xb1, 0x03, 0xfc,
+    0x64, 0x47, 0xd6, 0xd8, 0xaa, 0xd9, 0x36, 0x60,
+    0x04, 0xcc, 0xbe, 0x7d, 0x6a, 0xe8, 0x34, 0x49};
+const unsigned char kTestCertSha512[] = {
+    0x51, 0x1d, 0xec, 0x02, 0x3d, 0x51, 0x45, 0xd3,
+    0xd8, 0x1d, 0xa4, 0x9d, 0x43, 0xc9, 0xee, 0x32,
+    0x6f, 0x4f, 0x37, 0xee, 0xab, 0x3f, 0x25, 0xdf,
+    0x72, 0xfc, 0x61, 0x1a, 0xd5, 0x92, 0xff, 0x6b,
+    0x28, 0x71, 0x58, 0xb3, 0xe1, 0x8a, 0x18, 0xcf,
+    0x61, 0x33, 0x0e, 0x14, 0xc3, 0x04, 0xaa, 0x07,
+    0xf6, 0xa5, 0xda, 0xdc, 0x42, 0x42, 0x22, 0x35,
+    0xce, 0x26, 0x58, 0x4a, 0x33, 0x6d, 0xbc, 0xb6};
+
+// These PEM strings were created by generating an identity with
+// |SSLIdentity::Generate| and invoking |identity->PrivateKeyToPEMString()|,
+// |identity->PublicKeyToPEMString()| and
+// |identity->certificate().ToPEMString()|. If the crypto library is updated,
+// and the update changes the string form of the keys, these will have to be
+// updated too.  The fingerprint, fingerprint algorithm and base64 certificate
+// were created by calling |identity->certificate().GetStats()|.
+static const char kRSA_PRIVATE_KEY_PEM[] =
+    "-----BEGIN PRIVATE KEY-----\n"
+    "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMQPqDStRlYeDpkX\n"
+    "erRmv+a1naM8vSVSY0gG2plnrnofViWRW3MRqWC+020MsIj3hPZeSAnt/y/FL/nr\n"
+    "4Ea7NXcwdRo1/1xEK7U/f/cjSg1aunyvHCHwcFcMr31HLFvHr0ZgcFwbgIuFLNEl\n"
+    "7kK5HMO9APz1ntUjek8BmBj8yMl9AgMBAAECgYA8FWBC5GcNtSBcIinkZyigF0A7\n"
+    "6j081sa+J/uNz4xUuI257ZXM6biygUhhvuXK06/XoIULJfhyN0fAm1yb0HtNhiUs\n"
+    "kMOYeon6b8FqFaPjrQf7Gr9FMiIHXNK19uegTMKztXyPZoUWlX84X0iawY95x0Y3\n"
+    "73f6P2rN2UOjlVVjAQJBAOKy3l2w3Zj2w0oAJox0eMwl+RxBNt1C42SHrob2mFUT\n"
+    "rytpVVYOasr8CoDI0kjacjI94sLum+buJoXXX6YTGO0CQQDdZwlYIEkoS3ftfxPa\n"
+    "Ai0YTBzAWvHJg0r8Gk/TkHo6IM+LSsZ9ZYUv/vBe4BKLw1I4hZ+bQvBiq+f8ROtk\n"
+    "+TDRAkAPL3ghwoU1h+IRBO2QHwUwd6K2N9AbBi4BP+168O3HVSg4ujeTKigRLMzv\n"
+    "T4R2iNt5bhfQgvdCgtVlxcWMdF8JAkBwDCg3eEdt5BuyjwBt8XH+/O4ED0KUWCTH\n"
+    "x00k5dZlupsuhE5Fwe4QpzXg3gekwdnHjyCCQ/NCDHvgOMTkmhQxAkA9V03KRX9b\n"
+    "bhvEzY/fu8gEp+EzsER96/D79az5z1BaMGL5OPM2xHBPJATKlswnAa7Lp3QKGZGk\n"
+    "TxslfL18J71s\n"
+    "-----END PRIVATE KEY-----\n";
+static const char kRSA_PUBLIC_KEY_PEM[] =
+    "-----BEGIN PUBLIC KEY-----\n"
+    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDED6g0rUZWHg6ZF3q0Zr/mtZ2j\n"
+    "PL0lUmNIBtqZZ656H1YlkVtzEalgvtNtDLCI94T2XkgJ7f8vxS/56+BGuzV3MHUa\n"
+    "Nf9cRCu1P3/3I0oNWrp8rxwh8HBXDK99Ryxbx69GYHBcG4CLhSzRJe5CuRzDvQD8\n"
+    "9Z7VI3pPAZgY/MjJfQIDAQAB\n"
+    "-----END PUBLIC KEY-----\n";
+static const char kRSA_CERT_PEM[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIBnDCCAQWgAwIBAgIJAOEHLgeWYwrpMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV\n"
+    "BAMMBXRlc3QxMB4XDTE2MDQyNDE4MTAyMloXDTE2MDUyNTE4MTAyMlowEDEOMAwG\n"
+    "A1UEAwwFdGVzdDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMQPqDStRlYe\n"
+    "DpkXerRmv+a1naM8vSVSY0gG2plnrnofViWRW3MRqWC+020MsIj3hPZeSAnt/y/F\n"
+    "L/nr4Ea7NXcwdRo1/1xEK7U/f/cjSg1aunyvHCHwcFcMr31HLFvHr0ZgcFwbgIuF\n"
+    "LNEl7kK5HMO9APz1ntUjek8BmBj8yMl9AgMBAAEwDQYJKoZIhvcNAQELBQADgYEA\n"
+    "C3ehaZFl+oEYN069C2ht/gMzuC77L854RF/x7xRtNZzkcg9TVgXXdM3auUvJi8dx\n"
+    "yTpU3ixErjQvoZew5ngXTEvTY8BSQUijJEaLWh8n6NDKRbEGTdAk8nPAmq9hdCFq\n"
+    "e3UkexqNHm3g/VxG4NUC1Y+w29ai0/Rgh+VvgbDwK+Q=\n"
+    "-----END CERTIFICATE-----\n";
+static const char kRSA_FINGERPRINT[] =
+    "3C:E8:B2:70:09:CF:A9:09:5A:F4:EF:8F:8D:8A:32:FF:EA:04:91:BA:6E:D4:17:78:16"
+    ":2A:EE:F9:9A:DD:E2:2B";
+static const char kRSA_FINGERPRINT_ALGORITHM[] =
+    "sha-256";
+static const char kRSA_BASE64_CERTIFICATE[] =
+    "MIIBnDCCAQWgAwIBAgIJAOEHLgeWYwrpMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNVBAMMBXRlc3"
+    "QxMB4XDTE2MDQyNDE4MTAyMloXDTE2MDUyNTE4MTAyMlowEDEOMAwGA1UEAwwFdGVzdDEwgZ8w"
+    "DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMQPqDStRlYeDpkXerRmv+a1naM8vSVSY0gG2plnrn"
+    "ofViWRW3MRqWC+020MsIj3hPZeSAnt/y/FL/nr4Ea7NXcwdRo1/1xEK7U/f/cjSg1aunyvHCHw"
+    "cFcMr31HLFvHr0ZgcFwbgIuFLNEl7kK5HMO9APz1ntUjek8BmBj8yMl9AgMBAAEwDQYJKoZIhv"
+    "cNAQELBQADgYEAC3ehaZFl+oEYN069C2ht/gMzuC77L854RF/x7xRtNZzkcg9TVgXXdM3auUvJ"
+    "i8dxyTpU3ixErjQvoZew5ngXTEvTY8BSQUijJEaLWh8n6NDKRbEGTdAk8nPAmq9hdCFqe3Ukex"
+    "qNHm3g/VxG4NUC1Y+w29ai0/Rgh+VvgbDwK+Q=";
+
+static const char kECDSA_PRIVATE_KEY_PEM[] =
+    "-----BEGIN PRIVATE KEY-----\n"
+    "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/AkEA2hklq7dQ2rN\n"
+    "ZxYL6hOUACL4pn7P4FYlA3ZQhIChRANCAAR7YgdO3utP/8IqVRq8G4VZKreMAxeN\n"
+    "rUa12twthv4uFjuHAHa9D9oyAjncmn+xvZZRyVmKrA56jRzENcEEHoAg\n"
+    "-----END PRIVATE KEY-----\n";
+static const char kECDSA_PUBLIC_KEY_PEM[] =
+    "-----BEGIN PUBLIC KEY-----\n"
+    "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEe2IHTt7rT//CKlUavBuFWSq3jAMX\n"
+    "ja1GtdrcLYb+LhY7hwB2vQ/aMgI53Jp/sb2WUclZiqwOeo0cxDXBBB6AIA==\n"
+    "-----END PUBLIC KEY-----\n";
+static const char kECDSA_CERT_PEM[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIBFDCBu6ADAgECAgkArpkxjw62sW4wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwF\n"
+    "dGVzdDMwHhcNMTYwNDI0MTgxNDM4WhcNMTYwNTI1MTgxNDM4WjAQMQ4wDAYDVQQD\n"
+    "DAV0ZXN0MzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHtiB07e60//wipVGrwb\n"
+    "hVkqt4wDF42tRrXa3C2G/i4WO4cAdr0P2jICOdyaf7G9llHJWYqsDnqNHMQ1wQQe\n"
+    "gCAwCgYIKoZIzj0EAwIDSAAwRQIhANyreQ/K5yuPPpirsd0e/4WGLHou6bIOSQks\n"
+    "DYzo56NmAiAKOr3u8ol3LmygbUCwEvtWrS8QcJDygxHPACo99hkekw==\n"
+    "-----END CERTIFICATE-----\n";
+static const char kECDSA_FINGERPRINT[] =
+    "9F:47:FA:88:76:3D:18:B8:00:A0:59:9D:C3:5D:34:0B:1F:B8:99:9E:68:DA:F3:A5:DA"
+    ":50:33:A9:FF:4D:31:89";
+static const char kECDSA_FINGERPRINT_ALGORITHM[] =
+    "sha-256";
+static const char kECDSA_BASE64_CERTIFICATE[] =
+    "MIIBFDCBu6ADAgECAgkArpkxjw62sW4wCgYIKoZIzj0EAwIwEDEOMAwGA1UEAwwFdGVzdDMwHh"
+    "cNMTYwNDI0MTgxNDM4WhcNMTYwNTI1MTgxNDM4WjAQMQ4wDAYDVQQDDAV0ZXN0MzBZMBMGByqG"
+    "SM49AgEGCCqGSM49AwEHA0IABHtiB07e60//wipVGrwbhVkqt4wDF42tRrXa3C2G/i4WO4cAdr"
+    "0P2jICOdyaf7G9llHJWYqsDnqNHMQ1wQQegCAwCgYIKoZIzj0EAwIDSAAwRQIhANyreQ/K5yuP"
+    "Ppirsd0e/4WGLHou6bIOSQksDYzo56NmAiAKOr3u8ol3LmygbUCwEvtWrS8QcJDygxHPACo99h"
+    "kekw==";
+
+struct IdentityAndInfo {
+  std::unique_ptr<rtc::SSLIdentity> identity;
+  std::vector<std::string> ders;
+  std::vector<std::string> pems;
+  std::vector<std::string> fingerprints;
+};
+
+IdentityAndInfo CreateFakeIdentityAndInfoFromDers(
+    const std::vector<std::string>& ders) {
+  RTC_CHECK(!ders.empty());
+  IdentityAndInfo info;
+  info.ders = ders;
+  for (const std::string& der : ders) {
+    info.pems.push_back(rtc::SSLIdentity::DerToPem(
+        "CERTIFICATE",
+        reinterpret_cast<const unsigned char*>(der.c_str()),
+        der.length()));
+  }
+  info.identity.reset(
+      new rtc::FakeSSLIdentity(rtc::FakeSSLCertificate(info.pems)));
+  // Strip header/footer and newline characters of PEM strings.
+  for (size_t i = 0; i < info.pems.size(); ++i) {
+    rtc::replace_substrs("-----BEGIN CERTIFICATE-----", 27,
+                         "", 0, &info.pems[i]);
+    rtc::replace_substrs("-----END CERTIFICATE-----", 25,
+                         "", 0, &info.pems[i]);
+    rtc::replace_substrs("\n", 1,
+                         "", 0, &info.pems[i]);
+  }
+  // Fingerprint of leaf certificate.
+  std::unique_ptr<rtc::SSLFingerprint> fp(
+      rtc::SSLFingerprint::Create("sha-1", &info.identity->certificate()));
+  EXPECT_TRUE(fp);
+  info.fingerprints.push_back(fp->GetRfc4572Fingerprint());
+  // Fingerprints of the rest of the chain.
+  std::unique_ptr<rtc::SSLCertChain> chain =
+      info.identity->certificate().GetChain();
+  if (chain) {
+    for (size_t i = 0; i < chain->GetSize(); i++) {
+      fp.reset(rtc::SSLFingerprint::Create("sha-1", &chain->Get(i)));
+      EXPECT_TRUE(fp);
+      info.fingerprints.push_back(fp->GetRfc4572Fingerprint());
+    }
+  }
+  EXPECT_EQ(info.ders.size(), info.fingerprints.size());
+  return info;
+}
+
+class SSLIdentityTest : public testing::Test {
+ public:
+  SSLIdentityTest() {}
+
+  ~SSLIdentityTest() {
+  }
+
+  virtual void SetUp() {
+    identity_rsa1_.reset(SSLIdentity::Generate("test1", rtc::KT_RSA));
+    identity_rsa2_.reset(SSLIdentity::Generate("test2", rtc::KT_RSA));
+    identity_ecdsa1_.reset(SSLIdentity::Generate("test3", rtc::KT_ECDSA));
+    identity_ecdsa2_.reset(SSLIdentity::Generate("test4", rtc::KT_ECDSA));
+
+    ASSERT_TRUE(identity_rsa1_);
+    ASSERT_TRUE(identity_rsa2_);
+    ASSERT_TRUE(identity_ecdsa1_);
+    ASSERT_TRUE(identity_ecdsa2_);
+
+    test_cert_.reset(rtc::SSLCertificate::FromPEMString(kTestCertificate));
+    ASSERT_TRUE(test_cert_);
+  }
+
+  void TestGetSignatureDigestAlgorithm() {
+    std::string digest_algorithm;
+
+    ASSERT_TRUE(identity_rsa1_->certificate().GetSignatureDigestAlgorithm(
+        &digest_algorithm));
+    ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm);
+
+    ASSERT_TRUE(identity_rsa2_->certificate().GetSignatureDigestAlgorithm(
+        &digest_algorithm));
+    ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm);
+
+    ASSERT_TRUE(identity_ecdsa1_->certificate().GetSignatureDigestAlgorithm(
+        &digest_algorithm));
+    ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm);
+
+    ASSERT_TRUE(identity_ecdsa2_->certificate().GetSignatureDigestAlgorithm(
+        &digest_algorithm));
+    ASSERT_EQ(rtc::DIGEST_SHA_256, digest_algorithm);
+
+    // The test certificate has an MD5-based signature.
+    ASSERT_TRUE(test_cert_->GetSignatureDigestAlgorithm(&digest_algorithm));
+    ASSERT_EQ(rtc::DIGEST_MD5, digest_algorithm);
+  }
+
+  typedef unsigned char DigestType[rtc::MessageDigest::kMaxSize];
+
+  void TestDigestHelper(DigestType digest,
+                        const SSLIdentity* identity,
+                        const std::string& algorithm,
+                        size_t expected_len) {
+    DigestType digest1;
+    size_t digest_len;
+    bool rv;
+
+    memset(digest, 0, expected_len);
+    rv = identity->certificate().ComputeDigest(algorithm, digest,
+                                               sizeof(DigestType), &digest_len);
+    EXPECT_TRUE(rv);
+    EXPECT_EQ(expected_len, digest_len);
+
+    // Repeat digest computation for the identity as a sanity check.
+    memset(digest1, 0xff, expected_len);
+    rv = identity->certificate().ComputeDigest(algorithm, digest1,
+                                               sizeof(DigestType), &digest_len);
+    EXPECT_TRUE(rv);
+    EXPECT_EQ(expected_len, digest_len);
+
+    EXPECT_EQ(0, memcmp(digest, digest1, expected_len));
+  }
+
+  void TestDigestForGeneratedCert(const std::string& algorithm,
+                                  size_t expected_len) {
+    DigestType digest[4];
+
+    ASSERT_TRUE(expected_len <= sizeof(DigestType));
+
+    TestDigestHelper(digest[0], identity_rsa1_.get(), algorithm, expected_len);
+    TestDigestHelper(digest[1], identity_rsa2_.get(), algorithm, expected_len);
+    TestDigestHelper(digest[2], identity_ecdsa1_.get(), algorithm,
+                     expected_len);
+    TestDigestHelper(digest[3], identity_ecdsa2_.get(), algorithm,
+                     expected_len);
+
+    // Sanity check that all four digests are unique.  This could theoretically
+    // fail, since cryptographic hash collisions have a non-zero probability.
+    for (int i = 0; i < 4; i++) {
+      for (int j = 0; j < 4; j++) {
+        if (i != j)
+          EXPECT_NE(0, memcmp(digest[i], digest[j], expected_len));
+      }
+    }
+  }
+
+  void TestDigestForFixedCert(const std::string& algorithm,
+                              size_t expected_len,
+                              const unsigned char* expected_digest) {
+    bool rv;
+    DigestType digest;
+    size_t digest_len;
+
+    ASSERT_TRUE(expected_len <= sizeof(DigestType));
+
+    rv = test_cert_->ComputeDigest(algorithm, digest, sizeof(digest),
+                                   &digest_len);
+    EXPECT_TRUE(rv);
+    EXPECT_EQ(expected_len, digest_len);
+    EXPECT_EQ(0, memcmp(digest, expected_digest, expected_len));
+  }
+
+  void TestCloningIdentity(const SSLIdentity& identity) {
+    // Convert |identity| to PEM strings and create a new identity by converting
+    // back from the string format.
+    std::string priv_pem = identity.PrivateKeyToPEMString();
+    std::string publ_pem = identity.PublicKeyToPEMString();
+    std::string cert_pem = identity.certificate().ToPEMString();
+    std::unique_ptr<SSLIdentity> clone(
+        SSLIdentity::FromPEMStrings(priv_pem, cert_pem));
+    EXPECT_TRUE(clone);
+
+    // Make sure the clone is identical to the original.
+    EXPECT_TRUE(identity == *clone);
+    ASSERT_EQ(identity.certificate().CertificateExpirationTime(),
+              clone->certificate().CertificateExpirationTime());
+
+    // At this point we are confident that the identities are identical. To be
+    // extra sure, we compare PEM strings of the clone with the original. Note
+    // that the PEM strings of two identities are not strictly guaranteed to be
+    // equal (they describe structs whose members could be listed in a different
+    // order, for example). But because the same function is used to produce
+    // both PEMs, its a good enough bet that this comparison will work. If the
+    // assumption stops holding in the future we can always remove this from the
+    // unittest.
+    std::string clone_priv_pem = clone->PrivateKeyToPEMString();
+    std::string clone_publ_pem = clone->PublicKeyToPEMString();
+    std::string clone_cert_pem = clone->certificate().ToPEMString();
+    ASSERT_EQ(priv_pem, clone_priv_pem);
+    ASSERT_EQ(publ_pem, clone_publ_pem);
+    ASSERT_EQ(cert_pem, clone_cert_pem);
+  }
+
+ protected:
+  std::unique_ptr<SSLIdentity> identity_rsa1_;
+  std::unique_ptr<SSLIdentity> identity_rsa2_;
+  std::unique_ptr<SSLIdentity> identity_ecdsa1_;
+  std::unique_ptr<SSLIdentity> identity_ecdsa2_;
+  std::unique_ptr<rtc::SSLCertificate> test_cert_;
+};
+
+TEST_F(SSLIdentityTest, FixedDigestSHA1) {
+  TestDigestForFixedCert(rtc::DIGEST_SHA_1, 20, kTestCertSha1);
+}
+
+// HASH_AlgSHA224 is not supported in the chromium linux build.
+TEST_F(SSLIdentityTest, FixedDigestSHA224) {
+  TestDigestForFixedCert(rtc::DIGEST_SHA_224, 28, kTestCertSha224);
+}
+
+TEST_F(SSLIdentityTest, FixedDigestSHA256) {
+  TestDigestForFixedCert(rtc::DIGEST_SHA_256, 32, kTestCertSha256);
+}
+
+TEST_F(SSLIdentityTest, FixedDigestSHA384) {
+  TestDigestForFixedCert(rtc::DIGEST_SHA_384, 48, kTestCertSha384);
+}
+
+TEST_F(SSLIdentityTest, FixedDigestSHA512) {
+  TestDigestForFixedCert(rtc::DIGEST_SHA_512, 64, kTestCertSha512);
+}
+
+// HASH_AlgSHA224 is not supported in the chromium linux build.
+TEST_F(SSLIdentityTest, DigestSHA224) {
+  TestDigestForGeneratedCert(rtc::DIGEST_SHA_224, 28);
+}
+
+TEST_F(SSLIdentityTest, DigestSHA256) {
+  TestDigestForGeneratedCert(rtc::DIGEST_SHA_256, 32);
+}
+
+TEST_F(SSLIdentityTest, DigestSHA384) {
+  TestDigestForGeneratedCert(rtc::DIGEST_SHA_384, 48);
+}
+
+TEST_F(SSLIdentityTest, DigestSHA512) {
+  TestDigestForGeneratedCert(rtc::DIGEST_SHA_512, 64);
+}
+
+TEST_F(SSLIdentityTest, IdentityComparison) {
+  EXPECT_TRUE(*identity_rsa1_ == *identity_rsa1_);
+  EXPECT_FALSE(*identity_rsa1_ == *identity_rsa2_);
+  EXPECT_FALSE(*identity_rsa1_ == *identity_ecdsa1_);
+  EXPECT_FALSE(*identity_rsa1_ == *identity_ecdsa2_);
+
+  EXPECT_TRUE(*identity_rsa2_ == *identity_rsa2_);
+  EXPECT_FALSE(*identity_rsa2_ == *identity_ecdsa1_);
+  EXPECT_FALSE(*identity_rsa2_ == *identity_ecdsa2_);
+
+  EXPECT_TRUE(*identity_ecdsa1_ == *identity_ecdsa1_);
+  EXPECT_FALSE(*identity_ecdsa1_ == *identity_ecdsa2_);
+}
+
+TEST_F(SSLIdentityTest, FromPEMStringsRSA) {
+  std::unique_ptr<SSLIdentity> identity(
+      SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kRSA_CERT_PEM));
+  EXPECT_TRUE(identity);
+  EXPECT_EQ(kRSA_PRIVATE_KEY_PEM, identity->PrivateKeyToPEMString());
+  EXPECT_EQ(kRSA_PUBLIC_KEY_PEM, identity->PublicKeyToPEMString());
+  EXPECT_EQ(kRSA_CERT_PEM, identity->certificate().ToPEMString());
+}
+
+TEST_F(SSLIdentityTest, FromPEMStringsEC) {
+  std::unique_ptr<SSLIdentity> identity(
+      SSLIdentity::FromPEMStrings(kECDSA_PRIVATE_KEY_PEM, kECDSA_CERT_PEM));
+  EXPECT_TRUE(identity);
+  EXPECT_EQ(kECDSA_PRIVATE_KEY_PEM, identity->PrivateKeyToPEMString());
+  EXPECT_EQ(kECDSA_PUBLIC_KEY_PEM, identity->PublicKeyToPEMString());
+  EXPECT_EQ(kECDSA_CERT_PEM, identity->certificate().ToPEMString());
+}
+
+TEST_F(SSLIdentityTest, CloneIdentityRSA) {
+  TestCloningIdentity(*identity_rsa1_);
+  TestCloningIdentity(*identity_rsa2_);
+}
+
+TEST_F(SSLIdentityTest, CloneIdentityECDSA) {
+  TestCloningIdentity(*identity_ecdsa1_);
+  TestCloningIdentity(*identity_ecdsa2_);
+}
+
+TEST_F(SSLIdentityTest, PemDerConversion) {
+  std::string der;
+  EXPECT_TRUE(SSLIdentity::PemToDer("CERTIFICATE", kTestCertificate, &der));
+
+  EXPECT_EQ(kTestCertificate, SSLIdentity::DerToPem(
+      "CERTIFICATE",
+      reinterpret_cast<const unsigned char*>(der.data()), der.length()));
+}
+
+TEST_F(SSLIdentityTest, GetSignatureDigestAlgorithm) {
+  TestGetSignatureDigestAlgorithm();
+}
+
+TEST_F(SSLIdentityTest, SSLCertificateGetStatsRSA) {
+  std::unique_ptr<SSLIdentity> identity(
+      SSLIdentity::FromPEMStrings(kRSA_PRIVATE_KEY_PEM, kRSA_CERT_PEM));
+  std::unique_ptr<rtc::SSLCertificateStats> stats =
+      identity->certificate().GetStats();
+  EXPECT_EQ(stats->fingerprint, kRSA_FINGERPRINT);
+  EXPECT_EQ(stats->fingerprint_algorithm, kRSA_FINGERPRINT_ALGORITHM);
+  EXPECT_EQ(stats->base64_certificate, kRSA_BASE64_CERTIFICATE);
+  EXPECT_FALSE(stats->issuer);
+}
+
+TEST_F(SSLIdentityTest, SSLCertificateGetStatsECDSA) {
+  std::unique_ptr<SSLIdentity> identity(
+      SSLIdentity::FromPEMStrings(kECDSA_PRIVATE_KEY_PEM, kECDSA_CERT_PEM));
+  std::unique_ptr<rtc::SSLCertificateStats> stats =
+      identity->certificate().GetStats();
+  EXPECT_EQ(stats->fingerprint, kECDSA_FINGERPRINT);
+  EXPECT_EQ(stats->fingerprint_algorithm, kECDSA_FINGERPRINT_ALGORITHM);
+  EXPECT_EQ(stats->base64_certificate, kECDSA_BASE64_CERTIFICATE);
+  EXPECT_FALSE(stats->issuer);
+}
+
+TEST_F(SSLIdentityTest, SSLCertificateGetStatsWithChain) {
+  std::vector<std::string> ders;
+  ders.push_back("every der results in");
+  ders.push_back("an identity + certificate");
+  ders.push_back("in a certificate chain");
+  IdentityAndInfo info = CreateFakeIdentityAndInfoFromDers(ders);
+  EXPECT_TRUE(info.identity);
+  EXPECT_EQ(info.ders, ders);
+  EXPECT_EQ(info.pems.size(), info.ders.size());
+  EXPECT_EQ(info.fingerprints.size(), info.ders.size());
+
+  std::unique_ptr<rtc::SSLCertificateStats> first_stats =
+      info.identity->certificate().GetStats();
+  rtc::SSLCertificateStats* cert_stats = first_stats.get();
+  for (size_t i = 0; i < info.ders.size(); ++i) {
+    EXPECT_EQ(cert_stats->fingerprint, info.fingerprints[i]);
+    EXPECT_EQ(cert_stats->fingerprint_algorithm, "sha-1");
+    EXPECT_EQ(cert_stats->base64_certificate, info.pems[i]);
+    cert_stats = cert_stats->issuer.get();
+    EXPECT_EQ(static_cast<bool>(cert_stats), i + 1 < info.ders.size());
+  }
+}
+
+class SSLIdentityExpirationTest : public testing::Test {
+ public:
+  SSLIdentityExpirationTest() {
+    // Set use of the test RNG to get deterministic expiration timestamp.
+    rtc::SetRandomTestMode(true);
+  }
+  ~SSLIdentityExpirationTest() {
+    // Put it back for the next test.
+    rtc::SetRandomTestMode(false);
+  }
+
+  void TestASN1TimeToSec() {
+    struct asn_example {
+      const char* string;
+      bool long_format;
+      int64_t want;
+    } static const data[] = {
+      // Valid examples.
+      {"19700101000000Z",  true,  0},
+      {"700101000000Z",    false, 0},
+      {"19700101000001Z",  true,  1},
+      {"700101000001Z",    false, 1},
+      {"19700101000100Z",  true,  60},
+      {"19700101000101Z",  true,  61},
+      {"19700101010000Z",  true,  3600},
+      {"19700101010001Z",  true,  3601},
+      {"19700101010100Z",  true,  3660},
+      {"19700101010101Z",  true,  3661},
+      {"710911012345Z",    false, 53400225},
+      {"20000101000000Z",  true,  946684800},
+      {"20000101000000Z",  true,  946684800},
+      {"20151130140156Z",  true,  1448892116},
+      {"151130140156Z",    false, 1448892116},
+      {"20491231235959Z",  true,  2524607999},
+      {"491231235959Z",    false, 2524607999},
+      {"20500101000000Z",  true,  2524607999+1},
+      {"20700101000000Z",  true,  3155760000},
+      {"21000101000000Z",  true,  4102444800},
+      {"24000101000000Z",  true,  13569465600},
+
+      // Invalid examples.
+      {"19700101000000",    true,  -1},  // missing Z long format
+      {"19700101000000X",   true,  -1},  // X instead of Z long format
+      {"197001010000000",   true,  -1},  // 0 instead of Z long format
+      {"1970010100000000Z", true,  -1},  // excess digits long format
+      {"700101000000",      false, -1},  // missing Z short format
+      {"700101000000X",     false, -1},  // X instead of Z short format
+      {"7001010000000",     false, -1},  // 0 instead of Z short format
+      {"70010100000000Z",   false, -1},  // excess digits short format
+      {":9700101000000Z",   true,  -1},  // invalid character
+      {"1:700101000001Z",   true,  -1},  // invalid character
+      {"19:00101000100Z",   true,  -1},  // invalid character
+      {"197:0101000101Z",   true,  -1},  // invalid character
+      {"1970:101010000Z",   true,  -1},  // invalid character
+      {"19700:01010001Z",   true,  -1},  // invalid character
+      {"197001:1010100Z",   true,  -1},  // invalid character
+      {"1970010:010101Z",   true,  -1},  // invalid character
+      {"70010100:000Z",     false, -1},  // invalid character
+      {"700101000:01Z",     false, -1},  // invalid character
+      {"2000010100:000Z",   true,  -1},  // invalid character
+      {"21000101000:00Z",   true,  -1},  // invalid character
+      {"240001010000:0Z",   true,  -1},  // invalid character
+      {"500101000000Z",     false, -1},  // but too old for epoch
+      {"691231235959Z",     false, -1},  // too old for epoch
+      {"19611118043000Z",   false, -1},  // way too old for epoch
+    };
+
+    unsigned char buf[20];
+
+    // Run all examples and check for the expected result.
+    for (const auto& entry : data) {
+      size_t length = strlen(entry.string);
+      memcpy(buf, entry.string, length);    // Copy the ASN1 string...
+      buf[length] = rtc::CreateRandomId();  // ...and terminate it with junk.
+      int64_t res = rtc::ASN1TimeToSec(buf, length, entry.long_format);
+      LOG(LS_VERBOSE) << entry.string;
+      ASSERT_EQ(entry.want, res);
+    }
+    // Run all examples again, but with an invalid length.
+    for (const auto& entry : data) {
+      size_t length = strlen(entry.string);
+      memcpy(buf, entry.string, length);    // Copy the ASN1 string...
+      buf[length] = rtc::CreateRandomId();  // ...and terminate it with junk.
+      int64_t res = rtc::ASN1TimeToSec(buf, length - 1, entry.long_format);
+      LOG(LS_VERBOSE) << entry.string;
+      ASSERT_EQ(-1, res);
+    }
+  }
+
+  void TestExpireTime(int times) {
+    // We test just ECDSA here since what we're out to exercise is the
+    // interfaces for expiration setting and reading.
+    for (int i = 0; i < times; i++) {
+      // We limit the time to < 2^31 here, i.e., we stay before 2038, since else
+      // we hit time offset limitations in OpenSSL on some 32-bit systems.
+      time_t time_before_generation = time(nullptr);
+      time_t lifetime =
+          rtc::CreateRandomId() % (0x80000000 - time_before_generation);
+      rtc::KeyParams key_params = rtc::KeyParams::ECDSA(rtc::EC_NIST_P256);
+      SSLIdentity* identity =
+          rtc::SSLIdentity::GenerateWithExpiration("", key_params, lifetime);
+      time_t time_after_generation = time(nullptr);
+      EXPECT_LE(time_before_generation + lifetime,
+                identity->certificate().CertificateExpirationTime());
+      EXPECT_GE(time_after_generation + lifetime,
+                identity->certificate().CertificateExpirationTime());
+      delete identity;
+    }
+  }
+};
+
+TEST_F(SSLIdentityExpirationTest, TestASN1TimeToSec) {
+  TestASN1TimeToSec();
+}
+
+TEST_F(SSLIdentityExpirationTest, TestExpireTime) {
+  TestExpireTime(500);
+}
diff --git a/base/sslroots.h b/base/sslroots.h
index 683f48c..0464ac8 100644
--- a/base/sslroots.h
+++ b/base/sslroots.h
@@ -1,6 +1,3 @@
-#ifndef WEBRTC_BASE_SSLROOTS_H_
-#define WEBRTC_BASE_SSLROOTS_H_
-
 // This file is the root certificates in C form that are needed to connect to
 // Google.
 
@@ -4267,4 +4264,3 @@
   1122,
 };
 
-#endif  // WEBRTC_BASE_SSLROOTS_H_
diff --git a/base/sslstreamadapter.cc b/base/sslstreamadapter.cc
new file mode 100644
index 0000000..0927704
--- /dev/null
+++ b/base/sslstreamadapter.cc
@@ -0,0 +1,165 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/sslstreamadapter.h"
+
+#include "webrtc/base/opensslstreamadapter.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace rtc {
+
+// TODO(guoweis): Move this to SDP layer and use int form internally.
+// webrtc:5043.
+const char CS_AES_CM_128_HMAC_SHA1_80[] = "AES_CM_128_HMAC_SHA1_80";
+const char CS_AES_CM_128_HMAC_SHA1_32[] = "AES_CM_128_HMAC_SHA1_32";
+const char CS_AEAD_AES_128_GCM[] = "AEAD_AES_128_GCM";
+const char CS_AEAD_AES_256_GCM[] = "AEAD_AES_256_GCM";
+
+std::string SrtpCryptoSuiteToName(int crypto_suite) {
+  switch (crypto_suite) {
+  case SRTP_AES128_CM_SHA1_32:
+    return CS_AES_CM_128_HMAC_SHA1_32;
+  case SRTP_AES128_CM_SHA1_80:
+    return CS_AES_CM_128_HMAC_SHA1_80;
+  case SRTP_AEAD_AES_128_GCM:
+    return CS_AEAD_AES_128_GCM;
+  case SRTP_AEAD_AES_256_GCM:
+    return CS_AEAD_AES_256_GCM;
+  default:
+    return std::string();
+  }
+}
+
+int SrtpCryptoSuiteFromName(const std::string& crypto_suite) {
+  if (crypto_suite == CS_AES_CM_128_HMAC_SHA1_32)
+    return SRTP_AES128_CM_SHA1_32;
+  if (crypto_suite == CS_AES_CM_128_HMAC_SHA1_80)
+    return SRTP_AES128_CM_SHA1_80;
+  if (crypto_suite == CS_AEAD_AES_128_GCM)
+    return SRTP_AEAD_AES_128_GCM;
+  if (crypto_suite == CS_AEAD_AES_256_GCM)
+    return SRTP_AEAD_AES_256_GCM;
+  return SRTP_INVALID_CRYPTO_SUITE;
+}
+
+bool GetSrtpKeyAndSaltLengths(int crypto_suite, int *key_length,
+    int *salt_length) {
+  switch (crypto_suite) {
+  case SRTP_AES128_CM_SHA1_32:
+  case SRTP_AES128_CM_SHA1_80:
+    // SRTP_AES128_CM_HMAC_SHA1_32 and SRTP_AES128_CM_HMAC_SHA1_80 are defined
+    // in RFC 5764 to use a 128 bits key and 112 bits salt for the cipher.
+    *key_length = 16;
+    *salt_length = 14;
+    break;
+  case SRTP_AEAD_AES_128_GCM:
+    // SRTP_AEAD_AES_128_GCM is defined in RFC 7714 to use a 128 bits key and
+    // a 96 bits salt for the cipher.
+    *key_length = 16;
+    *salt_length = 12;
+    break;
+  case SRTP_AEAD_AES_256_GCM:
+    // SRTP_AEAD_AES_256_GCM is defined in RFC 7714 to use a 256 bits key and
+    // a 96 bits salt for the cipher.
+    *key_length = 32;
+    *salt_length = 12;
+    break;
+  default:
+    return false;
+  }
+  return true;
+}
+
+bool IsGcmCryptoSuite(int crypto_suite) {
+  return (crypto_suite == SRTP_AEAD_AES_256_GCM ||
+          crypto_suite == SRTP_AEAD_AES_128_GCM);
+}
+
+bool IsGcmCryptoSuiteName(const std::string& crypto_suite) {
+  return (crypto_suite == CS_AEAD_AES_256_GCM ||
+          crypto_suite == CS_AEAD_AES_128_GCM);
+}
+
+// static
+CryptoOptions CryptoOptions::NoGcm() {
+  CryptoOptions options;
+  options.enable_gcm_crypto_suites = false;
+  return options;
+}
+
+std::vector<int> GetSupportedDtlsSrtpCryptoSuites(
+    const rtc::CryptoOptions& crypto_options) {
+  std::vector<int> crypto_suites;
+  if (crypto_options.enable_gcm_crypto_suites) {
+    crypto_suites.push_back(rtc::SRTP_AEAD_AES_256_GCM);
+    crypto_suites.push_back(rtc::SRTP_AEAD_AES_128_GCM);
+  }
+  // Note: SRTP_AES128_CM_SHA1_80 is what is required to be supported (by
+  // draft-ietf-rtcweb-security-arch), but SRTP_AES128_CM_SHA1_32 is allowed as
+  // well, and saves a few bytes per packet if it ends up selected.
+  crypto_suites.push_back(rtc::SRTP_AES128_CM_SHA1_32);
+  crypto_suites.push_back(rtc::SRTP_AES128_CM_SHA1_80);
+  return crypto_suites;
+}
+
+SSLStreamAdapter* SSLStreamAdapter::Create(StreamInterface* stream) {
+  return new OpenSSLStreamAdapter(stream);
+}
+
+SSLStreamAdapter::SSLStreamAdapter(StreamInterface* stream)
+    : StreamAdapterInterface(stream),
+      ignore_bad_cert_(false),
+      client_auth_enabled_(true) {}
+
+SSLStreamAdapter::~SSLStreamAdapter() {}
+
+bool SSLStreamAdapter::GetSslCipherSuite(int* cipher_suite) {
+  return false;
+}
+
+bool SSLStreamAdapter::ExportKeyingMaterial(const std::string& label,
+                                            const uint8_t* context,
+                                            size_t context_len,
+                                            bool use_context,
+                                            uint8_t* result,
+                                            size_t result_len) {
+  return false;  // Default is unsupported
+}
+
+bool SSLStreamAdapter::SetDtlsSrtpCryptoSuites(
+    const std::vector<int>& crypto_suites) {
+  return false;
+}
+
+bool SSLStreamAdapter::GetDtlsSrtpCryptoSuite(int* crypto_suite) {
+  return false;
+}
+
+bool SSLStreamAdapter::IsBoringSsl() {
+  return OpenSSLStreamAdapter::IsBoringSsl();
+}
+bool SSLStreamAdapter::IsAcceptableCipher(int cipher, KeyType key_type) {
+  return OpenSSLStreamAdapter::IsAcceptableCipher(cipher, key_type);
+}
+bool SSLStreamAdapter::IsAcceptableCipher(const std::string& cipher,
+                                          KeyType key_type) {
+  return OpenSSLStreamAdapter::IsAcceptableCipher(cipher, key_type);
+}
+std::string SSLStreamAdapter::SslCipherSuiteToName(int cipher_suite) {
+  return OpenSSLStreamAdapter::SslCipherSuiteToName(cipher_suite);
+}
+void SSLStreamAdapter::enable_time_callback_for_testing() {
+  OpenSSLStreamAdapter::enable_time_callback_for_testing();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
diff --git a/base/sslstreamadapter.h b/base/sslstreamadapter.h
index d7c062e..62a7249 100644
--- a/base/sslstreamadapter.h
+++ b/base/sslstreamadapter.h
@@ -11,9 +11,265 @@
 #ifndef WEBRTC_BASE_SSLSTREAMADAPTER_H_
 #define WEBRTC_BASE_SSLSTREAMADAPTER_H_
 
+#include <memory>
+#include <string>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/sslstreamadapter.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/sslidentity.h"
+
+namespace rtc {
+
+// Constants for SSL profile.
+const int TLS_NULL_WITH_NULL_NULL = 0;
+
+// Constants for SRTP profiles.
+const int SRTP_INVALID_CRYPTO_SUITE = 0;
+#ifndef SRTP_AES128_CM_SHA1_80
+const int SRTP_AES128_CM_SHA1_80 = 0x0001;
+#endif
+#ifndef SRTP_AES128_CM_SHA1_32
+const int SRTP_AES128_CM_SHA1_32 = 0x0002;
+#endif
+#ifndef SRTP_AEAD_AES_128_GCM
+const int SRTP_AEAD_AES_128_GCM = 0x0007;
+#endif
+#ifndef SRTP_AEAD_AES_256_GCM
+const int SRTP_AEAD_AES_256_GCM = 0x0008;
+#endif
+
+// Names of SRTP profiles listed above.
+// 128-bit AES with 80-bit SHA-1 HMAC.
+extern const char CS_AES_CM_128_HMAC_SHA1_80[];
+// 128-bit AES with 32-bit SHA-1 HMAC.
+extern const char CS_AES_CM_128_HMAC_SHA1_32[];
+// 128-bit AES GCM with 16 byte AEAD auth tag.
+extern const char CS_AEAD_AES_128_GCM[];
+// 256-bit AES GCM with 16 byte AEAD auth tag.
+extern const char CS_AEAD_AES_256_GCM[];
+
+// Given the DTLS-SRTP protection profile ID, as defined in
+// https://tools.ietf.org/html/rfc4568#section-6.2 , return the SRTP profile
+// name, as defined in https://tools.ietf.org/html/rfc5764#section-4.1.2.
+std::string SrtpCryptoSuiteToName(int crypto_suite);
+
+// The reverse of above conversion.
+int SrtpCryptoSuiteFromName(const std::string& crypto_suite);
+
+// Get key length and salt length for given crypto suite. Returns true for
+// valid suites, otherwise false.
+bool GetSrtpKeyAndSaltLengths(int crypto_suite, int *key_length,
+    int *salt_length);
+
+// Returns true if the given crypto suite id uses a GCM cipher.
+bool IsGcmCryptoSuite(int crypto_suite);
+
+// Returns true if the given crypto suite name uses a GCM cipher.
+bool IsGcmCryptoSuiteName(const std::string& crypto_suite);
+
+struct CryptoOptions {
+  CryptoOptions() {}
+
+  // Helper method to return an instance of the CryptoOptions with GCM crypto
+  // suites disabled. This method should be used instead of depending on current
+  // default values set by the constructor.
+  static CryptoOptions NoGcm();
+
+  // Enable GCM crypto suites from RFC 7714 for SRTP. GCM will only be used
+  // if both sides enable it.
+  bool enable_gcm_crypto_suites = false;
+};
+
+// Returns supported crypto suites, given |crypto_options|.
+// CS_AES_CM_128_HMAC_SHA1_32 will be preferred by default.
+std::vector<int> GetSupportedDtlsSrtpCryptoSuites(
+    const rtc::CryptoOptions& crypto_options);
+
+// SSLStreamAdapter : A StreamInterfaceAdapter that does SSL/TLS.
+// After SSL has been started, the stream will only open on successful
+// SSL verification of certificates, and the communication is
+// encrypted of course.
+//
+// This class was written with SSLAdapter as a starting point. It
+// offers a similar interface, with two differences: there is no
+// support for a restartable SSL connection, and this class has a
+// peer-to-peer mode.
+//
+// The SSL library requires initialization and cleanup. Static method
+// for doing this are in SSLAdapter. They should possibly be moved out
+// to a neutral class.
+
+
+enum SSLRole { SSL_CLIENT, SSL_SERVER };
+enum SSLMode { SSL_MODE_TLS, SSL_MODE_DTLS };
+enum SSLProtocolVersion {
+  SSL_PROTOCOL_TLS_10,
+  SSL_PROTOCOL_TLS_11,
+  SSL_PROTOCOL_TLS_12,
+  SSL_PROTOCOL_DTLS_10 = SSL_PROTOCOL_TLS_11,
+  SSL_PROTOCOL_DTLS_12 = SSL_PROTOCOL_TLS_12,
+};
+enum class SSLPeerCertificateDigestError {
+  NONE,
+  UNKNOWN_ALGORITHM,
+  INVALID_LENGTH,
+  VERIFICATION_FAILED,
+};
+
+// Errors for Read -- in the high range so no conflict with OpenSSL.
+enum { SSE_MSG_TRUNC = 0xff0001 };
+
+// Used to send back UMA histogram value. Logged when Dtls handshake fails.
+enum class SSLHandshakeError { UNKNOWN, INCOMPATIBLE_CIPHERSUITE, MAX_VALUE };
+
+class SSLStreamAdapter : public StreamAdapterInterface {
+ public:
+  // Instantiate an SSLStreamAdapter wrapping the given stream,
+  // (using the selected implementation for the platform).
+  // Caller is responsible for freeing the returned object.
+  static SSLStreamAdapter* Create(StreamInterface* stream);
+
+  explicit SSLStreamAdapter(StreamInterface* stream);
+  ~SSLStreamAdapter() override;
+
+  void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; }
+  bool ignore_bad_cert() const { return ignore_bad_cert_; }
+
+  void set_client_auth_enabled(bool enabled) { client_auth_enabled_ = enabled; }
+  bool client_auth_enabled() const { return client_auth_enabled_; }
+
+  // Specify our SSL identity: key and certificate. SSLStream takes ownership
+  // of the SSLIdentity object and will free it when appropriate. Should be
+  // called no more than once on a given SSLStream instance.
+  virtual void SetIdentity(SSLIdentity* identity) = 0;
+
+  // Call this to indicate that we are to play the server role (or client role,
+  // if the default argument is replaced by SSL_CLIENT).
+  // The default argument is for backward compatibility.
+  // TODO(ekr@rtfm.com): rename this SetRole to reflect its new function
+  virtual void SetServerRole(SSLRole role = SSL_SERVER) = 0;
+
+  // Do DTLS or TLS.
+  virtual void SetMode(SSLMode mode) = 0;
+
+  // Set maximum supported protocol version. The highest version supported by
+  // both ends will be used for the connection, i.e. if one party supports
+  // DTLS 1.0 and the other DTLS 1.2, DTLS 1.0 will be used.
+  // If requested version is not supported by underlying crypto library, the
+  // next lower will be used.
+  virtual void SetMaxProtocolVersion(SSLProtocolVersion version) = 0;
+
+  // Set the initial retransmission timeout for DTLS messages. When the timeout
+  // expires, the message gets retransmitted and the timeout is exponentially
+  // increased.
+  // This should only be called before StartSSL().
+  virtual void SetInitialRetransmissionTimeout(int timeout_ms) = 0;
+
+  // StartSSL starts negotiation with a peer, whose certificate is verified
+  // using the certificate digest. Generally, SetIdentity() and possibly
+  // SetServerRole() should have been called before this.
+  // SetPeerCertificateDigest() must also be called. It may be called after
+  // StartSSLWithPeer() but must be called before the underlying stream opens.
+  //
+  // Use of the stream prior to calling StartSSL will pass data in clear text.
+  // Calling StartSSL causes SSL negotiation to begin as soon as possible: right
+  // away if the underlying wrapped stream is already opened, or else as soon as
+  // it opens.
+  //
+  // StartSSL returns a negative error code on failure. Returning 0 means
+  // success so far, but negotiation is probably not complete and will continue
+  // asynchronously. In that case, the exposed stream will open after
+  // successful negotiation and verification, or an SE_CLOSE event will be
+  // raised if negotiation fails.
+  virtual int StartSSL() = 0;
+
+  // Specify the digest of the certificate that our peer is expected to use.
+  // Only this certificate will be accepted during SSL verification. The
+  // certificate is assumed to have been obtained through some other secure
+  // channel (such as the signaling channel). This must specify the terminal
+  // certificate, not just a CA. SSLStream makes a copy of the digest value.
+  //
+  // Returns true if successful.
+  // |error| is optional and provides more information about the failure.
+  virtual bool SetPeerCertificateDigest(
+      const std::string& digest_alg,
+      const unsigned char* digest_val,
+      size_t digest_len,
+      SSLPeerCertificateDigestError* error = nullptr) = 0;
+
+  // Retrieves the peer's X.509 certificate, if a connection has been
+  // established. It returns the transmitted over SSL, including the entire
+  // chain.
+  virtual std::unique_ptr<SSLCertificate> GetPeerCertificate() const = 0;
+
+  // Retrieves the IANA registration id of the cipher suite used for the
+  // connection (e.g. 0x2F for "TLS_RSA_WITH_AES_128_CBC_SHA").
+  virtual bool GetSslCipherSuite(int* cipher_suite);
+
+  virtual int GetSslVersion() const = 0;
+
+  // Key Exporter interface from RFC 5705
+  // Arguments are:
+  // label               -- the exporter label.
+  //                        part of the RFC defining each exporter
+  //                        usage (IN)
+  // context/context_len -- a context to bind to for this connection;
+  //                        optional, can be null, 0 (IN)
+  // use_context         -- whether to use the context value
+  //                        (needed to distinguish no context from
+  //                        zero-length ones).
+  // result              -- where to put the computed value
+  // result_len          -- the length of the computed value
+  virtual bool ExportKeyingMaterial(const std::string& label,
+                                    const uint8_t* context,
+                                    size_t context_len,
+                                    bool use_context,
+                                    uint8_t* result,
+                                    size_t result_len);
+
+  // DTLS-SRTP interface
+  virtual bool SetDtlsSrtpCryptoSuites(const std::vector<int>& crypto_suites);
+  virtual bool GetDtlsSrtpCryptoSuite(int* crypto_suite);
+
+  // Returns true if a TLS connection has been established.
+  // The only difference between this and "GetState() == SE_OPEN" is that if
+  // the peer certificate digest hasn't been verified, the state will still be
+  // SS_OPENING but IsTlsConnected should return true.
+  virtual bool IsTlsConnected() = 0;
+
+  // Capabilities testing.
+  // Used to have "DTLS supported", "DTLS-SRTP supported" etc. methods, but now
+  // that's assumed.
+  static bool IsBoringSsl();
+
+  // Returns true iff the supplied cipher is deemed to be strong.
+  // TODO(torbjorng): Consider removing the KeyType argument.
+  static bool IsAcceptableCipher(int cipher, KeyType key_type);
+  static bool IsAcceptableCipher(const std::string& cipher, KeyType key_type);
+
+  // TODO(guoweis): Move this away from a static class method. Currently this is
+  // introduced such that any caller could depend on sslstreamadapter.h without
+  // depending on specific SSL implementation.
+  static std::string SslCipherSuiteToName(int cipher_suite);
+
+  // Use our timeutils.h source of timing in BoringSSL, allowing us to test
+  // using a fake clock.
+  static void enable_time_callback_for_testing();
+
+  sigslot::signal1<SSLHandshakeError> SignalSSLHandshakeError;
+
+ private:
+  // If true, the server certificate need not match the configured
+  // server_name, and in fact missing certificate authority and other
+  // verification errors are ignored.
+  bool ignore_bad_cert_;
+
+  // If true (default), the client is required to provide a certificate during
+  // handshake. If no certificate is given, handshake fails. This applies to
+  // server mode only.
+  bool client_auth_enabled_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_SSLSTREAMADAPTER_H_
diff --git a/base/sslstreamadapter_unittest.cc b/base/sslstreamadapter_unittest.cc
new file mode 100644
index 0000000..b5184e6
--- /dev/null
+++ b/base/sslstreamadapter_unittest.cc
@@ -0,0 +1,1352 @@
+/*
+ *  Copyright 2011 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "webrtc/base/bufferqueue.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/ssladapter.h"
+#include "webrtc/base/sslidentity.h"
+#include "webrtc/base/sslstreamadapter.h"
+#include "webrtc/base/stream.h"
+
+using ::testing::WithParamInterface;
+using ::testing::Values;
+using ::testing::Combine;
+using ::testing::tuple;
+
+static const int kBlockSize = 4096;
+static const char kExporterLabel[] = "label";
+static const unsigned char kExporterContext[] = "context";
+static int kExporterContextLen = sizeof(kExporterContext);
+
+static const char kRSA_PRIVATE_KEY_PEM[] =
+    "-----BEGIN RSA PRIVATE KEY-----\n"
+    "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMYRkbhmI7kVA/rM\n"
+    "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n"
+    "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n"
+    "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAECgYAvgOs4FJcgvp+TuREx7YtiYVsH\n"
+    "mwQPTum2z/8VzWGwR8BBHBvIpVe1MbD/Y4seyI2aco/7UaisatSgJhsU46/9Y4fq\n"
+    "2TwXH9QANf4at4d9n/R6rzwpAJOpgwZgKvdQjkfrKTtgLV+/dawvpxUYkRH4JZM1\n"
+    "CVGukMfKNrSVH4Ap4QJBAOJmGV1ASPnB4r4nc99at7JuIJmd7fmuVUwUgYi4XgaR\n"
+    "WhScBsgYwZ/JoywdyZJgnbcrTDuVcWG56B3vXbhdpMsCQQDf9zeJrjnPZ3Cqm79y\n"
+    "kdqANep0uwZciiNiWxsQrCHztywOvbFhdp8iYVFG9EK8DMY41Y5TxUwsHD+67zao\n"
+    "ZNqJAkEA1suLUP/GvL8IwuRneQd2tWDqqRQ/Td3qq03hP7e77XtF/buya3Ghclo5\n"
+    "54czUR89QyVfJEC6278nzA7n2h1uVQJAcG6mztNL6ja/dKZjYZye2CY44QjSlLo0\n"
+    "MTgTSjdfg/28fFn2Jjtqf9Pi/X+50LWI/RcYMC2no606wRk9kyOuIQJBAK6VSAim\n"
+    "1pOEjsYQn0X5KEIrz1G3bfCbB848Ime3U2/FWlCHMr6ch8kCZ5d1WUeJD3LbwMNG\n"
+    "UCXiYxSsu20QNVw=\n"
+    "-----END RSA PRIVATE KEY-----\n";
+
+static const char kCERT_PEM[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIBmTCCAQKgAwIBAgIEbzBSAjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZX\n"
+    "ZWJSVEMwHhcNMTQwMTAyMTgyNDQ3WhcNMTQwMjAxMTgyNDQ3WjARMQ8wDQYDVQQD\n"
+    "EwZXZWJSVEMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMYRkbhmI7kVA/rM\n"
+    "czsZ+6JDhDvnkF+vn6yCAGuRPV03zuRqZtDy4N4to7PZu9PjqrRl7nDMXrG3YG9y\n"
+    "rlIAZ72KjcKKFAJxQyAKLCIdawKRyp8RdK3LEySWEZb0AV58IadqPZDTNHHRX8dz\n"
+    "5aTSMsbbkZ+C/OzTnbiMqLL/vg6jAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAUflI\n"
+    "VUe5Krqf5RVa5C3u/UTAOAUJBiDS3VANTCLBxjuMsvqOG0WvaYWP3HYPgrz0jXK2\n"
+    "LJE/mGw3MyFHEqi81jh95J+ypl6xKW6Rm8jKLR87gUvCaVYn/Z4/P3AqcQTB7wOv\n"
+    "UD0A8qfhfDM+LK6rPAnCsVN0NRDY3jvd6rzix9M=\n"
+    "-----END CERTIFICATE-----\n";
+
+class SSLStreamAdapterTestBase;
+
+class SSLDummyStreamBase : public rtc::StreamInterface,
+                           public sigslot::has_slots<> {
+ public:
+  SSLDummyStreamBase(SSLStreamAdapterTestBase* test,
+                     const std::string &side,
+                     rtc::StreamInterface* in,
+                     rtc::StreamInterface* out) :
+      test_base_(test),
+      side_(side),
+      in_(in),
+      out_(out),
+      first_packet_(true) {
+    in_->SignalEvent.connect(this, &SSLDummyStreamBase::OnEventIn);
+    out_->SignalEvent.connect(this, &SSLDummyStreamBase::OnEventOut);
+  }
+
+  rtc::StreamState GetState() const override { return rtc::SS_OPEN; }
+
+  rtc::StreamResult Read(void* buffer, size_t buffer_len,
+                         size_t* read, int* error) override {
+    rtc::StreamResult r;
+
+    r = in_->Read(buffer, buffer_len, read, error);
+    if (r == rtc::SR_BLOCK)
+      return rtc::SR_BLOCK;
+    if (r == rtc::SR_EOS)
+      return rtc::SR_EOS;
+
+    if (r != rtc::SR_SUCCESS) {
+      ADD_FAILURE();
+      return rtc::SR_ERROR;
+    }
+
+    return rtc::SR_SUCCESS;
+  }
+
+  // Catch readability events on in and pass them up.
+  void OnEventIn(rtc::StreamInterface* stream, int sig, int err) {
+    int mask = (rtc::SE_READ | rtc::SE_CLOSE);
+
+    if (sig & mask) {
+      LOG(LS_VERBOSE) << "SSLDummyStreamBase::OnEvent side=" << side_
+                      << " sig=" << sig << " forwarding upward";
+      PostEvent(sig & mask, 0);
+    }
+  }
+
+  // Catch writeability events on out and pass them up.
+  void OnEventOut(rtc::StreamInterface* stream, int sig, int err) {
+    if (sig & rtc::SE_WRITE) {
+      LOG(LS_VERBOSE) << "SSLDummyStreamBase::OnEvent side=" << side_
+                      << " sig=" << sig << " forwarding upward";
+
+      PostEvent(sig & rtc::SE_WRITE, 0);
+    }
+  }
+
+  // Write to the outgoing FifoBuffer
+  rtc::StreamResult WriteData(const void* data, size_t data_len,
+                              size_t* written, int* error) {
+    return out_->Write(data, data_len, written, error);
+  }
+
+  rtc::StreamResult Write(const void* data, size_t data_len,
+                          size_t* written, int* error) override;
+
+  void Close() override {
+    LOG(LS_INFO) << "Closing outbound stream";
+    out_->Close();
+  }
+
+ protected:
+  SSLStreamAdapterTestBase* test_base_;
+  const std::string side_;
+  rtc::StreamInterface* in_;
+  rtc::StreamInterface* out_;
+  bool first_packet_;
+};
+
+class SSLDummyStreamTLS : public SSLDummyStreamBase {
+ public:
+  SSLDummyStreamTLS(SSLStreamAdapterTestBase* test,
+                    const std::string& side,
+                    rtc::FifoBuffer* in,
+                    rtc::FifoBuffer* out) :
+      SSLDummyStreamBase(test, side, in, out) {
+  }
+};
+
+class BufferQueueStream : public rtc::BufferQueue,
+                          public rtc::StreamInterface {
+ public:
+  BufferQueueStream(size_t capacity, size_t default_size)
+      : rtc::BufferQueue(capacity, default_size) {
+  }
+
+  // Implementation of abstract StreamInterface methods.
+
+  // A buffer queue stream is always "open".
+  rtc::StreamState GetState() const override { return rtc::SS_OPEN; }
+
+  // Reading a buffer queue stream will either succeed or block.
+  rtc::StreamResult Read(void* buffer, size_t buffer_len,
+                         size_t* read, int* error) override {
+    if (!ReadFront(buffer, buffer_len, read)) {
+      return rtc::SR_BLOCK;
+    }
+    return rtc::SR_SUCCESS;
+  }
+
+  // Writing to a buffer queue stream will either succeed or block.
+  rtc::StreamResult Write(const void* data, size_t data_len,
+                          size_t* written, int* error) override {
+    if (!WriteBack(data, data_len, written)) {
+      return rtc::SR_BLOCK;
+    }
+    return rtc::SR_SUCCESS;
+  }
+
+  // A buffer queue stream can not be closed.
+  void Close() override {}
+
+ protected:
+  void NotifyReadableForTest() override {
+    PostEvent(rtc::SE_READ, 0);
+  }
+
+  void NotifyWritableForTest() override {
+    PostEvent(rtc::SE_WRITE, 0);
+  }
+};
+
+class SSLDummyStreamDTLS : public SSLDummyStreamBase {
+ public:
+  SSLDummyStreamDTLS(SSLStreamAdapterTestBase* test,
+                     const std::string& side,
+                     BufferQueueStream* in,
+                     BufferQueueStream* out) :
+      SSLDummyStreamBase(test, side, in, out) {
+  }
+};
+
+static const int kFifoBufferSize = 4096;
+static const int kBufferCapacity = 1;
+static const size_t kDefaultBufferSize = 2048;
+
+class SSLStreamAdapterTestBase : public testing::Test,
+                                 public sigslot::has_slots<> {
+ public:
+  SSLStreamAdapterTestBase(
+      const std::string& client_cert_pem,
+      const std::string& client_private_key_pem,
+      bool dtls,
+      rtc::KeyParams client_key_type = rtc::KeyParams(rtc::KT_DEFAULT),
+      rtc::KeyParams server_key_type = rtc::KeyParams(rtc::KT_DEFAULT))
+      : client_cert_pem_(client_cert_pem),
+        client_private_key_pem_(client_private_key_pem),
+        client_key_type_(client_key_type),
+        server_key_type_(server_key_type),
+        client_stream_(nullptr),
+        server_stream_(nullptr),
+        client_identity_(nullptr),
+        server_identity_(nullptr),
+        delay_(0),
+        mtu_(1460),
+        loss_(0),
+        lose_first_packet_(false),
+        damage_(false),
+        dtls_(dtls),
+        handshake_wait_(5000),
+        identities_set_(false) {
+    // Set use of the test RNG to get predictable loss patterns.
+    rtc::SetRandomTestMode(true);
+  }
+
+  ~SSLStreamAdapterTestBase() {
+    // Put it back for the next test.
+    rtc::SetRandomTestMode(false);
+  }
+
+  void SetUp() override {
+    CreateStreams();
+
+    client_ssl_.reset(rtc::SSLStreamAdapter::Create(client_stream_));
+    server_ssl_.reset(rtc::SSLStreamAdapter::Create(server_stream_));
+
+    // Set up the slots
+    client_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent);
+    server_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent);
+
+    if (!client_cert_pem_.empty() && !client_private_key_pem_.empty()) {
+      client_identity_ = rtc::SSLIdentity::FromPEMStrings(
+          client_private_key_pem_, client_cert_pem_);
+    } else {
+      client_identity_ = rtc::SSLIdentity::Generate("client", client_key_type_);
+    }
+    server_identity_ = rtc::SSLIdentity::Generate("server", server_key_type_);
+
+    client_ssl_->SetIdentity(client_identity_);
+    server_ssl_->SetIdentity(server_identity_);
+  }
+
+  void TearDown() override {
+    client_ssl_.reset(nullptr);
+    server_ssl_.reset(nullptr);
+  }
+
+  virtual void CreateStreams() = 0;
+
+  // Recreate the client/server identities with the specified validity period.
+  // |not_before| and |not_after| are offsets from the current time in number
+  // of seconds.
+  void ResetIdentitiesWithValidity(int not_before, int not_after) {
+    CreateStreams();
+
+    client_ssl_.reset(rtc::SSLStreamAdapter::Create(client_stream_));
+    server_ssl_.reset(rtc::SSLStreamAdapter::Create(server_stream_));
+
+    client_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent);
+    server_ssl_->SignalEvent.connect(this, &SSLStreamAdapterTestBase::OnEvent);
+
+    time_t now = time(nullptr);
+
+    rtc::SSLIdentityParams client_params;
+    client_params.key_params = rtc::KeyParams(rtc::KT_DEFAULT);
+    client_params.common_name = "client";
+    client_params.not_before = now + not_before;
+    client_params.not_after = now + not_after;
+    client_identity_ = rtc::SSLIdentity::GenerateForTest(client_params);
+
+    rtc::SSLIdentityParams server_params;
+    server_params.key_params = rtc::KeyParams(rtc::KT_DEFAULT);
+    server_params.common_name = "server";
+    server_params.not_before = now + not_before;
+    server_params.not_after = now + not_after;
+    server_identity_ = rtc::SSLIdentity::GenerateForTest(server_params);
+
+    client_ssl_->SetIdentity(client_identity_);
+    server_ssl_->SetIdentity(server_identity_);
+  }
+
+  virtual void OnEvent(rtc::StreamInterface *stream, int sig, int err) {
+    LOG(LS_VERBOSE) << "SSLStreamAdapterTestBase::OnEvent sig=" << sig;
+
+    if (sig & rtc::SE_READ) {
+      ReadData(stream);
+    }
+
+    if ((stream == client_ssl_.get()) && (sig & rtc::SE_WRITE)) {
+      WriteData();
+    }
+  }
+
+  void SetPeerIdentitiesByDigest(bool correct, bool expect_success) {
+    unsigned char server_digest[20];
+    size_t server_digest_len;
+    unsigned char client_digest[20];
+    size_t client_digest_len;
+    bool rv;
+    rtc::SSLPeerCertificateDigestError err;
+    rtc::SSLPeerCertificateDigestError expected_err =
+        expect_success
+            ? rtc::SSLPeerCertificateDigestError::NONE
+            : rtc::SSLPeerCertificateDigestError::VERIFICATION_FAILED;
+
+    LOG(LS_INFO) << "Setting peer identities by digest";
+
+    rv = server_identity_->certificate().ComputeDigest(
+        rtc::DIGEST_SHA_1, server_digest, 20, &server_digest_len);
+    ASSERT_TRUE(rv);
+    rv = client_identity_->certificate().ComputeDigest(
+        rtc::DIGEST_SHA_1, client_digest, 20, &client_digest_len);
+    ASSERT_TRUE(rv);
+
+    if (!correct) {
+      LOG(LS_INFO) << "Setting bogus digest for server cert";
+      server_digest[0]++;
+    }
+    rv = client_ssl_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, server_digest,
+                                               server_digest_len, &err);
+    EXPECT_EQ(expected_err, err);
+    EXPECT_EQ(expect_success, rv);
+
+    if (!correct) {
+      LOG(LS_INFO) << "Setting bogus digest for client cert";
+      client_digest[0]++;
+    }
+    rv = server_ssl_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, client_digest,
+                                               client_digest_len, &err);
+    EXPECT_EQ(expected_err, err);
+    EXPECT_EQ(expect_success, rv);
+
+    identities_set_ = true;
+  }
+
+  void SetupProtocolVersions(rtc::SSLProtocolVersion server_version,
+                             rtc::SSLProtocolVersion client_version) {
+    server_ssl_->SetMaxProtocolVersion(server_version);
+    client_ssl_->SetMaxProtocolVersion(client_version);
+  }
+
+  void TestHandshake(bool expect_success = true) {
+    server_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS :
+                         rtc::SSL_MODE_TLS);
+    client_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS :
+                         rtc::SSL_MODE_TLS);
+
+    if (!dtls_) {
+      // Make sure we simulate a reliable network for TLS.
+      // This is just a check to make sure that people don't write wrong
+      // tests.
+      RTC_CHECK_EQ(1460, mtu_);
+      RTC_CHECK(!loss_);
+      RTC_CHECK(!lose_first_packet_);
+    }
+
+    if (!identities_set_)
+      SetPeerIdentitiesByDigest(true, true);
+
+    // Start the handshake
+    int rv;
+
+    server_ssl_->SetServerRole();
+    rv = server_ssl_->StartSSL();
+    ASSERT_EQ(0, rv);
+
+    rv = client_ssl_->StartSSL();
+    ASSERT_EQ(0, rv);
+
+    // Now run the handshake
+    if (expect_success) {
+      EXPECT_TRUE_WAIT((client_ssl_->GetState() == rtc::SS_OPEN)
+                       && (server_ssl_->GetState() == rtc::SS_OPEN),
+                       handshake_wait_);
+    } else {
+      EXPECT_TRUE_WAIT(client_ssl_->GetState() == rtc::SS_CLOSED,
+                       handshake_wait_);
+    }
+  }
+
+  // This tests that the handshake can complete before the identity is
+  // verified, and the identity will be verified after the fact.
+  void TestHandshakeWithDelayedIdentity(bool valid_identity) {
+    server_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS);
+    client_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS);
+
+    if (!dtls_) {
+      // Make sure we simulate a reliable network for TLS.
+      // This is just a check to make sure that people don't write wrong
+      // tests.
+      RTC_CHECK_EQ(1460, mtu_);
+      RTC_CHECK(!loss_);
+      RTC_CHECK(!lose_first_packet_);
+    }
+
+    // Start the handshake
+    int rv;
+
+    server_ssl_->SetServerRole();
+    rv = server_ssl_->StartSSL();
+    ASSERT_EQ(0, rv);
+
+    rv = client_ssl_->StartSSL();
+    ASSERT_EQ(0, rv);
+
+    // Now run the handshake.
+    EXPECT_TRUE_WAIT(
+        client_ssl_->IsTlsConnected() && server_ssl_->IsTlsConnected(),
+        handshake_wait_);
+
+    // Until the identity has been verified, the state should still be
+    // SS_OPENING and writes should return SR_BLOCK.
+    EXPECT_EQ(rtc::SS_OPENING, client_ssl_->GetState());
+    EXPECT_EQ(rtc::SS_OPENING, server_ssl_->GetState());
+    unsigned char packet[1];
+    size_t sent;
+    EXPECT_EQ(rtc::SR_BLOCK, client_ssl_->Write(&packet, 1, &sent, 0));
+    EXPECT_EQ(rtc::SR_BLOCK, server_ssl_->Write(&packet, 1, &sent, 0));
+
+    // If we set an invalid identity at this point, SetPeerCertificateDigest
+    // should return false.
+    SetPeerIdentitiesByDigest(valid_identity, valid_identity);
+    // State should then transition to SS_OPEN or SS_CLOSED based on validation
+    // of the identity.
+    if (valid_identity) {
+      EXPECT_EQ(rtc::SS_OPEN, client_ssl_->GetState());
+      EXPECT_EQ(rtc::SS_OPEN, server_ssl_->GetState());
+    } else {
+      EXPECT_EQ(rtc::SS_CLOSED, client_ssl_->GetState());
+      EXPECT_EQ(rtc::SS_CLOSED, server_ssl_->GetState());
+    }
+  }
+
+  rtc::StreamResult DataWritten(SSLDummyStreamBase *from, const void *data,
+                                size_t data_len, size_t *written,
+                                int *error) {
+    // Randomly drop loss_ percent of packets
+    if (rtc::CreateRandomId() % 100 < static_cast<uint32_t>(loss_)) {
+      LOG(LS_VERBOSE) << "Randomly dropping packet, size=" << data_len;
+      *written = data_len;
+      return rtc::SR_SUCCESS;
+    }
+    if (dtls_ && (data_len > mtu_)) {
+      LOG(LS_VERBOSE) << "Dropping packet > mtu, size=" << data_len;
+      *written = data_len;
+      return rtc::SR_SUCCESS;
+    }
+
+    // Optionally damage application data (type 23). Note that we don't damage
+    // handshake packets and we damage the last byte to keep the header
+    // intact but break the MAC.
+    if (damage_ && (*static_cast<const unsigned char *>(data) == 23)) {
+      std::vector<char> buf(data_len);
+
+      LOG(LS_VERBOSE) << "Damaging packet";
+
+      memcpy(&buf[0], data, data_len);
+      buf[data_len - 1]++;
+
+      return from->WriteData(&buf[0], data_len, written, error);
+    }
+
+    return from->WriteData(data, data_len, written, error);
+  }
+
+  void SetDelay(int delay) {
+    delay_ = delay;
+  }
+  int GetDelay() { return delay_; }
+
+  void SetLoseFirstPacket(bool lose) {
+    lose_first_packet_ = lose;
+  }
+  bool GetLoseFirstPacket() { return lose_first_packet_; }
+
+  void SetLoss(int percent) {
+    loss_ = percent;
+  }
+
+  void SetDamage() {
+    damage_ = true;
+  }
+
+  void SetMtu(size_t mtu) {
+    mtu_ = mtu;
+  }
+
+  void SetHandshakeWait(int wait) {
+    handshake_wait_ = wait;
+  }
+
+  void SetDtlsSrtpCryptoSuites(const std::vector<int>& ciphers, bool client) {
+    if (client)
+      client_ssl_->SetDtlsSrtpCryptoSuites(ciphers);
+    else
+      server_ssl_->SetDtlsSrtpCryptoSuites(ciphers);
+  }
+
+  bool GetDtlsSrtpCryptoSuite(bool client, int* retval) {
+    if (client)
+      return client_ssl_->GetDtlsSrtpCryptoSuite(retval);
+    else
+      return server_ssl_->GetDtlsSrtpCryptoSuite(retval);
+  }
+
+  std::unique_ptr<rtc::SSLCertificate> GetPeerCertificate(bool client) {
+    if (client)
+      return client_ssl_->GetPeerCertificate();
+    else
+      return server_ssl_->GetPeerCertificate();
+  }
+
+  bool GetSslCipherSuite(bool client, int* retval) {
+    if (client)
+      return client_ssl_->GetSslCipherSuite(retval);
+    else
+      return server_ssl_->GetSslCipherSuite(retval);
+  }
+
+  int GetSslVersion(bool client) {
+    if (client)
+      return client_ssl_->GetSslVersion();
+    else
+      return server_ssl_->GetSslVersion();
+  }
+
+  bool ExportKeyingMaterial(const char *label,
+                            const unsigned char *context,
+                            size_t context_len,
+                            bool use_context,
+                            bool client,
+                            unsigned char *result,
+                            size_t result_len) {
+    if (client)
+      return client_ssl_->ExportKeyingMaterial(label,
+                                               context, context_len,
+                                               use_context,
+                                               result, result_len);
+    else
+      return server_ssl_->ExportKeyingMaterial(label,
+                                               context, context_len,
+                                               use_context,
+                                               result, result_len);
+  }
+
+  // To be implemented by subclasses.
+  virtual void WriteData() = 0;
+  virtual void ReadData(rtc::StreamInterface *stream) = 0;
+  virtual void TestTransfer(int size) = 0;
+
+ protected:
+  std::string client_cert_pem_;
+  std::string client_private_key_pem_;
+  rtc::KeyParams client_key_type_;
+  rtc::KeyParams server_key_type_;
+  SSLDummyStreamBase *client_stream_;  // freed by client_ssl_ destructor
+  SSLDummyStreamBase *server_stream_;  // freed by server_ssl_ destructor
+  std::unique_ptr<rtc::SSLStreamAdapter> client_ssl_;
+  std::unique_ptr<rtc::SSLStreamAdapter> server_ssl_;
+  rtc::SSLIdentity *client_identity_;  // freed by client_ssl_ destructor
+  rtc::SSLIdentity *server_identity_;  // freed by server_ssl_ destructor
+  int delay_;
+  size_t mtu_;
+  int loss_;
+  bool lose_first_packet_;
+  bool damage_;
+  bool dtls_;
+  int handshake_wait_;
+  bool identities_set_;
+};
+
+class SSLStreamAdapterTestTLS
+    : public SSLStreamAdapterTestBase,
+      public WithParamInterface<tuple<rtc::KeyParams, rtc::KeyParams>> {
+ public:
+  SSLStreamAdapterTestTLS()
+      : SSLStreamAdapterTestBase("",
+                                 "",
+                                 false,
+                                 ::testing::get<0>(GetParam()),
+                                 ::testing::get<1>(GetParam())),
+        client_buffer_(kFifoBufferSize),
+        server_buffer_(kFifoBufferSize) {
+  }
+
+  void CreateStreams() override {
+    client_stream_ =
+        new SSLDummyStreamTLS(this, "c2s", &client_buffer_, &server_buffer_);
+    server_stream_ =
+        new SSLDummyStreamTLS(this, "s2c", &server_buffer_, &client_buffer_);
+  }
+
+  // Test data transfer for TLS
+  void TestTransfer(int size) override {
+    LOG(LS_INFO) << "Starting transfer test with " << size << " bytes";
+    // Create some dummy data to send.
+    size_t received;
+
+    send_stream_.ReserveSize(size);
+    for (int i = 0; i < size; ++i) {
+      char ch = static_cast<char>(i);
+      send_stream_.Write(&ch, 1, nullptr, nullptr);
+    }
+    send_stream_.Rewind();
+
+    // Prepare the receive stream.
+    recv_stream_.ReserveSize(size);
+
+    // Start sending
+    WriteData();
+
+    // Wait for the client to close
+    EXPECT_TRUE_WAIT(server_ssl_->GetState() == rtc::SS_CLOSED, 10000);
+
+    // Now check the data
+    recv_stream_.GetSize(&received);
+
+    EXPECT_EQ(static_cast<size_t>(size), received);
+    EXPECT_EQ(0, memcmp(send_stream_.GetBuffer(),
+                        recv_stream_.GetBuffer(), size));
+  }
+
+  void WriteData() override {
+    size_t position, tosend, size;
+    rtc::StreamResult rv;
+    size_t sent;
+    char block[kBlockSize];
+
+    send_stream_.GetSize(&size);
+    if (!size)
+      return;
+
+    for (;;) {
+      send_stream_.GetPosition(&position);
+      if (send_stream_.Read(block, sizeof(block), &tosend, nullptr) !=
+          rtc::SR_EOS) {
+        rv = client_ssl_->Write(block, tosend, &sent, 0);
+
+        if (rv == rtc::SR_SUCCESS) {
+          send_stream_.SetPosition(position + sent);
+          LOG(LS_VERBOSE) << "Sent: " << position + sent;
+        } else if (rv == rtc::SR_BLOCK) {
+          LOG(LS_VERBOSE) << "Blocked...";
+          send_stream_.SetPosition(position);
+          break;
+        } else {
+          ADD_FAILURE();
+          break;
+        }
+      } else {
+        // Now close
+        LOG(LS_INFO) << "Wrote " << position << " bytes. Closing";
+        client_ssl_->Close();
+        break;
+      }
+    }
+  };
+
+  void ReadData(rtc::StreamInterface *stream) override {
+    char buffer[1600];
+    size_t bread;
+    int err2;
+    rtc::StreamResult r;
+
+    for (;;) {
+      r = stream->Read(buffer, sizeof(buffer), &bread, &err2);
+
+      if (r == rtc::SR_ERROR || r == rtc::SR_EOS) {
+        // Unfortunately, errors are the way that the stream adapter
+        // signals close in OpenSSL.
+        stream->Close();
+        return;
+      }
+
+      if (r == rtc::SR_BLOCK)
+        break;
+
+      ASSERT_EQ(rtc::SR_SUCCESS, r);
+      LOG(LS_VERBOSE) << "Read " << bread;
+
+      recv_stream_.Write(buffer, bread, nullptr, nullptr);
+    }
+  }
+
+ private:
+  rtc::FifoBuffer client_buffer_;
+  rtc::FifoBuffer server_buffer_;
+  rtc::MemoryStream send_stream_;
+  rtc::MemoryStream recv_stream_;
+};
+
+class SSLStreamAdapterTestDTLS
+    : public SSLStreamAdapterTestBase,
+      public WithParamInterface<tuple<rtc::KeyParams, rtc::KeyParams>> {
+ public:
+  SSLStreamAdapterTestDTLS()
+      : SSLStreamAdapterTestBase("",
+                                 "",
+                                 true,
+                                 ::testing::get<0>(GetParam()),
+                                 ::testing::get<1>(GetParam())),
+        client_buffer_(kBufferCapacity, kDefaultBufferSize),
+        server_buffer_(kBufferCapacity, kDefaultBufferSize),
+        packet_size_(1000),
+        count_(0),
+        sent_(0) {}
+
+  SSLStreamAdapterTestDTLS(const std::string& cert_pem,
+                           const std::string& private_key_pem) :
+      SSLStreamAdapterTestBase(cert_pem, private_key_pem, true),
+      client_buffer_(kBufferCapacity, kDefaultBufferSize),
+      server_buffer_(kBufferCapacity, kDefaultBufferSize),
+      packet_size_(1000), count_(0), sent_(0) {
+  }
+
+  void CreateStreams() override {
+    client_stream_ =
+        new SSLDummyStreamDTLS(this, "c2s", &client_buffer_, &server_buffer_);
+    server_stream_ =
+        new SSLDummyStreamDTLS(this, "s2c", &server_buffer_, &client_buffer_);
+  }
+
+  void WriteData() override {
+    unsigned char *packet = new unsigned char[1600];
+
+    while (sent_ < count_) {
+      unsigned int rand_state = sent_;
+      packet[0] = sent_;
+      for (size_t i = 1; i < packet_size_; i++) {
+        // This is a simple LC PRNG.  Keep in synch with identical code below.
+        rand_state = (rand_state * 251 + 19937) >> 7;
+        packet[i] = rand_state & 0xff;
+      }
+
+      size_t sent;
+      rtc::StreamResult rv = client_ssl_->Write(packet, packet_size_, &sent, 0);
+      if (rv == rtc::SR_SUCCESS) {
+        LOG(LS_VERBOSE) << "Sent: " << sent_;
+        sent_++;
+      } else if (rv == rtc::SR_BLOCK) {
+        LOG(LS_VERBOSE) << "Blocked...";
+        break;
+      } else {
+        ADD_FAILURE();
+        break;
+      }
+    }
+
+    delete [] packet;
+  }
+
+  void ReadData(rtc::StreamInterface *stream) override {
+    unsigned char buffer[2000];
+    size_t bread;
+    int err2;
+    rtc::StreamResult r;
+
+    for (;;) {
+      r = stream->Read(buffer, 2000, &bread, &err2);
+
+      if (r == rtc::SR_ERROR) {
+        // Unfortunately, errors are the way that the stream adapter
+        // signals close right now
+        stream->Close();
+        return;
+      }
+
+      if (r == rtc::SR_BLOCK)
+        break;
+
+      ASSERT_EQ(rtc::SR_SUCCESS, r);
+      LOG(LS_VERBOSE) << "Read " << bread;
+
+      // Now parse the datagram
+      ASSERT_EQ(packet_size_, bread);
+      unsigned char packet_num = buffer[0];
+
+      unsigned int rand_state = packet_num;
+      for (size_t i = 1; i < packet_size_; i++) {
+        // This is a simple LC PRNG.  Keep in synch with identical code above.
+        rand_state = (rand_state * 251 + 19937) >> 7;
+        ASSERT_EQ(rand_state & 0xff, buffer[i]);
+      }
+      received_.insert(packet_num);
+    }
+  }
+
+  void TestTransfer(int count) override {
+    count_ = count;
+
+    WriteData();
+
+    EXPECT_TRUE_WAIT(sent_ == count_, 10000);
+    LOG(LS_INFO) << "sent_ == " << sent_;
+
+    if (damage_) {
+      WAIT(false, 2000);
+      EXPECT_EQ(0U, received_.size());
+    } else if (loss_ == 0) {
+        EXPECT_EQ_WAIT(static_cast<size_t>(sent_), received_.size(), 1000);
+    } else {
+      LOG(LS_INFO) << "Sent " << sent_ << " packets; received " <<
+          received_.size();
+    }
+  };
+
+ private:
+  BufferQueueStream client_buffer_;
+  BufferQueueStream server_buffer_;
+  size_t packet_size_;
+  int count_;
+  int sent_;
+  std::set<int> received_;
+};
+
+
+rtc::StreamResult SSLDummyStreamBase::Write(const void* data, size_t data_len,
+                                              size_t* written, int* error) {
+  LOG(LS_VERBOSE) << "Writing to loopback " << data_len;
+
+  if (first_packet_) {
+    first_packet_ = false;
+    if (test_base_->GetLoseFirstPacket()) {
+      LOG(LS_INFO) << "Losing initial packet of length " << data_len;
+      *written = data_len;  // Fake successful writing also to writer.
+      return rtc::SR_SUCCESS;
+    }
+  }
+
+  return test_base_->DataWritten(this, data, data_len, written, error);
+};
+
+class SSLStreamAdapterTestDTLSFromPEMStrings : public SSLStreamAdapterTestDTLS {
+ public:
+  SSLStreamAdapterTestDTLSFromPEMStrings() :
+      SSLStreamAdapterTestDTLS(kCERT_PEM, kRSA_PRIVATE_KEY_PEM) {
+  }
+};
+
+// Basic tests: TLS
+
+// Test that we can make a handshake work
+TEST_P(SSLStreamAdapterTestTLS, TestTLSConnect) {
+  TestHandshake();
+};
+
+// Test that closing the connection on one side updates the other side.
+TEST_P(SSLStreamAdapterTestTLS, TestTLSClose) {
+  TestHandshake();
+  client_ssl_->Close();
+  EXPECT_EQ_WAIT(rtc::SS_CLOSED, server_ssl_->GetState(), handshake_wait_);
+};
+
+// Test transfer -- trivial
+TEST_P(SSLStreamAdapterTestTLS, TestTLSTransfer) {
+  TestHandshake();
+  TestTransfer(100000);
+};
+
+// Test read-write after close.
+TEST_P(SSLStreamAdapterTestTLS, ReadWriteAfterClose) {
+  TestHandshake();
+  TestTransfer(100000);
+  client_ssl_->Close();
+
+  rtc::StreamResult rv;
+  char block[kBlockSize];
+  size_t dummy;
+
+  // It's an error to write after closed.
+  rv = client_ssl_->Write(block, sizeof(block), &dummy, nullptr);
+  ASSERT_EQ(rtc::SR_ERROR, rv);
+
+  // But after closed read gives you EOS.
+  rv = client_ssl_->Read(block, sizeof(block), &dummy, nullptr);
+  ASSERT_EQ(rtc::SR_EOS, rv);
+};
+
+// Test a handshake with a bogus peer digest
+TEST_P(SSLStreamAdapterTestTLS, TestTLSBogusDigest) {
+  SetPeerIdentitiesByDigest(false, true);
+  TestHandshake(false);
+};
+
+TEST_P(SSLStreamAdapterTestTLS, TestTLSDelayedIdentity) {
+  TestHandshakeWithDelayedIdentity(true);
+};
+
+TEST_P(SSLStreamAdapterTestTLS, TestTLSDelayedIdentityWithBogusDigest) {
+  TestHandshakeWithDelayedIdentity(false);
+};
+
+// Test that the correct error is returned when SetPeerCertificateDigest is
+// called with an unknown algorithm.
+TEST_P(SSLStreamAdapterTestTLS,
+       TestSetPeerCertificateDigestWithUnknownAlgorithm) {
+  unsigned char server_digest[20];
+  size_t server_digest_len;
+  bool rv;
+  rtc::SSLPeerCertificateDigestError err;
+
+  rv = server_identity_->certificate().ComputeDigest(
+      rtc::DIGEST_SHA_1, server_digest, 20, &server_digest_len);
+  ASSERT_TRUE(rv);
+
+  rv = client_ssl_->SetPeerCertificateDigest("unknown algorithm", server_digest,
+                                             server_digest_len, &err);
+  EXPECT_EQ(rtc::SSLPeerCertificateDigestError::UNKNOWN_ALGORITHM, err);
+  EXPECT_FALSE(rv);
+}
+
+// Test that the correct error is returned when SetPeerCertificateDigest is
+// called with an invalid digest length.
+TEST_P(SSLStreamAdapterTestTLS, TestSetPeerCertificateDigestWithInvalidLength) {
+  unsigned char server_digest[20];
+  size_t server_digest_len;
+  bool rv;
+  rtc::SSLPeerCertificateDigestError err;
+
+  rv = server_identity_->certificate().ComputeDigest(
+      rtc::DIGEST_SHA_1, server_digest, 20, &server_digest_len);
+  ASSERT_TRUE(rv);
+
+  rv = client_ssl_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, server_digest,
+                                             server_digest_len - 1, &err);
+  EXPECT_EQ(rtc::SSLPeerCertificateDigestError::INVALID_LENGTH, err);
+  EXPECT_FALSE(rv);
+}
+
+// Test moving a bunch of data
+
+// Basic tests: DTLS
+// Test that we can make a handshake work
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSConnect) {
+  TestHandshake();
+};
+
+// Test that we can make a handshake work if the first packet in
+// each direction is lost. This gives us predictable loss
+// rather than having to tune random
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSConnectWithLostFirstPacket) {
+  SetLoseFirstPacket(true);
+  TestHandshake();
+};
+
+// Test a handshake with loss and delay
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSConnectWithLostFirstPacketDelay2s) {
+  SetLoseFirstPacket(true);
+  SetDelay(2000);
+  SetHandshakeWait(20000);
+  TestHandshake();
+};
+
+// Test a handshake with small MTU
+// Disabled due to https://code.google.com/p/webrtc/issues/detail?id=3910
+TEST_P(SSLStreamAdapterTestDTLS, DISABLED_TestDTLSConnectWithSmallMtu) {
+  SetMtu(700);
+  SetHandshakeWait(20000);
+  TestHandshake();
+};
+
+// Test transfer -- trivial
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSTransfer) {
+  TestHandshake();
+  TestTransfer(100);
+};
+
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSTransferWithLoss) {
+  TestHandshake();
+  SetLoss(10);
+  TestTransfer(100);
+};
+
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSTransferWithDamage) {
+  SetDamage();  // Must be called first because first packet
+                // write happens at end of handshake.
+  TestHandshake();
+  TestTransfer(100);
+};
+
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSDelayedIdentity) {
+  TestHandshakeWithDelayedIdentity(true);
+};
+
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSDelayedIdentityWithBogusDigest) {
+  TestHandshakeWithDelayedIdentity(false);
+};
+
+// Test DTLS-SRTP with all high ciphers
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpHigh) {
+  std::vector<int> high;
+  high.push_back(rtc::SRTP_AES128_CM_SHA1_80);
+  SetDtlsSrtpCryptoSuites(high, true);
+  SetDtlsSrtpCryptoSuites(high, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(client_cipher, rtc::SRTP_AES128_CM_SHA1_80);
+};
+
+// Test DTLS-SRTP with all low ciphers
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpLow) {
+  std::vector<int> low;
+  low.push_back(rtc::SRTP_AES128_CM_SHA1_32);
+  SetDtlsSrtpCryptoSuites(low, true);
+  SetDtlsSrtpCryptoSuites(low, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(client_cipher, rtc::SRTP_AES128_CM_SHA1_32);
+};
+
+// Test DTLS-SRTP with a mismatch -- should not converge
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpHighLow) {
+  std::vector<int> high;
+  high.push_back(rtc::SRTP_AES128_CM_SHA1_80);
+  std::vector<int> low;
+  low.push_back(rtc::SRTP_AES128_CM_SHA1_32);
+  SetDtlsSrtpCryptoSuites(high, true);
+  SetDtlsSrtpCryptoSuites(low, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_FALSE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_FALSE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+};
+
+// Test DTLS-SRTP with each side being mixed -- should select high
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpMixed) {
+  std::vector<int> mixed;
+  mixed.push_back(rtc::SRTP_AES128_CM_SHA1_80);
+  mixed.push_back(rtc::SRTP_AES128_CM_SHA1_32);
+  SetDtlsSrtpCryptoSuites(mixed, true);
+  SetDtlsSrtpCryptoSuites(mixed, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(client_cipher, rtc::SRTP_AES128_CM_SHA1_80);
+};
+
+// Test DTLS-SRTP with all GCM-128 ciphers.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpGCM128) {
+  std::vector<int> gcm128;
+  gcm128.push_back(rtc::SRTP_AEAD_AES_128_GCM);
+  SetDtlsSrtpCryptoSuites(gcm128, true);
+  SetDtlsSrtpCryptoSuites(gcm128, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(client_cipher, rtc::SRTP_AEAD_AES_128_GCM);
+};
+
+// Test DTLS-SRTP with all GCM-256 ciphers.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpGCM256) {
+  std::vector<int> gcm256;
+  gcm256.push_back(rtc::SRTP_AEAD_AES_256_GCM);
+  SetDtlsSrtpCryptoSuites(gcm256, true);
+  SetDtlsSrtpCryptoSuites(gcm256, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(client_cipher, rtc::SRTP_AEAD_AES_256_GCM);
+};
+
+// Test DTLS-SRTP with mixed GCM-128/-256 ciphers -- should not converge.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpGCMMismatch) {
+  std::vector<int> gcm128;
+  gcm128.push_back(rtc::SRTP_AEAD_AES_128_GCM);
+  std::vector<int> gcm256;
+  gcm256.push_back(rtc::SRTP_AEAD_AES_256_GCM);
+  SetDtlsSrtpCryptoSuites(gcm128, true);
+  SetDtlsSrtpCryptoSuites(gcm256, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_FALSE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_FALSE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+};
+
+// Test DTLS-SRTP with both GCM-128/-256 ciphers -- should select GCM-256.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpGCMMixed) {
+  std::vector<int> gcmBoth;
+  gcmBoth.push_back(rtc::SRTP_AEAD_AES_256_GCM);
+  gcmBoth.push_back(rtc::SRTP_AEAD_AES_128_GCM);
+  SetDtlsSrtpCryptoSuites(gcmBoth, true);
+  SetDtlsSrtpCryptoSuites(gcmBoth, false);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetDtlsSrtpCryptoSuite(false, &server_cipher));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_EQ(client_cipher, rtc::SRTP_AEAD_AES_256_GCM);
+};
+
+// Test SRTP cipher suite lengths.
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSSrtpKeyAndSaltLengths) {
+  int key_len;
+  int salt_len;
+
+  ASSERT_FALSE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_INVALID_CRYPTO_SUITE, &key_len, &salt_len));
+
+  ASSERT_TRUE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_AES128_CM_SHA1_32, &key_len, &salt_len));
+  ASSERT_EQ(128/8, key_len);
+  ASSERT_EQ(112/8, salt_len);
+
+  ASSERT_TRUE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_AES128_CM_SHA1_80, &key_len, &salt_len));
+  ASSERT_EQ(128/8, key_len);
+  ASSERT_EQ(112/8, salt_len);
+
+  ASSERT_TRUE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_AEAD_AES_128_GCM, &key_len, &salt_len));
+  ASSERT_EQ(128/8, key_len);
+  ASSERT_EQ(96/8, salt_len);
+
+  ASSERT_TRUE(rtc::GetSrtpKeyAndSaltLengths(
+      rtc::SRTP_AEAD_AES_256_GCM, &key_len, &salt_len));
+  ASSERT_EQ(256/8, key_len);
+  ASSERT_EQ(96/8, salt_len);
+};
+
+// Test an exporter
+TEST_P(SSLStreamAdapterTestDTLS, TestDTLSExporter) {
+  TestHandshake();
+  unsigned char client_out[20];
+  unsigned char server_out[20];
+
+  bool result;
+  result = ExportKeyingMaterial(kExporterLabel,
+                                kExporterContext, kExporterContextLen,
+                                true, true,
+                                client_out, sizeof(client_out));
+  ASSERT_TRUE(result);
+
+  result = ExportKeyingMaterial(kExporterLabel,
+                                kExporterContext, kExporterContextLen,
+                                true, false,
+                                server_out, sizeof(server_out));
+  ASSERT_TRUE(result);
+
+  ASSERT_TRUE(!memcmp(client_out, server_out, sizeof(client_out)));
+}
+
+// Test not yet valid certificates are not rejected.
+TEST_P(SSLStreamAdapterTestDTLS, TestCertNotYetValid) {
+  long one_day = 60 * 60 * 24;
+  // Make the certificates not valid until one day later.
+  ResetIdentitiesWithValidity(one_day, one_day);
+  TestHandshake();
+}
+
+// Test expired certificates are not rejected.
+TEST_P(SSLStreamAdapterTestDTLS, TestCertExpired) {
+  long one_day = 60 * 60 * 24;
+  // Make the certificates already expired.
+  ResetIdentitiesWithValidity(-one_day, -one_day);
+  TestHandshake();
+}
+
+// Test data transfer using certs created from strings.
+TEST_F(SSLStreamAdapterTestDTLSFromPEMStrings, TestTransfer) {
+  TestHandshake();
+  TestTransfer(100);
+}
+
+// Test getting the remote certificate.
+TEST_F(SSLStreamAdapterTestDTLSFromPEMStrings, TestDTLSGetPeerCertificate) {
+  // Peer certificates haven't been received yet.
+  ASSERT_FALSE(GetPeerCertificate(true));
+  ASSERT_FALSE(GetPeerCertificate(false));
+
+  TestHandshake();
+
+  // The client should have a peer certificate after the handshake.
+  std::unique_ptr<rtc::SSLCertificate> client_peer_cert =
+      GetPeerCertificate(true);
+  ASSERT_TRUE(client_peer_cert);
+
+  // It's not kCERT_PEM.
+  std::string client_peer_string = client_peer_cert->ToPEMString();
+  ASSERT_NE(kCERT_PEM, client_peer_string);
+
+  // It must not have a chain, because the test certs are self-signed.
+  ASSERT_FALSE(client_peer_cert->GetChain());
+
+  // The server should have a peer certificate after the handshake.
+  std::unique_ptr<rtc::SSLCertificate> server_peer_cert =
+      GetPeerCertificate(false);
+  ASSERT_TRUE(server_peer_cert);
+
+  // It's kCERT_PEM
+  ASSERT_EQ(kCERT_PEM, server_peer_cert->ToPEMString());
+
+  // It must not have a chain, because the test certs are self-signed.
+  ASSERT_FALSE(server_peer_cert->GetChain());
+}
+
+// Test getting the used DTLS ciphers.
+// DTLS 1.2 enabled for neither client nor server -> DTLS 1.0 will be used.
+TEST_P(SSLStreamAdapterTestDTLS, TestGetSslCipherSuite) {
+  SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_10);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetSslCipherSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetSslCipherSuite(false, &server_cipher));
+
+  ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(true));
+  ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(false));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_TRUE(rtc::SSLStreamAdapter::IsAcceptableCipher(
+      server_cipher, ::testing::get<1>(GetParam()).type()));
+}
+
+// Test getting the used DTLS 1.2 ciphers.
+// DTLS 1.2 enabled for client and server -> DTLS 1.2 will be used.
+TEST_P(SSLStreamAdapterTestDTLS, TestGetSslCipherSuiteDtls12Both) {
+  SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_12, rtc::SSL_PROTOCOL_DTLS_12);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetSslCipherSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetSslCipherSuite(false, &server_cipher));
+
+  ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_12, GetSslVersion(true));
+  ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_12, GetSslVersion(false));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_TRUE(rtc::SSLStreamAdapter::IsAcceptableCipher(
+      server_cipher, ::testing::get<1>(GetParam()).type()));
+}
+
+// DTLS 1.2 enabled for client only -> DTLS 1.0 will be used.
+TEST_P(SSLStreamAdapterTestDTLS, TestGetSslCipherSuiteDtls12Client) {
+  SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_10, rtc::SSL_PROTOCOL_DTLS_12);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetSslCipherSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetSslCipherSuite(false, &server_cipher));
+
+  ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(true));
+  ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(false));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_TRUE(rtc::SSLStreamAdapter::IsAcceptableCipher(
+      server_cipher, ::testing::get<1>(GetParam()).type()));
+}
+
+// DTLS 1.2 enabled for server only -> DTLS 1.0 will be used.
+TEST_P(SSLStreamAdapterTestDTLS, TestGetSslCipherSuiteDtls12Server) {
+  SetupProtocolVersions(rtc::SSL_PROTOCOL_DTLS_12, rtc::SSL_PROTOCOL_DTLS_10);
+  TestHandshake();
+
+  int client_cipher;
+  ASSERT_TRUE(GetSslCipherSuite(true, &client_cipher));
+  int server_cipher;
+  ASSERT_TRUE(GetSslCipherSuite(false, &server_cipher));
+
+  ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(true));
+  ASSERT_EQ(rtc::SSL_PROTOCOL_DTLS_10, GetSslVersion(false));
+
+  ASSERT_EQ(client_cipher, server_cipher);
+  ASSERT_TRUE(rtc::SSLStreamAdapter::IsAcceptableCipher(
+      server_cipher, ::testing::get<1>(GetParam()).type()));
+}
+
+// The RSA keysizes here might look strange, why not include the RFC's size
+// 2048?. The reason is test case slowness; testing two sizes to exercise
+// parametrization is sufficient.
+INSTANTIATE_TEST_CASE_P(
+    SSLStreamAdapterTestsTLS,
+    SSLStreamAdapterTestTLS,
+    Combine(Values(rtc::KeyParams::RSA(1024, 65537),
+                   rtc::KeyParams::RSA(1152, 65537),
+                   rtc::KeyParams::ECDSA(rtc::EC_NIST_P256)),
+            Values(rtc::KeyParams::RSA(1024, 65537),
+                   rtc::KeyParams::RSA(1152, 65537),
+                   rtc::KeyParams::ECDSA(rtc::EC_NIST_P256))));
+INSTANTIATE_TEST_CASE_P(
+    SSLStreamAdapterTestsDTLS,
+    SSLStreamAdapterTestDTLS,
+    Combine(Values(rtc::KeyParams::RSA(1024, 65537),
+                   rtc::KeyParams::RSA(1152, 65537),
+                   rtc::KeyParams::ECDSA(rtc::EC_NIST_P256)),
+            Values(rtc::KeyParams::RSA(1024, 65537),
+                   rtc::KeyParams::RSA(1152, 65537),
+                   rtc::KeyParams::ECDSA(rtc::EC_NIST_P256))));
diff --git a/base/stream.cc b/base/stream.cc
new file mode 100644
index 0000000..67ef104
--- /dev/null
+++ b/base/stream.cc
@@ -0,0 +1,1123 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#if defined(WEBRTC_POSIX)
+#include <sys/file.h>
+#endif  // WEBRTC_POSIX
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <algorithm>
+#include <string>
+
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/messagequeue.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/timeutils.h"
+
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#define fileno _fileno
+#endif
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamInterface
+///////////////////////////////////////////////////////////////////////////////
+StreamInterface::~StreamInterface() {
+}
+
+StreamResult StreamInterface::WriteAll(const void* data, size_t data_len,
+                                       size_t* written, int* error) {
+  StreamResult result = SR_SUCCESS;
+  size_t total_written = 0, current_written;
+  while (total_written < data_len) {
+    result = Write(static_cast<const char*>(data) + total_written,
+                   data_len - total_written, &current_written, error);
+    if (result != SR_SUCCESS)
+      break;
+    total_written += current_written;
+  }
+  if (written)
+    *written = total_written;
+  return result;
+}
+
+StreamResult StreamInterface::ReadAll(void* buffer, size_t buffer_len,
+                                      size_t* read, int* error) {
+  StreamResult result = SR_SUCCESS;
+  size_t total_read = 0, current_read;
+  while (total_read < buffer_len) {
+    result = Read(static_cast<char*>(buffer) + total_read,
+                  buffer_len - total_read, &current_read, error);
+    if (result != SR_SUCCESS)
+      break;
+    total_read += current_read;
+  }
+  if (read)
+    *read = total_read;
+  return result;
+}
+
+StreamResult StreamInterface::ReadLine(std::string* line) {
+  line->clear();
+  StreamResult result = SR_SUCCESS;
+  while (true) {
+    char ch;
+    result = Read(&ch, sizeof(ch), nullptr, nullptr);
+    if (result != SR_SUCCESS) {
+      break;
+    }
+    if (ch == '\n') {
+      break;
+    }
+    line->push_back(ch);
+  }
+  if (!line->empty()) {   // give back the line we've collected so far with
+    result = SR_SUCCESS;  // a success code.  Otherwise return the last code
+  }
+  return result;
+}
+
+void StreamInterface::PostEvent(Thread* t, int events, int err) {
+  t->Post(RTC_FROM_HERE, this, MSG_POST_EVENT,
+          new StreamEventData(events, err));
+}
+
+void StreamInterface::PostEvent(int events, int err) {
+  PostEvent(Thread::Current(), events, err);
+}
+
+const void* StreamInterface::GetReadData(size_t* data_len) {
+  return nullptr;
+}
+
+void* StreamInterface::GetWriteBuffer(size_t* buf_len) {
+  return nullptr;
+}
+
+bool StreamInterface::SetPosition(size_t position) {
+  return false;
+}
+
+bool StreamInterface::GetPosition(size_t* position) const {
+  return false;
+}
+
+bool StreamInterface::GetSize(size_t* size) const {
+  return false;
+}
+
+bool StreamInterface::GetAvailable(size_t* size) const {
+  return false;
+}
+
+bool StreamInterface::GetWriteRemaining(size_t* size) const {
+  return false;
+}
+
+bool StreamInterface::Flush() {
+  return false;
+}
+
+bool StreamInterface::ReserveSize(size_t size) {
+  return true;
+}
+
+StreamInterface::StreamInterface() {
+}
+
+void StreamInterface::OnMessage(Message* msg) {
+  if (MSG_POST_EVENT == msg->message_id) {
+    StreamEventData* pe = static_cast<StreamEventData*>(msg->pdata);
+    SignalEvent(this, pe->events, pe->error);
+    delete msg->pdata;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamAdapterInterface
+///////////////////////////////////////////////////////////////////////////////
+
+StreamAdapterInterface::StreamAdapterInterface(StreamInterface* stream,
+                                               bool owned)
+    : stream_(stream), owned_(owned) {
+  if (nullptr != stream_)
+    stream_->SignalEvent.connect(this, &StreamAdapterInterface::OnEvent);
+}
+
+StreamState StreamAdapterInterface::GetState() const {
+  return stream_->GetState();
+}
+StreamResult StreamAdapterInterface::Read(void* buffer,
+                                          size_t buffer_len,
+                                          size_t* read,
+                                          int* error) {
+  return stream_->Read(buffer, buffer_len, read, error);
+}
+StreamResult StreamAdapterInterface::Write(const void* data,
+                                           size_t data_len,
+                                           size_t* written,
+                                           int* error) {
+  return stream_->Write(data, data_len, written, error);
+}
+void StreamAdapterInterface::Close() {
+  stream_->Close();
+}
+
+bool StreamAdapterInterface::SetPosition(size_t position) {
+  return stream_->SetPosition(position);
+}
+
+bool StreamAdapterInterface::GetPosition(size_t* position) const {
+  return stream_->GetPosition(position);
+}
+
+bool StreamAdapterInterface::GetSize(size_t* size) const {
+  return stream_->GetSize(size);
+}
+
+bool StreamAdapterInterface::GetAvailable(size_t* size) const {
+  return stream_->GetAvailable(size);
+}
+
+bool StreamAdapterInterface::GetWriteRemaining(size_t* size) const {
+  return stream_->GetWriteRemaining(size);
+}
+
+bool StreamAdapterInterface::ReserveSize(size_t size) {
+  return stream_->ReserveSize(size);
+}
+
+bool StreamAdapterInterface::Flush() {
+  return stream_->Flush();
+}
+
+void StreamAdapterInterface::Attach(StreamInterface* stream, bool owned) {
+  if (nullptr != stream_)
+    stream_->SignalEvent.disconnect(this);
+  if (owned_)
+    delete stream_;
+  stream_ = stream;
+  owned_ = owned;
+  if (nullptr != stream_)
+    stream_->SignalEvent.connect(this, &StreamAdapterInterface::OnEvent);
+}
+
+StreamInterface* StreamAdapterInterface::Detach() {
+  if (nullptr != stream_)
+    stream_->SignalEvent.disconnect(this);
+  StreamInterface* stream = stream_;
+  stream_ = nullptr;
+  return stream;
+}
+
+StreamAdapterInterface::~StreamAdapterInterface() {
+  if (owned_)
+    delete stream_;
+}
+
+void StreamAdapterInterface::OnEvent(StreamInterface* stream,
+                                     int events,
+                                     int err) {
+  SignalEvent(this, events, err);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamTap
+///////////////////////////////////////////////////////////////////////////////
+
+StreamTap::StreamTap(StreamInterface* stream, StreamInterface* tap)
+    : StreamAdapterInterface(stream), tap_(), tap_result_(SR_SUCCESS),
+        tap_error_(0) {
+  AttachTap(tap);
+}
+
+StreamTap::~StreamTap() = default;
+
+void StreamTap::AttachTap(StreamInterface* tap) {
+  tap_.reset(tap);
+}
+
+StreamInterface* StreamTap::DetachTap() {
+  return tap_.release();
+}
+
+StreamResult StreamTap::GetTapResult(int* error) {
+  if (error) {
+    *error = tap_error_;
+  }
+  return tap_result_;
+}
+
+StreamResult StreamTap::Read(void* buffer, size_t buffer_len,
+                             size_t* read, int* error) {
+  size_t backup_read;
+  if (!read) {
+    read = &backup_read;
+  }
+  StreamResult res = StreamAdapterInterface::Read(buffer, buffer_len,
+                                                  read, error);
+  if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) {
+    tap_result_ = tap_->WriteAll(buffer, *read, nullptr, &tap_error_);
+  }
+  return res;
+}
+
+StreamResult StreamTap::Write(const void* data, size_t data_len,
+                              size_t* written, int* error) {
+  size_t backup_written;
+  if (!written) {
+    written = &backup_written;
+  }
+  StreamResult res = StreamAdapterInterface::Write(data, data_len,
+                                                   written, error);
+  if ((res == SR_SUCCESS) && (tap_result_ == SR_SUCCESS)) {
+    tap_result_ = tap_->WriteAll(data, *written, nullptr, &tap_error_);
+  }
+  return res;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// NullStream
+///////////////////////////////////////////////////////////////////////////////
+
+NullStream::NullStream() {
+}
+
+NullStream::~NullStream() {
+}
+
+StreamState NullStream::GetState() const {
+  return SS_OPEN;
+}
+
+StreamResult NullStream::Read(void* buffer, size_t buffer_len,
+                              size_t* read, int* error) {
+  if (error) *error = -1;
+  return SR_ERROR;
+}
+
+StreamResult NullStream::Write(const void* data, size_t data_len,
+                               size_t* written, int* error) {
+  if (written) *written = data_len;
+  return SR_SUCCESS;
+}
+
+void NullStream::Close() {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FileStream
+///////////////////////////////////////////////////////////////////////////////
+
+FileStream::FileStream() : file_(nullptr) {}
+
+FileStream::~FileStream() {
+  FileStream::Close();
+}
+
+bool FileStream::Open(const std::string& filename, const char* mode,
+                      int* error) {
+  Close();
+#if defined(WEBRTC_WIN)
+  std::wstring wfilename;
+  if (Utf8ToWindowsFilename(filename, &wfilename)) {
+    file_ = _wfopen(wfilename.c_str(), ToUtf16(mode).c_str());
+  } else {
+    if (error) {
+      *error = -1;
+      return false;
+    }
+  }
+#else
+  file_ = fopen(filename.c_str(), mode);
+#endif
+  if (!file_ && error) {
+    *error = errno;
+  }
+  return (file_ != nullptr);
+}
+
+bool FileStream::OpenShare(const std::string& filename, const char* mode,
+                           int shflag, int* error) {
+  Close();
+#if defined(WEBRTC_WIN)
+  std::wstring wfilename;
+  if (Utf8ToWindowsFilename(filename, &wfilename)) {
+    file_ = _wfsopen(wfilename.c_str(), ToUtf16(mode).c_str(), shflag);
+    if (!file_ && error) {
+      *error = errno;
+      return false;
+    }
+    return file_ != nullptr;
+  } else {
+    if (error) {
+      *error = -1;
+    }
+    return false;
+  }
+#else
+  return Open(filename, mode, error);
+#endif
+}
+
+bool FileStream::DisableBuffering() {
+  if (!file_)
+    return false;
+  return (setvbuf(file_, nullptr, _IONBF, 0) == 0);
+}
+
+StreamState FileStream::GetState() const {
+  return (file_ == nullptr) ? SS_CLOSED : SS_OPEN;
+}
+
+StreamResult FileStream::Read(void* buffer, size_t buffer_len,
+                              size_t* read, int* error) {
+  if (!file_)
+    return SR_EOS;
+  size_t result = fread(buffer, 1, buffer_len, file_);
+  if ((result == 0) && (buffer_len > 0)) {
+    if (feof(file_))
+      return SR_EOS;
+    if (error)
+      *error = errno;
+    return SR_ERROR;
+  }
+  if (read)
+    *read = result;
+  return SR_SUCCESS;
+}
+
+StreamResult FileStream::Write(const void* data, size_t data_len,
+                               size_t* written, int* error) {
+  if (!file_)
+    return SR_EOS;
+  size_t result = fwrite(data, 1, data_len, file_);
+  if ((result == 0) && (data_len > 0)) {
+    if (error)
+      *error = errno;
+    return SR_ERROR;
+  }
+  if (written)
+    *written = result;
+  return SR_SUCCESS;
+}
+
+void FileStream::Close() {
+  if (file_) {
+    DoClose();
+    file_ = nullptr;
+  }
+}
+
+bool FileStream::SetPosition(size_t position) {
+  if (!file_)
+    return false;
+  return (fseek(file_, static_cast<int>(position), SEEK_SET) == 0);
+}
+
+bool FileStream::GetPosition(size_t* position) const {
+  RTC_DCHECK(nullptr != position);
+  if (!file_)
+    return false;
+  long result = ftell(file_);
+  if (result < 0)
+    return false;
+  if (position)
+    *position = result;
+  return true;
+}
+
+bool FileStream::GetSize(size_t* size) const {
+  RTC_DCHECK(nullptr != size);
+  if (!file_)
+    return false;
+  struct stat file_stats;
+  if (fstat(fileno(file_), &file_stats) != 0)
+    return false;
+  if (size)
+    *size = file_stats.st_size;
+  return true;
+}
+
+bool FileStream::GetAvailable(size_t* size) const {
+  RTC_DCHECK(nullptr != size);
+  if (!GetSize(size))
+    return false;
+  long result = ftell(file_);
+  if (result < 0)
+    return false;
+  if (size)
+    *size -= result;
+  return true;
+}
+
+bool FileStream::ReserveSize(size_t size) {
+  // TODO: extend the file to the proper length
+  return true;
+}
+
+bool FileStream::GetSize(const std::string& filename, size_t* size) {
+  struct stat file_stats;
+  if (stat(filename.c_str(), &file_stats) != 0)
+    return false;
+  *size = file_stats.st_size;
+  return true;
+}
+
+bool FileStream::Flush() {
+  if (file_) {
+    return (0 == fflush(file_));
+  }
+  // try to flush empty file?
+  RTC_NOTREACHED();
+  return false;
+}
+
+#if defined(WEBRTC_POSIX) && !defined(__native_client__)
+
+bool FileStream::TryLock() {
+  if (file_ == nullptr) {
+    // Stream not open.
+    RTC_NOTREACHED();
+    return false;
+  }
+
+  return flock(fileno(file_), LOCK_EX|LOCK_NB) == 0;
+}
+
+bool FileStream::Unlock() {
+  if (file_ == nullptr) {
+    // Stream not open.
+    RTC_NOTREACHED();
+    return false;
+  }
+
+  return flock(fileno(file_), LOCK_UN) == 0;
+}
+
+#endif
+
+void FileStream::DoClose() {
+  fclose(file_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryStream
+///////////////////////////////////////////////////////////////////////////////
+
+MemoryStreamBase::MemoryStreamBase()
+    : buffer_(nullptr), buffer_length_(0), data_length_(0), seek_position_(0) {}
+
+StreamState MemoryStreamBase::GetState() const {
+  return SS_OPEN;
+}
+
+StreamResult MemoryStreamBase::Read(void* buffer, size_t bytes,
+                                    size_t* bytes_read, int* error) {
+  if (seek_position_ >= data_length_) {
+    return SR_EOS;
+  }
+  size_t available = data_length_ - seek_position_;
+  if (bytes > available) {
+    // Read partial buffer
+    bytes = available;
+  }
+  memcpy(buffer, &buffer_[seek_position_], bytes);
+  seek_position_ += bytes;
+  if (bytes_read) {
+    *bytes_read = bytes;
+  }
+  return SR_SUCCESS;
+}
+
+StreamResult MemoryStreamBase::Write(const void* buffer, size_t bytes,
+                                     size_t* bytes_written, int* error) {
+  size_t available = buffer_length_ - seek_position_;
+  if (0 == available) {
+    // Increase buffer size to the larger of:
+    // a) new position rounded up to next 256 bytes
+    // b) double the previous length
+    size_t new_buffer_length =
+        std::max(((seek_position_ + bytes) | 0xFF) + 1, buffer_length_ * 2);
+    StreamResult result = DoReserve(new_buffer_length, error);
+    if (SR_SUCCESS != result) {
+      return result;
+    }
+    RTC_DCHECK(buffer_length_ >= new_buffer_length);
+    available = buffer_length_ - seek_position_;
+  }
+
+  if (bytes > available) {
+    bytes = available;
+  }
+  memcpy(&buffer_[seek_position_], buffer, bytes);
+  seek_position_ += bytes;
+  if (data_length_ < seek_position_) {
+    data_length_ = seek_position_;
+  }
+  if (bytes_written) {
+    *bytes_written = bytes;
+  }
+  return SR_SUCCESS;
+}
+
+void MemoryStreamBase::Close() {
+  // nothing to do
+}
+
+bool MemoryStreamBase::SetPosition(size_t position) {
+  if (position > data_length_)
+    return false;
+  seek_position_ = position;
+  return true;
+}
+
+bool MemoryStreamBase::GetPosition(size_t* position) const {
+  if (position)
+    *position = seek_position_;
+  return true;
+}
+
+bool MemoryStreamBase::GetSize(size_t* size) const {
+  if (size)
+    *size = data_length_;
+  return true;
+}
+
+bool MemoryStreamBase::GetAvailable(size_t* size) const {
+  if (size)
+    *size = data_length_ - seek_position_;
+  return true;
+}
+
+bool MemoryStreamBase::ReserveSize(size_t size) {
+  return (SR_SUCCESS == DoReserve(size, nullptr));
+}
+
+StreamResult MemoryStreamBase::DoReserve(size_t size, int* error) {
+  return (buffer_length_ >= size) ? SR_SUCCESS : SR_EOS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MemoryStream::MemoryStream() : buffer_alloc_(nullptr) {}
+
+MemoryStream::MemoryStream(const char* data) : buffer_alloc_(nullptr) {
+  SetData(data, strlen(data));
+}
+
+MemoryStream::MemoryStream(const void* data, size_t length)
+    : buffer_alloc_(nullptr) {
+  SetData(data, length);
+}
+
+MemoryStream::~MemoryStream() {
+  delete [] buffer_alloc_;
+}
+
+void MemoryStream::SetData(const void* data, size_t length) {
+  data_length_ = buffer_length_ = length;
+  delete [] buffer_alloc_;
+  buffer_alloc_ = new char[buffer_length_ + kAlignment];
+  buffer_ = reinterpret_cast<char*>(ALIGNP(buffer_alloc_, kAlignment));
+  memcpy(buffer_, data, data_length_);
+  seek_position_ = 0;
+}
+
+StreamResult MemoryStream::DoReserve(size_t size, int* error) {
+  if (buffer_length_ >= size)
+    return SR_SUCCESS;
+
+  if (char* new_buffer_alloc = new char[size + kAlignment]) {
+    char* new_buffer = reinterpret_cast<char*>(
+        ALIGNP(new_buffer_alloc, kAlignment));
+    memcpy(new_buffer, buffer_, data_length_);
+    delete [] buffer_alloc_;
+    buffer_alloc_ = new_buffer_alloc;
+    buffer_ = new_buffer;
+    buffer_length_ = size;
+    return SR_SUCCESS;
+  }
+
+  if (error) {
+    *error = ENOMEM;
+  }
+  return SR_ERROR;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ExternalMemoryStream::ExternalMemoryStream() {
+}
+
+ExternalMemoryStream::ExternalMemoryStream(void* data, size_t length) {
+  SetData(data, length);
+}
+
+ExternalMemoryStream::~ExternalMemoryStream() {
+}
+
+void ExternalMemoryStream::SetData(void* data, size_t length) {
+  data_length_ = buffer_length_ = length;
+  buffer_ = static_cast<char*>(data);
+  seek_position_ = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FifoBuffer
+///////////////////////////////////////////////////////////////////////////////
+
+FifoBuffer::FifoBuffer(size_t size)
+    : state_(SS_OPEN), buffer_(new char[size]), buffer_length_(size),
+      data_length_(0), read_position_(0), owner_(Thread::Current()) {
+  // all events are done on the owner_ thread
+}
+
+FifoBuffer::FifoBuffer(size_t size, Thread* owner)
+    : state_(SS_OPEN), buffer_(new char[size]), buffer_length_(size),
+      data_length_(0), read_position_(0), owner_(owner) {
+  // all events are done on the owner_ thread
+}
+
+FifoBuffer::~FifoBuffer() {
+}
+
+bool FifoBuffer::GetBuffered(size_t* size) const {
+  CritScope cs(&crit_);
+  *size = data_length_;
+  return true;
+}
+
+bool FifoBuffer::SetCapacity(size_t size) {
+  CritScope cs(&crit_);
+  if (data_length_ > size) {
+    return false;
+  }
+
+  if (size != buffer_length_) {
+    char* buffer = new char[size];
+    const size_t copy = data_length_;
+    const size_t tail_copy = std::min(copy, buffer_length_ - read_position_);
+    memcpy(buffer, &buffer_[read_position_], tail_copy);
+    memcpy(buffer + tail_copy, &buffer_[0], copy - tail_copy);
+    buffer_.reset(buffer);
+    read_position_ = 0;
+    buffer_length_ = size;
+  }
+  return true;
+}
+
+StreamResult FifoBuffer::ReadOffset(void* buffer, size_t bytes,
+                                    size_t offset, size_t* bytes_read) {
+  CritScope cs(&crit_);
+  return ReadOffsetLocked(buffer, bytes, offset, bytes_read);
+}
+
+StreamResult FifoBuffer::WriteOffset(const void* buffer, size_t bytes,
+                                     size_t offset, size_t* bytes_written) {
+  CritScope cs(&crit_);
+  return WriteOffsetLocked(buffer, bytes, offset, bytes_written);
+}
+
+StreamState FifoBuffer::GetState() const {
+  CritScope cs(&crit_);
+  return state_;
+}
+
+StreamResult FifoBuffer::Read(void* buffer, size_t bytes,
+                              size_t* bytes_read, int* error) {
+  CritScope cs(&crit_);
+  const bool was_writable = data_length_ < buffer_length_;
+  size_t copy = 0;
+  StreamResult result = ReadOffsetLocked(buffer, bytes, 0, &copy);
+
+  if (result == SR_SUCCESS) {
+    // If read was successful then adjust the read position and number of
+    // bytes buffered.
+    read_position_ = (read_position_ + copy) % buffer_length_;
+    data_length_ -= copy;
+    if (bytes_read) {
+      *bytes_read = copy;
+    }
+
+    // if we were full before, and now we're not, post an event
+    if (!was_writable && copy > 0) {
+      PostEvent(owner_, SE_WRITE, 0);
+    }
+  }
+  return result;
+}
+
+StreamResult FifoBuffer::Write(const void* buffer, size_t bytes,
+                               size_t* bytes_written, int* error) {
+  CritScope cs(&crit_);
+
+  const bool was_readable = (data_length_ > 0);
+  size_t copy = 0;
+  StreamResult result = WriteOffsetLocked(buffer, bytes, 0, &copy);
+
+  if (result == SR_SUCCESS) {
+    // If write was successful then adjust the number of readable bytes.
+    data_length_ += copy;
+    if (bytes_written) {
+      *bytes_written = copy;
+    }
+
+    // if we didn't have any data to read before, and now we do, post an event
+    if (!was_readable && copy > 0) {
+      PostEvent(owner_, SE_READ, 0);
+    }
+  }
+  return result;
+}
+
+void FifoBuffer::Close() {
+  CritScope cs(&crit_);
+  state_ = SS_CLOSED;
+}
+
+const void* FifoBuffer::GetReadData(size_t* size) {
+  CritScope cs(&crit_);
+  *size = (read_position_ + data_length_ <= buffer_length_) ?
+      data_length_ : buffer_length_ - read_position_;
+  return &buffer_[read_position_];
+}
+
+void FifoBuffer::ConsumeReadData(size_t size) {
+  CritScope cs(&crit_);
+  RTC_DCHECK(size <= data_length_);
+  const bool was_writable = data_length_ < buffer_length_;
+  read_position_ = (read_position_ + size) % buffer_length_;
+  data_length_ -= size;
+  if (!was_writable && size > 0) {
+    PostEvent(owner_, SE_WRITE, 0);
+  }
+}
+
+void* FifoBuffer::GetWriteBuffer(size_t* size) {
+  CritScope cs(&crit_);
+  if (state_ == SS_CLOSED) {
+    return nullptr;
+  }
+
+  // if empty, reset the write position to the beginning, so we can get
+  // the biggest possible block
+  if (data_length_ == 0) {
+    read_position_ = 0;
+  }
+
+  const size_t write_position = (read_position_ + data_length_)
+      % buffer_length_;
+  *size = (write_position > read_position_ || data_length_ == 0) ?
+      buffer_length_ - write_position : read_position_ - write_position;
+  return &buffer_[write_position];
+}
+
+void FifoBuffer::ConsumeWriteBuffer(size_t size) {
+  CritScope cs(&crit_);
+  RTC_DCHECK(size <= buffer_length_ - data_length_);
+  const bool was_readable = (data_length_ > 0);
+  data_length_ += size;
+  if (!was_readable && size > 0) {
+    PostEvent(owner_, SE_READ, 0);
+  }
+}
+
+bool FifoBuffer::GetWriteRemaining(size_t* size) const {
+  CritScope cs(&crit_);
+  *size = buffer_length_ - data_length_;
+  return true;
+}
+
+StreamResult FifoBuffer::ReadOffsetLocked(void* buffer,
+                                          size_t bytes,
+                                          size_t offset,
+                                          size_t* bytes_read) {
+  if (offset >= data_length_) {
+    return (state_ != SS_CLOSED) ? SR_BLOCK : SR_EOS;
+  }
+
+  const size_t available = data_length_ - offset;
+  const size_t read_position = (read_position_ + offset) % buffer_length_;
+  const size_t copy = std::min(bytes, available);
+  const size_t tail_copy = std::min(copy, buffer_length_ - read_position);
+  char* const p = static_cast<char*>(buffer);
+  memcpy(p, &buffer_[read_position], tail_copy);
+  memcpy(p + tail_copy, &buffer_[0], copy - tail_copy);
+
+  if (bytes_read) {
+    *bytes_read = copy;
+  }
+  return SR_SUCCESS;
+}
+
+StreamResult FifoBuffer::WriteOffsetLocked(const void* buffer,
+                                           size_t bytes,
+                                           size_t offset,
+                                           size_t* bytes_written) {
+  if (state_ == SS_CLOSED) {
+    return SR_EOS;
+  }
+
+  if (data_length_ + offset >= buffer_length_) {
+    return SR_BLOCK;
+  }
+
+  const size_t available = buffer_length_ - data_length_ - offset;
+  const size_t write_position = (read_position_ + data_length_ + offset)
+      % buffer_length_;
+  const size_t copy = std::min(bytes, available);
+  const size_t tail_copy = std::min(copy, buffer_length_ - write_position);
+  const char* const p = static_cast<const char*>(buffer);
+  memcpy(&buffer_[write_position], p, tail_copy);
+  memcpy(&buffer_[0], p + tail_copy, copy - tail_copy);
+
+  if (bytes_written) {
+    *bytes_written = copy;
+  }
+  return SR_SUCCESS;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// LoggingAdapter
+///////////////////////////////////////////////////////////////////////////////
+
+LoggingAdapter::LoggingAdapter(StreamInterface* stream, LoggingSeverity level,
+                               const std::string& label, bool hex_mode)
+    : StreamAdapterInterface(stream), level_(level), hex_mode_(hex_mode) {
+  set_label(label);
+}
+
+void LoggingAdapter::set_label(const std::string& label) {
+  label_.assign("[");
+  label_.append(label);
+  label_.append("]");
+}
+
+StreamResult LoggingAdapter::Read(void* buffer, size_t buffer_len,
+                                  size_t* read, int* error) {
+  size_t local_read; if (!read) read = &local_read;
+  StreamResult result = StreamAdapterInterface::Read(buffer, buffer_len, read,
+                                                     error);
+  if (result == SR_SUCCESS) {
+    LogMultiline(level_, label_.c_str(), true, buffer, *read, hex_mode_, &lms_);
+  }
+  return result;
+}
+
+StreamResult LoggingAdapter::Write(const void* data, size_t data_len,
+                                   size_t* written, int* error) {
+  size_t local_written;
+  if (!written) written = &local_written;
+  StreamResult result = StreamAdapterInterface::Write(data, data_len, written,
+                                                      error);
+  if (result == SR_SUCCESS) {
+    LogMultiline(level_, label_.c_str(), false, data, *written, hex_mode_,
+                 &lms_);
+  }
+  return result;
+}
+
+void LoggingAdapter::Close() {
+  LogMultiline(level_, label_.c_str(), false, nullptr, 0, hex_mode_, &lms_);
+  LogMultiline(level_, label_.c_str(), true, nullptr, 0, hex_mode_, &lms_);
+  LOG_V(level_) << label_ << " Closed locally";
+  StreamAdapterInterface::Close();
+}
+
+void LoggingAdapter::OnEvent(StreamInterface* stream, int events, int err) {
+  if (events & SE_OPEN) {
+    LOG_V(level_) << label_ << " Open";
+  } else if (events & SE_CLOSE) {
+    LogMultiline(level_, label_.c_str(), false, nullptr, 0, hex_mode_, &lms_);
+    LogMultiline(level_, label_.c_str(), true, nullptr, 0, hex_mode_, &lms_);
+    LOG_V(level_) << label_ << " Closed with error: " << err;
+  }
+  StreamAdapterInterface::OnEvent(stream, events, err);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream - Reads/Writes to an external std::string
+///////////////////////////////////////////////////////////////////////////////
+
+StringStream::StringStream(std::string* str)
+    : str_(*str), read_pos_(0), read_only_(false) {
+}
+
+StringStream::StringStream(const std::string& str)
+    : str_(const_cast<std::string&>(str)), read_pos_(0), read_only_(true) {
+}
+
+StreamState StringStream::GetState() const {
+  return SS_OPEN;
+}
+
+StreamResult StringStream::Read(void* buffer, size_t buffer_len,
+                                      size_t* read, int* error) {
+  size_t available = std::min(buffer_len, str_.size() - read_pos_);
+  if (!available)
+    return SR_EOS;
+  memcpy(buffer, str_.data() + read_pos_, available);
+  read_pos_ += available;
+  if (read)
+    *read = available;
+  return SR_SUCCESS;
+}
+
+StreamResult StringStream::Write(const void* data, size_t data_len,
+                                      size_t* written, int* error) {
+  if (read_only_) {
+    if (error) {
+      *error = -1;
+    }
+    return SR_ERROR;
+  }
+  str_.append(static_cast<const char*>(data),
+              static_cast<const char*>(data) + data_len);
+  if (written)
+    *written = data_len;
+  return SR_SUCCESS;
+}
+
+void StringStream::Close() {
+}
+
+bool StringStream::SetPosition(size_t position) {
+  if (position > str_.size())
+    return false;
+  read_pos_ = position;
+  return true;
+}
+
+bool StringStream::GetPosition(size_t* position) const {
+  if (position)
+    *position = read_pos_;
+  return true;
+}
+
+bool StringStream::GetSize(size_t* size) const {
+  if (size)
+    *size = str_.size();
+  return true;
+}
+
+bool StringStream::GetAvailable(size_t* size) const {
+  if (size)
+    *size = str_.size() - read_pos_;
+  return true;
+}
+
+bool StringStream::ReserveSize(size_t size) {
+  if (read_only_)
+    return false;
+  str_.reserve(size);
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamReference
+///////////////////////////////////////////////////////////////////////////////
+
+StreamReference::StreamReference(StreamInterface* stream)
+    : StreamAdapterInterface(stream, false) {
+  // owner set to false so the destructor does not free the stream.
+  stream_ref_count_ = new StreamRefCount(stream);
+}
+
+StreamInterface* StreamReference::NewReference() {
+  stream_ref_count_->AddReference();
+  return new StreamReference(stream_ref_count_, stream());
+}
+
+StreamReference::~StreamReference() {
+  stream_ref_count_->Release();
+}
+
+StreamReference::StreamReference(StreamRefCount* stream_ref_count,
+                                 StreamInterface* stream)
+    : StreamAdapterInterface(stream, false),
+      stream_ref_count_(stream_ref_count) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+StreamResult Flow(StreamInterface* source,
+                  char* buffer,
+                  size_t buffer_len,
+                  StreamInterface* sink,
+                  size_t* data_len /* = nullptr */) {
+  RTC_DCHECK(buffer_len > 0);
+
+  StreamResult result;
+  size_t count, read_pos, write_pos;
+  if (data_len) {
+    read_pos = *data_len;
+  } else {
+    read_pos = 0;
+  }
+
+  bool end_of_stream = false;
+  do {
+    // Read until buffer is full, end of stream, or error
+    while (!end_of_stream && (read_pos < buffer_len)) {
+      result = source->Read(buffer + read_pos, buffer_len - read_pos, &count,
+                            nullptr);
+      if (result == SR_EOS) {
+        end_of_stream = true;
+      } else if (result != SR_SUCCESS) {
+        if (data_len) {
+          *data_len = read_pos;
+        }
+        return result;
+      } else {
+        read_pos += count;
+      }
+    }
+
+    // Write until buffer is empty, or error (including end of stream)
+    write_pos = 0;
+    while (write_pos < read_pos) {
+      result = sink->Write(buffer + write_pos, read_pos - write_pos, &count,
+                           nullptr);
+      if (result != SR_SUCCESS) {
+        if (data_len) {
+          *data_len = read_pos - write_pos;
+          if (write_pos > 0) {
+            memmove(buffer, buffer + write_pos, *data_len);
+          }
+        }
+        return result;
+      }
+      write_pos += count;
+    }
+
+    read_pos = 0;
+  } while (!end_of_stream);
+
+  if (data_len) {
+    *data_len = 0;
+  }
+  return SR_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
diff --git a/base/stream.h b/base/stream.h
index 18dd865..dbc2ad7 100644
--- a/base/stream.h
+++ b/base/stream.h
@@ -11,9 +11,705 @@
 #ifndef WEBRTC_BASE_STREAM_H_
 #define WEBRTC_BASE_STREAM_H_
 
+#include <stdio.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/stream.h"
+#include <memory>
+
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/messagehandler.h"
+#include "webrtc/base/messagequeue.h"
+#include "webrtc/base/sigslot.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamInterface is a generic asynchronous stream interface, supporting read,
+// write, and close operations, and asynchronous signalling of state changes.
+// The interface is designed with file, memory, and socket implementations in
+// mind.  Some implementations offer extended operations, such as seeking.
+///////////////////////////////////////////////////////////////////////////////
+
+// The following enumerations are declared outside of the StreamInterface
+// class for brevity in use.
+
+// The SS_OPENING state indicates that the stream will signal open or closed
+// in the future.
+enum StreamState { SS_CLOSED, SS_OPENING, SS_OPEN };
+
+// Stream read/write methods return this value to indicate various success
+// and failure conditions described below.
+enum StreamResult { SR_ERROR, SR_SUCCESS, SR_BLOCK, SR_EOS };
+
+// StreamEvents are used to asynchronously signal state transitionss.  The flags
+// may be combined.
+//  SE_OPEN: The stream has transitioned to the SS_OPEN state
+//  SE_CLOSE: The stream has transitioned to the SS_CLOSED state
+//  SE_READ: Data is available, so Read is likely to not return SR_BLOCK
+//  SE_WRITE: Data can be written, so Write is likely to not return SR_BLOCK
+enum StreamEvent { SE_OPEN = 1, SE_READ = 2, SE_WRITE = 4, SE_CLOSE = 8 };
+
+class Thread;
+
+struct StreamEventData : public MessageData {
+  int events, error;
+  StreamEventData(int ev, int er) : events(ev), error(er) { }
+};
+
+class StreamInterface : public MessageHandler {
+ public:
+  enum {
+    MSG_POST_EVENT = 0xF1F1, MSG_MAX = MSG_POST_EVENT
+  };
+
+  ~StreamInterface() override;
+
+  virtual StreamState GetState() const = 0;
+
+  // Read attempts to fill buffer of size buffer_len.  Write attempts to send
+  // data_len bytes stored in data.  The variables read and write are set only
+  // on SR_SUCCESS (see below).  Likewise, error is only set on SR_ERROR.
+  // Read and Write return a value indicating:
+  //  SR_ERROR: an error occurred, which is returned in a non-null error
+  //    argument.  Interpretation of the error requires knowledge of the
+  //    stream's concrete type, which limits its usefulness.
+  //  SR_SUCCESS: some number of bytes were successfully written, which is
+  //    returned in a non-null read/write argument.
+  //  SR_BLOCK: the stream is in non-blocking mode, and the operation would
+  //    block, or the stream is in SS_OPENING state.
+  //  SR_EOS: the end-of-stream has been reached, or the stream is in the
+  //    SS_CLOSED state.
+  virtual StreamResult Read(void* buffer, size_t buffer_len,
+                            size_t* read, int* error) = 0;
+  virtual StreamResult Write(const void* data, size_t data_len,
+                             size_t* written, int* error) = 0;
+  // Attempt to transition to the SS_CLOSED state.  SE_CLOSE will not be
+  // signalled as a result of this call.
+  virtual void Close() = 0;
+
+  // Streams may signal one or more StreamEvents to indicate state changes.
+  // The first argument identifies the stream on which the state change occured.
+  // The second argument is a bit-wise combination of StreamEvents.
+  // If SE_CLOSE is signalled, then the third argument is the associated error
+  // code.  Otherwise, the value is undefined.
+  // Note: Not all streams will support asynchronous event signalling.  However,
+  // SS_OPENING and SR_BLOCK returned from stream member functions imply that
+  // certain events will be raised in the future.
+  sigslot::signal3<StreamInterface*, int, int> SignalEvent;
+
+  // Like calling SignalEvent, but posts a message to the specified thread,
+  // which will call SignalEvent.  This helps unroll the stack and prevent
+  // re-entrancy.
+  void PostEvent(Thread* t, int events, int err);
+  // Like the aforementioned method, but posts to the current thread.
+  void PostEvent(int events, int err);
+
+  //
+  // OPTIONAL OPERATIONS
+  //
+  // Not all implementations will support the following operations.  In general,
+  // a stream will only support an operation if it reasonably efficient to do
+  // so.  For example, while a socket could buffer incoming data to support
+  // seeking, it will not do so.  Instead, a buffering stream adapter should
+  // be used.
+  //
+  // Even though several of these operations are related, you should
+  // always use whichever operation is most relevant.  For example, you may
+  // be tempted to use GetSize() and GetPosition() to deduce the result of
+  // GetAvailable().  However, a stream which is read-once may support the
+  // latter operation but not the former.
+  //
+
+  // The following four methods are used to avoid copying data multiple times.
+
+  // GetReadData returns a pointer to a buffer which is owned by the stream.
+  // The buffer contains data_len bytes.  null is returned if no data is
+  // available, or if the method fails.  If the caller processes the data, it
+  // must call ConsumeReadData with the number of processed bytes.  GetReadData
+  // does not require a matching call to ConsumeReadData if the data is not
+  // processed.  Read and ConsumeReadData invalidate the buffer returned by
+  // GetReadData.
+  virtual const void* GetReadData(size_t* data_len);
+  virtual void ConsumeReadData(size_t used) {}
+
+  // GetWriteBuffer returns a pointer to a buffer which is owned by the stream.
+  // The buffer has a capacity of buf_len bytes.  null is returned if there is
+  // no buffer available, or if the method fails.  The call may write data to
+  // the buffer, and then call ConsumeWriteBuffer with the number of bytes
+  // written.  GetWriteBuffer does not require a matching call to
+  // ConsumeWriteData if no data is written.  Write, ForceWrite, and
+  // ConsumeWriteData invalidate the buffer returned by GetWriteBuffer.
+  // TODO: Allow the caller to specify a minimum buffer size.  If the specified
+  // amount of buffer is not yet available, return null and Signal SE_WRITE
+  // when it is available.  If the requested amount is too large, return an
+  // error.
+  virtual void* GetWriteBuffer(size_t* buf_len);
+  virtual void ConsumeWriteBuffer(size_t used) {}
+
+  // Write data_len bytes found in data, circumventing any throttling which
+  // would could cause SR_BLOCK to be returned.  Returns true if all the data
+  // was written.  Otherwise, the method is unsupported, or an unrecoverable
+  // error occurred, and the error value is set.  This method should be used
+  // sparingly to write critical data which should not be throttled.  A stream
+  // which cannot circumvent its blocking constraints should not implement this
+  // method.
+  // NOTE: This interface is being considered experimentally at the moment.  It
+  // would be used by JUDP and BandwidthStream as a way to circumvent certain
+  // soft limits in writing.
+  //virtual bool ForceWrite(const void* data, size_t data_len, int* error) {
+  //  if (error) *error = -1;
+  //  return false;
+  //}
+
+  // Seek to a byte offset from the beginning of the stream.  Returns false if
+  // the stream does not support seeking, or cannot seek to the specified
+  // position.
+  virtual bool SetPosition(size_t position);
+
+  // Get the byte offset of the current position from the start of the stream.
+  // Returns false if the position is not known.
+  virtual bool GetPosition(size_t* position) const;
+
+  // Get the byte length of the entire stream.  Returns false if the length
+  // is not known.
+  virtual bool GetSize(size_t* size) const;
+
+  // Return the number of Read()-able bytes remaining before end-of-stream.
+  // Returns false if not known.
+  virtual bool GetAvailable(size_t* size) const;
+
+  // Return the number of Write()-able bytes remaining before end-of-stream.
+  // Returns false if not known.
+  virtual bool GetWriteRemaining(size_t* size) const;
+
+  // Return true if flush is successful.
+  virtual bool Flush();
+
+  // Communicates the amount of data which will be written to the stream.  The
+  // stream may choose to preallocate memory to accomodate this data.  The
+  // stream may return false to indicate that there is not enough room (ie,
+  // Write will return SR_EOS/SR_ERROR at some point).  Note that calling this
+  // function should not affect the existing state of data in the stream.
+  virtual bool ReserveSize(size_t size);
+
+  //
+  // CONVENIENCE METHODS
+  //
+  // These methods are implemented in terms of other methods, for convenience.
+  //
+
+  // Seek to the start of the stream.
+  inline bool Rewind() { return SetPosition(0); }
+
+  // WriteAll is a helper function which repeatedly calls Write until all the
+  // data is written, or something other than SR_SUCCESS is returned.  Note that
+  // unlike Write, the argument 'written' is always set, and may be non-zero
+  // on results other than SR_SUCCESS.  The remaining arguments have the
+  // same semantics as Write.
+  StreamResult WriteAll(const void* data, size_t data_len,
+                        size_t* written, int* error);
+
+  // Similar to ReadAll.  Calls Read until buffer_len bytes have been read, or
+  // until a non-SR_SUCCESS result is returned.  'read' is always set.
+  StreamResult ReadAll(void* buffer, size_t buffer_len,
+                       size_t* read, int* error);
+
+  // ReadLine is a helper function which repeatedly calls Read until it hits
+  // the end-of-line character, or something other than SR_SUCCESS.
+  // TODO: this is too inefficient to keep here.  Break this out into a buffered
+  // readline object or adapter
+  StreamResult ReadLine(std::string* line);
+
+ protected:
+  StreamInterface();
+
+  // MessageHandler Interface
+  void OnMessage(Message* msg) override;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(StreamInterface);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamAdapterInterface is a convenient base-class for adapting a stream.
+// By default, all operations are pass-through.  Override the methods that you
+// require adaptation.  Streams should really be upgraded to reference-counted.
+// In the meantime, use the owned flag to indicate whether the adapter should
+// own the adapted stream.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamAdapterInterface : public StreamInterface,
+                               public sigslot::has_slots<> {
+ public:
+  explicit StreamAdapterInterface(StreamInterface* stream, bool owned = true);
+
+  // Core Stream Interface
+  StreamState GetState() const override;
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override;
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+  void Close() override;
+
+  // Optional Stream Interface
+  /*  Note: Many stream adapters were implemented prior to this Read/Write
+      interface.  Therefore, a simple pass through of data in those cases may
+      be broken.  At a later time, we should do a once-over pass of all
+      adapters, and make them compliant with these interfaces, after which this
+      code can be uncommented.
+  virtual const void* GetReadData(size_t* data_len) {
+    return stream_->GetReadData(data_len);
+  }
+  virtual void ConsumeReadData(size_t used) {
+    stream_->ConsumeReadData(used);
+  }
+
+  virtual void* GetWriteBuffer(size_t* buf_len) {
+    return stream_->GetWriteBuffer(buf_len);
+  }
+  virtual void ConsumeWriteBuffer(size_t used) {
+    stream_->ConsumeWriteBuffer(used);
+  }
+  */
+
+  /*  Note: This interface is currently undergoing evaluation.
+  virtual bool ForceWrite(const void* data, size_t data_len, int* error) {
+    return stream_->ForceWrite(data, data_len, error);
+  }
+  */
+
+  bool SetPosition(size_t position) override;
+  bool GetPosition(size_t* position) const override;
+  bool GetSize(size_t* size) const override;
+  bool GetAvailable(size_t* size) const override;
+  bool GetWriteRemaining(size_t* size) const override;
+  bool ReserveSize(size_t size) override;
+  bool Flush() override;
+
+  void Attach(StreamInterface* stream, bool owned = true);
+  StreamInterface* Detach();
+
+ protected:
+  ~StreamAdapterInterface() override;
+
+  // Note that the adapter presents itself as the origin of the stream events,
+  // since users of the adapter may not recognize the adapted object.
+  virtual void OnEvent(StreamInterface* stream, int events, int err);
+  StreamInterface* stream() { return stream_; }
+
+ private:
+  StreamInterface* stream_;
+  bool owned_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(StreamAdapterInterface);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamTap is a non-modifying, pass-through adapter, which copies all data
+// in either direction to the tap.  Note that errors or blocking on writing to
+// the tap will prevent further tap writes from occurring.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamTap : public StreamAdapterInterface {
+ public:
+  explicit StreamTap(StreamInterface* stream, StreamInterface* tap);
+  ~StreamTap() override;
+
+  void AttachTap(StreamInterface* tap);
+  StreamInterface* DetachTap();
+  StreamResult GetTapResult(int* error);
+
+  // StreamAdapterInterface Interface
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override;
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+
+ private:
+  std::unique_ptr<StreamInterface> tap_;
+  StreamResult tap_result_;
+  int tap_error_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(StreamTap);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// NullStream gives errors on read, and silently discards all written data.
+///////////////////////////////////////////////////////////////////////////////
+
+class NullStream : public StreamInterface {
+ public:
+  NullStream();
+  ~NullStream() override;
+
+  // StreamInterface Interface
+  StreamState GetState() const override;
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override;
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+  void Close() override;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// FileStream is a simple implementation of a StreamInterface, which does not
+// support asynchronous notification.
+///////////////////////////////////////////////////////////////////////////////
+
+class FileStream : public StreamInterface {
+ public:
+  FileStream();
+  ~FileStream() override;
+
+  // The semantics of filename and mode are the same as stdio's fopen
+  virtual bool Open(const std::string& filename, const char* mode, int* error);
+  virtual bool OpenShare(const std::string& filename, const char* mode,
+                         int shflag, int* error);
+
+  // By default, reads and writes are buffered for efficiency.  Disabling
+  // buffering causes writes to block until the bytes on disk are updated.
+  virtual bool DisableBuffering();
+
+  StreamState GetState() const override;
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override;
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+  void Close() override;
+  bool SetPosition(size_t position) override;
+  bool GetPosition(size_t* position) const override;
+  bool GetSize(size_t* size) const override;
+  bool GetAvailable(size_t* size) const override;
+  bool ReserveSize(size_t size) override;
+
+  bool Flush() override;
+
+#if defined(WEBRTC_POSIX) && !defined(__native_client__)
+  // Tries to aquire an exclusive lock on the file.
+  // Use OpenShare(...) on win32 to get similar functionality.
+  bool TryLock();
+  bool Unlock();
+#endif
+
+  // Note: Deprecated in favor of Filesystem::GetFileSize().
+  static bool GetSize(const std::string& filename, size_t* size);
+
+ protected:
+  virtual void DoClose();
+
+  FILE* file_;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(FileStream);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryStream is a simple implementation of a StreamInterface over in-memory
+// data.  Data is read and written at the current seek position.  Reads return
+// end-of-stream when they reach the end of data.  Writes actually extend the
+// end of data mark.
+///////////////////////////////////////////////////////////////////////////////
+
+class MemoryStreamBase : public StreamInterface {
+ public:
+  StreamState GetState() const override;
+  StreamResult Read(void* buffer,
+                    size_t bytes,
+                    size_t* bytes_read,
+                    int* error) override;
+  StreamResult Write(const void* buffer,
+                     size_t bytes,
+                     size_t* bytes_written,
+                     int* error) override;
+  void Close() override;
+  bool SetPosition(size_t position) override;
+  bool GetPosition(size_t* position) const override;
+  bool GetSize(size_t* size) const override;
+  bool GetAvailable(size_t* size) const override;
+  bool ReserveSize(size_t size) override;
+
+  char* GetBuffer() { return buffer_; }
+  const char* GetBuffer() const { return buffer_; }
+
+ protected:
+  MemoryStreamBase();
+
+  virtual StreamResult DoReserve(size_t size, int* error);
+
+  // Invariant: 0 <= seek_position <= data_length_ <= buffer_length_
+  char* buffer_;
+  size_t buffer_length_;
+  size_t data_length_;
+  size_t seek_position_;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(MemoryStreamBase);
+};
+
+// MemoryStream dynamically resizes to accomodate written data.
+
+class MemoryStream : public MemoryStreamBase {
+ public:
+  MemoryStream();
+  explicit MemoryStream(const char* data);  // Calls SetData(data, strlen(data))
+  MemoryStream(const void* data, size_t length);  // Calls SetData(data, length)
+  ~MemoryStream() override;
+
+  void SetData(const void* data, size_t length);
+
+ protected:
+  StreamResult DoReserve(size_t size, int* error) override;
+  // Memory Streams are aligned for efficiency.
+  static const int kAlignment = 16;
+  char* buffer_alloc_;
+};
+
+// ExternalMemoryStream adapts an external memory buffer, so writes which would
+// extend past the end of the buffer will return end-of-stream.
+
+class ExternalMemoryStream : public MemoryStreamBase {
+ public:
+  ExternalMemoryStream();
+  ExternalMemoryStream(void* data, size_t length);
+  ~ExternalMemoryStream() override;
+
+  void SetData(void* data, size_t length);
+};
+
+// FifoBuffer allows for efficient, thread-safe buffering of data between
+// writer and reader. As the data can wrap around the end of the buffer,
+// MemoryStreamBase can't help us here.
+
+class FifoBuffer : public StreamInterface {
+ public:
+  // Creates a FIFO buffer with the specified capacity.
+  explicit FifoBuffer(size_t length);
+  // Creates a FIFO buffer with the specified capacity and owner
+  FifoBuffer(size_t length, Thread* owner);
+  ~FifoBuffer() override;
+  // Gets the amount of data currently readable from the buffer.
+  bool GetBuffered(size_t* data_len) const;
+  // Resizes the buffer to the specified capacity. Fails if data_length_ > size
+  bool SetCapacity(size_t length);
+
+  // Read into |buffer| with an offset from the current read position, offset
+  // is specified in number of bytes.
+  // This method doesn't adjust read position nor the number of available
+  // bytes, user has to call ConsumeReadData() to do this.
+  StreamResult ReadOffset(void* buffer, size_t bytes, size_t offset,
+                          size_t* bytes_read);
+
+  // Write |buffer| with an offset from the current write position, offset is
+  // specified in number of bytes.
+  // This method doesn't adjust the number of buffered bytes, user has to call
+  // ConsumeWriteBuffer() to do this.
+  StreamResult WriteOffset(const void* buffer, size_t bytes, size_t offset,
+                           size_t* bytes_written);
+
+  // StreamInterface methods
+  StreamState GetState() const override;
+  StreamResult Read(void* buffer,
+                    size_t bytes,
+                    size_t* bytes_read,
+                    int* error) override;
+  StreamResult Write(const void* buffer,
+                     size_t bytes,
+                     size_t* bytes_written,
+                     int* error) override;
+  void Close() override;
+  const void* GetReadData(size_t* data_len) override;
+  void ConsumeReadData(size_t used) override;
+  void* GetWriteBuffer(size_t* buf_len) override;
+  void ConsumeWriteBuffer(size_t used) override;
+  bool GetWriteRemaining(size_t* size) const override;
+
+ private:
+  // Helper method that implements ReadOffset. Caller must acquire a lock
+  // when calling this method.
+  StreamResult ReadOffsetLocked(void* buffer, size_t bytes, size_t offset,
+                                size_t* bytes_read)
+      EXCLUSIVE_LOCKS_REQUIRED(crit_);
+
+  // Helper method that implements WriteOffset. Caller must acquire a lock
+  // when calling this method.
+  StreamResult WriteOffsetLocked(const void* buffer, size_t bytes,
+                                 size_t offset, size_t* bytes_written)
+      EXCLUSIVE_LOCKS_REQUIRED(crit_);
+
+  // keeps the opened/closed state of the stream
+  StreamState state_ GUARDED_BY(crit_);
+  // the allocated buffer
+  std::unique_ptr<char[]> buffer_ GUARDED_BY(crit_);
+  // size of the allocated buffer
+  size_t buffer_length_ GUARDED_BY(crit_);
+  // amount of readable data in the buffer
+  size_t data_length_ GUARDED_BY(crit_);
+  // offset to the readable data
+  size_t read_position_ GUARDED_BY(crit_);
+  // stream callbacks are dispatched on this thread
+  Thread* owner_;
+  // object lock
+  CriticalSection crit_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(FifoBuffer);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class LoggingAdapter : public StreamAdapterInterface {
+ public:
+  LoggingAdapter(StreamInterface* stream, LoggingSeverity level,
+                 const std::string& label, bool hex_mode = false);
+
+  void set_label(const std::string& label);
+
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override;
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+  void Close() override;
+
+ protected:
+  void OnEvent(StreamInterface* stream, int events, int err) override;
+
+ private:
+  LoggingSeverity level_;
+  std::string label_;
+  bool hex_mode_;
+  LogMultilineState lms_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(LoggingAdapter);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream - Reads/Writes to an external std::string
+///////////////////////////////////////////////////////////////////////////////
+
+class StringStream : public StreamInterface {
+ public:
+  explicit StringStream(std::string* str);
+  explicit StringStream(const std::string& str);
+
+  StreamState GetState() const override;
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override;
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+  void Close() override;
+  bool SetPosition(size_t position) override;
+  bool GetPosition(size_t* position) const override;
+  bool GetSize(size_t* size) const override;
+  bool GetAvailable(size_t* size) const override;
+  bool ReserveSize(size_t size) override;
+
+ private:
+  std::string& str_;
+  size_t read_pos_;
+  bool read_only_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamReference - A reference counting stream adapter
+///////////////////////////////////////////////////////////////////////////////
+
+// Keep in mind that the streams and adapters defined in this file are
+// not thread-safe, so this has limited uses.
+
+// A StreamRefCount holds the reference count and a pointer to the
+// wrapped stream. It deletes the wrapped stream when there are no
+// more references. We can then have multiple StreamReference
+// instances pointing to one StreamRefCount, all wrapping the same
+// stream.
+
+class StreamReference : public StreamAdapterInterface {
+  class StreamRefCount;
+ public:
+  // Constructor for the first reference to a stream
+  // Note: get more references through NewReference(). Use this
+  // constructor only once on a given stream.
+  explicit StreamReference(StreamInterface* stream);
+  StreamInterface* GetStream() { return stream(); }
+  StreamInterface* NewReference();
+  ~StreamReference() override;
+
+ private:
+  class StreamRefCount {
+   public:
+    explicit StreamRefCount(StreamInterface* stream)
+        : stream_(stream), ref_count_(1) {
+    }
+    void AddReference() {
+      CritScope lock(&cs_);
+      ++ref_count_;
+    }
+    void Release() {
+      int ref_count;
+      {  // Atomic ops would have been a better fit here.
+        CritScope lock(&cs_);
+        ref_count = --ref_count_;
+      }
+      if (ref_count == 0) {
+        delete stream_;
+        delete this;
+      }
+    }
+   private:
+    StreamInterface* stream_;
+    int ref_count_;
+    CriticalSection cs_;
+    RTC_DISALLOW_COPY_AND_ASSIGN(StreamRefCount);
+  };
+
+  // Constructor for adding references
+  explicit StreamReference(StreamRefCount* stream_ref_count,
+                           StreamInterface* stream);
+
+  StreamRefCount* stream_ref_count_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(StreamReference);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Flow attempts to move bytes from source to sink via buffer of size
+// buffer_len.  The function returns SR_SUCCESS when source reaches
+// end-of-stream (returns SR_EOS), and all the data has been written successful
+// to sink.  Alternately, if source returns SR_BLOCK or SR_ERROR, or if sink
+// returns SR_BLOCK, SR_ERROR, or SR_EOS, then the function immediately returns
+// with the unexpected StreamResult value.
+// data_len is the length of the valid data in buffer. in case of error
+// this is the data that read from source but can't move to destination.
+// as a pass in parameter, it indicates data in buffer that should move to sink
+StreamResult Flow(StreamInterface* source,
+                  char* buffer,
+                  size_t buffer_len,
+                  StreamInterface* sink,
+                  size_t* data_len = nullptr);
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_STREAM_H_
diff --git a/base/stream_unittest.cc b/base/stream_unittest.cc
new file mode 100644
index 0000000..0fbb6ab
--- /dev/null
+++ b/base/stream_unittest.cc
@@ -0,0 +1,374 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/stream.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// TestStream
+///////////////////////////////////////////////////////////////////////////////
+
+class TestStream : public StreamInterface {
+ public:
+  TestStream() : pos_(0) { }
+
+  virtual StreamState GetState() const { return SS_OPEN; }
+  virtual StreamResult Read(void* buffer, size_t buffer_len,
+                            size_t* read, int* error) {
+    unsigned char* uc_buffer = static_cast<unsigned char*>(buffer);
+    for (size_t i = 0; i < buffer_len; ++i) {
+      uc_buffer[i] = static_cast<unsigned char>(pos_++);
+    }
+    if (read)
+      *read = buffer_len;
+    return SR_SUCCESS;
+  }
+  virtual StreamResult Write(const void* data, size_t data_len,
+                             size_t* written, int* error) {
+    if (error)
+      *error = -1;
+    return SR_ERROR;
+  }
+  virtual void Close() { }
+  virtual bool SetPosition(size_t position) {
+    pos_ = position;
+    return true;
+  }
+  virtual bool GetPosition(size_t* position) const {
+    if (position) *position = pos_;
+    return true;
+  }
+  virtual bool GetSize(size_t* size) const {
+    return false;
+  }
+  virtual bool GetAvailable(size_t* size) const {
+    return false;
+  }
+
+ private:
+  size_t pos_;
+};
+
+bool VerifyTestBuffer(unsigned char* buffer, size_t len,
+                      unsigned char value) {
+  bool passed = true;
+  for (size_t i = 0; i < len; ++i) {
+    if (buffer[i] != value++) {
+      passed = false;
+      break;
+    }
+  }
+  // Ensure that we don't pass again without re-writing
+  memset(buffer, 0, len);
+  return passed;
+}
+
+void SeekTest(StreamInterface* stream, const unsigned char value) {
+  size_t bytes;
+  unsigned char buffer[13] = { 0 };
+  const size_t kBufSize = sizeof(buffer);
+
+  EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, nullptr), SR_SUCCESS);
+  EXPECT_EQ(bytes, kBufSize);
+  EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, value));
+  EXPECT_TRUE(stream->GetPosition(&bytes));
+  EXPECT_EQ(13U, bytes);
+
+  EXPECT_TRUE(stream->SetPosition(7));
+
+  EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, nullptr), SR_SUCCESS);
+  EXPECT_EQ(bytes, kBufSize);
+  EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, value + 7));
+  EXPECT_TRUE(stream->GetPosition(&bytes));
+  EXPECT_EQ(20U, bytes);
+}
+
+TEST(FifoBufferTest, TestAll) {
+  const size_t kSize = 16;
+  const char in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
+  char out[kSize * 2];
+  void* p;
+  const void* q;
+  size_t bytes;
+  FifoBuffer buf(kSize);
+  StreamInterface* stream = &buf;
+
+  // Test assumptions about base state
+  EXPECT_EQ(SS_OPEN, stream->GetState());
+  EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, nullptr));
+  EXPECT_TRUE(nullptr != stream->GetReadData(&bytes));
+  EXPECT_EQ((size_t)0, bytes);
+  stream->ConsumeReadData(0);
+  EXPECT_TRUE(nullptr != stream->GetWriteBuffer(&bytes));
+  EXPECT_EQ(kSize, bytes);
+  stream->ConsumeWriteBuffer(0);
+
+  // Try a full write
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, nullptr));
+  EXPECT_EQ(kSize, bytes);
+
+  // Try a write that should block
+  EXPECT_EQ(SR_BLOCK, stream->Write(in, kSize, &bytes, nullptr));
+
+  // Try a full read
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, nullptr));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize));
+
+  // Try a read that should block
+  EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, nullptr));
+
+  // Try a too-big write
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize * 2, &bytes, nullptr));
+  EXPECT_EQ(bytes, kSize);
+
+  // Try a too-big read
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 2, &bytes, nullptr));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize));
+
+  // Try some small writes and reads
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize / 2));
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize / 2));
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize / 2));
+
+  // Try wraparound reads and writes in the following pattern
+  // WWWWWWWWWWWW.... 0123456789AB....
+  // RRRRRRRRXXXX.... ........89AB....
+  // WWWW....XXXXWWWW 4567....89AB0123
+  // XXXX....RRRRXXXX 4567........0123
+  // XXXXWWWWWWWWXXXX 4567012345670123
+  // RRRRXXXXXXXXRRRR ....01234567....
+  // ....RRRRRRRR.... ................
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize * 3 / 4, &bytes, nullptr));
+  EXPECT_EQ(kSize * 3 / 4, bytes);
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize / 2));
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 4, &bytes, nullptr));
+  EXPECT_EQ(kSize / 4 , bytes);
+  EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4));
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2 , bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize / 2));
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(kSize / 2 , bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize / 2));
+
+  // Use GetWriteBuffer to reset the read_position for the next tests
+  stream->GetWriteBuffer(&bytes);
+  stream->ConsumeWriteBuffer(0);
+
+  // Try using GetReadData to do a full read
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, nullptr));
+  q = stream->GetReadData(&bytes);
+  EXPECT_TRUE(nullptr != q);
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(0, memcmp(q, in, kSize));
+  stream->ConsumeReadData(kSize);
+  EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, nullptr));
+
+  // Try using GetReadData to do some small reads
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, nullptr));
+  q = stream->GetReadData(&bytes);
+  EXPECT_TRUE(nullptr != q);
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(0, memcmp(q, in, kSize / 2));
+  stream->ConsumeReadData(kSize / 2);
+  q = stream->GetReadData(&bytes);
+  EXPECT_TRUE(nullptr != q);
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(0, memcmp(q, in + kSize / 2, kSize / 2));
+  stream->ConsumeReadData(kSize / 2);
+  EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, nullptr));
+
+  // Try using GetReadData in a wraparound case
+  // WWWWWWWWWWWWWWWW 0123456789ABCDEF
+  // RRRRRRRRRRRRXXXX ............CDEF
+  // WWWWWWWW....XXXX 01234567....CDEF
+  // ............RRRR 01234567........
+  // RRRRRRRR........ ................
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, nullptr));
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 3 / 4, &bytes, nullptr));
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, nullptr));
+  q = stream->GetReadData(&bytes);
+  EXPECT_TRUE(nullptr != q);
+  EXPECT_EQ(kSize / 4, bytes);
+  EXPECT_EQ(0, memcmp(q, in + kSize * 3 / 4, kSize / 4));
+  stream->ConsumeReadData(kSize / 4);
+  q = stream->GetReadData(&bytes);
+  EXPECT_TRUE(nullptr != q);
+  EXPECT_EQ(kSize / 2, bytes);
+  EXPECT_EQ(0, memcmp(q, in, kSize / 2));
+  stream->ConsumeReadData(kSize / 2);
+
+  // Use GetWriteBuffer to reset the read_position for the next tests
+  stream->GetWriteBuffer(&bytes);
+  stream->ConsumeWriteBuffer(0);
+
+  // Try using GetWriteBuffer to do a full write
+  p = stream->GetWriteBuffer(&bytes);
+  EXPECT_TRUE(nullptr != p);
+  EXPECT_EQ(kSize, bytes);
+  memcpy(p, in, kSize);
+  stream->ConsumeWriteBuffer(kSize);
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, nullptr));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize));
+
+  // Try using GetWriteBuffer to do some small writes
+  p = stream->GetWriteBuffer(&bytes);
+  EXPECT_TRUE(nullptr != p);
+  EXPECT_EQ(kSize, bytes);
+  memcpy(p, in, kSize / 2);
+  stream->ConsumeWriteBuffer(kSize / 2);
+  p = stream->GetWriteBuffer(&bytes);
+  EXPECT_TRUE(nullptr != p);
+  EXPECT_EQ(kSize / 2, bytes);
+  memcpy(p, in + kSize / 2, kSize / 2);
+  stream->ConsumeWriteBuffer(kSize / 2);
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, nullptr));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize));
+
+  // Try using GetWriteBuffer in a wraparound case
+  // WWWWWWWWWWWW.... 0123456789AB....
+  // RRRRRRRRXXXX.... ........89AB....
+  // ........XXXXWWWW ........89AB0123
+  // WWWW....XXXXXXXX 4567....89AB0123
+  // RRRR....RRRRRRRR ................
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize * 3 / 4, &bytes, nullptr));
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, nullptr));
+  p = stream->GetWriteBuffer(&bytes);
+  EXPECT_TRUE(nullptr != p);
+  EXPECT_EQ(kSize / 4, bytes);
+  memcpy(p, in, kSize / 4);
+  stream->ConsumeWriteBuffer(kSize / 4);
+  p = stream->GetWriteBuffer(&bytes);
+  EXPECT_TRUE(nullptr != p);
+  EXPECT_EQ(kSize / 2, bytes);
+  memcpy(p, in + kSize / 4, kSize / 4);
+  stream->ConsumeWriteBuffer(kSize / 4);
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 3 / 4, &bytes, nullptr));
+  EXPECT_EQ(kSize * 3 / 4, bytes);
+  EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4));
+  EXPECT_EQ(0, memcmp(in, out + kSize / 4, kSize / 4));
+
+  // Check that the stream is now empty
+  EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, nullptr));
+
+  // Try growing the buffer
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, nullptr));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_TRUE(buf.SetCapacity(kSize * 2));
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in + kSize, kSize, &bytes, nullptr));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 2, &bytes, nullptr));
+  EXPECT_EQ(kSize * 2, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize * 2));
+
+  // Try shrinking the buffer
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, nullptr));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_TRUE(buf.SetCapacity(kSize));
+  EXPECT_EQ(SR_BLOCK, stream->Write(in, kSize, &bytes, nullptr));
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, nullptr));
+  EXPECT_EQ(kSize, bytes);
+  EXPECT_EQ(0, memcmp(in, out, kSize));
+
+  // Write to the stream, close it, read the remaining bytes
+  EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, nullptr));
+  stream->Close();
+  EXPECT_EQ(SS_CLOSED, stream->GetState());
+  EXPECT_EQ(SR_EOS, stream->Write(in, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, nullptr));
+  EXPECT_EQ(0, memcmp(in, out, kSize / 2));
+  EXPECT_EQ(SR_EOS, stream->Read(out, kSize / 2, &bytes, nullptr));
+}
+
+TEST(FifoBufferTest, FullBufferCheck) {
+  FifoBuffer buff(10);
+  buff.ConsumeWriteBuffer(10);
+
+  size_t free;
+  EXPECT_TRUE(buff.GetWriteBuffer(&free) != nullptr);
+  EXPECT_EQ(0U, free);
+}
+
+TEST(FifoBufferTest, WriteOffsetAndReadOffset) {
+  const size_t kSize = 16;
+  const char in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
+  char out[kSize * 2];
+  FifoBuffer buf(kSize);
+
+  // Write 14 bytes.
+  EXPECT_EQ(SR_SUCCESS, buf.Write(in, 14, nullptr, nullptr));
+
+  // Make sure data is in |buf|.
+  size_t buffered;
+  EXPECT_TRUE(buf.GetBuffered(&buffered));
+  EXPECT_EQ(14u, buffered);
+
+  // Read 10 bytes.
+  buf.ConsumeReadData(10);
+
+  // There should be now 12 bytes of available space.
+  size_t remaining;
+  EXPECT_TRUE(buf.GetWriteRemaining(&remaining));
+  EXPECT_EQ(12u, remaining);
+
+  // Write at offset 12, this should fail.
+  EXPECT_EQ(SR_BLOCK, buf.WriteOffset(in, 10, 12, nullptr));
+
+  // Write 8 bytes at offset 4, this wraps around the buffer.
+  EXPECT_EQ(SR_SUCCESS, buf.WriteOffset(in, 8, 4, nullptr));
+
+  // Number of available space remains the same until we call
+  // ConsumeWriteBuffer().
+  EXPECT_TRUE(buf.GetWriteRemaining(&remaining));
+  EXPECT_EQ(12u, remaining);
+  buf.ConsumeWriteBuffer(12);
+
+  // There's 4 bytes bypassed and 4 bytes no read so skip them and verify the
+  // 8 bytes written.
+  size_t read;
+  EXPECT_EQ(SR_SUCCESS, buf.ReadOffset(out, 8, 8, &read));
+  EXPECT_EQ(8u, read);
+  EXPECT_EQ(0, memcmp(out, in, 8));
+
+  // There should still be 16 bytes available for reading.
+  EXPECT_TRUE(buf.GetBuffered(&buffered));
+  EXPECT_EQ(16u, buffered);
+
+  // Read at offset 16, this should fail since we don't have that much data.
+  EXPECT_EQ(SR_BLOCK, buf.ReadOffset(out, 10, 16, nullptr));
+}
+
+}  // namespace rtc
diff --git a/base/string_to_number.cc b/base/string_to_number.cc
new file mode 100644
index 0000000..b8e969d
--- /dev/null
+++ b/base/string_to_number.cc
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <cerrno>
+#include <cstdlib>
+
+#include "webrtc/base/string_to_number.h"
+
+namespace rtc {
+namespace string_to_number_internal {
+
+rtc::Optional<signed_type> ParseSigned(const char* str, int base) {
+  RTC_DCHECK(str);
+  if (isdigit(str[0]) || str[0] == '-') {
+    char* end = nullptr;
+    errno = 0;
+    const signed_type value = std::strtoll(str, &end, base);
+    if (end && *end == '\0' && errno == 0) {
+      return rtc::Optional<signed_type>(value);
+    }
+  }
+  return rtc::Optional<signed_type>();
+}
+
+rtc::Optional<unsigned_type> ParseUnsigned(const char* str, int base) {
+  RTC_DCHECK(str);
+  if (isdigit(str[0]) || str[0] == '-') {
+    // Explicitly discard negative values. std::strtoull parsing causes unsigned
+    // wraparound. We cannot just reject values that start with -, though, since
+    // -0 is perfectly fine, as is -0000000000000000000000000000000.
+    const bool is_negative = str[0] == '-';
+    char* end = nullptr;
+    errno = 0;
+    const unsigned_type value = std::strtoull(str, &end, base);
+    if (end && *end == '\0' && errno == 0 && (value == 0 || !is_negative)) {
+      return rtc::Optional<unsigned_type>(value);
+    }
+  }
+  return rtc::Optional<unsigned_type>();
+}
+
+}  // namespace string_to_number_internal
+}  // namespace rtc
diff --git a/base/string_to_number.h b/base/string_to_number.h
index fa88ba4..8306f7c 100644
--- a/base/string_to_number.h
+++ b/base/string_to_number.h
@@ -11,9 +11,91 @@
 #ifndef WEBRTC_BASE_STRING_TO_NUMBER_H_
 #define WEBRTC_BASE_STRING_TO_NUMBER_H_
 
+#include <string>
+#include <limits>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/string_to_number.h"
+#include "webrtc/base/optional.h"
+
+namespace rtc {
+
+// This file declares a family of functions to parse integers from strings.
+// The standard C library functions either fail to indicate errors (atoi, etc.)
+// or are a hassle to work with (strtol, sscanf, etc.). The standard C++ library
+// functions (std::stoi, etc.) indicate errors by throwing exceptions, which
+// are disabled in WebRTC.
+//
+// Integers are parsed using one of the following functions:
+//   rtc::Optional<int-type> StringToNumber(const char* str, int base = 10);
+//   rtc::Optional<int-type> StringToNumber(const std::string& str,
+//                                          int base = 10);
+//
+// These functions parse a value from the beginning of a string into one of the
+// fundamental integer types, or returns an empty Optional if parsing
+// failed. Values outside of the range supported by the type will be
+// rejected. The strings must begin with a digit or a minus sign. No leading
+// space nor trailing contents are allowed.
+// By setting base to 0, one of octal, decimal or hexadecimal will be
+// detected from the string's prefix (0, nothing or 0x, respectively).
+// If non-zero, base can be set to a value between 2 and 36 inclusively.
+//
+// If desired, this interface could be extended with support for floating-point
+// types.
+
+namespace string_to_number_internal {
+// These must be (unsigned) long long, to match the signature of strto(u)ll.
+using unsigned_type = unsigned long long;  // NOLINT(runtime/int)
+using signed_type = long long;  // NOLINT(runtime/int)
+
+rtc::Optional<signed_type> ParseSigned(const char* str, int base);
+rtc::Optional<unsigned_type> ParseUnsigned(const char* str, int base);
+}  // namespace string_to_number_internal
+
+template <typename T>
+typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value,
+                        rtc::Optional<T>>::type
+StringToNumber(const char* str, int base = 10) {
+  using string_to_number_internal::signed_type;
+  static_assert(
+      std::numeric_limits<T>::max() <=
+              std::numeric_limits<signed_type>::max() &&
+          std::numeric_limits<T>::lowest() >=
+              std::numeric_limits<signed_type>::lowest(),
+      "StringToNumber only supports signed integers as large as long long int");
+  rtc::Optional<signed_type> value =
+      string_to_number_internal::ParseSigned(str, base);
+  if (value && *value >= std::numeric_limits<T>::lowest() &&
+      *value <= std::numeric_limits<T>::max()) {
+    return rtc::Optional<T>(static_cast<T>(*value));
+  }
+  return rtc::Optional<T>();
+}
+
+template <typename T>
+typename std::enable_if<std::is_integral<T>::value &&
+                            std::is_unsigned<T>::value,
+                        rtc::Optional<T>>::type
+StringToNumber(const char* str, int base = 10) {
+  using string_to_number_internal::unsigned_type;
+  static_assert(std::numeric_limits<T>::max() <=
+                    std::numeric_limits<unsigned_type>::max(),
+                "StringToNumber only supports unsigned integers as large as "
+                "unsigned long long int");
+  rtc::Optional<unsigned_type> value =
+      string_to_number_internal::ParseUnsigned(str, base);
+  if (value && *value <= std::numeric_limits<T>::max()) {
+    return rtc::Optional<T>(static_cast<T>(*value));
+  }
+  return rtc::Optional<T>();
+}
+
+// The std::string overloads only exists if there is a matching const char*
+// version.
+template <typename T>
+auto StringToNumber(const std::string& str, int base = 10)
+    -> decltype(StringToNumber<T>(str.c_str(), base)) {
+  return StringToNumber<T>(str.c_str(), base);
+}
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_STRING_TO_NUMBER_H_
diff --git a/base/string_to_number_unittest.cc b/base/string_to_number_unittest.cc
new file mode 100644
index 0000000..00ebd4c
--- /dev/null
+++ b/base/string_to_number_unittest.cc
@@ -0,0 +1,115 @@
+/*
+ *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/string_to_number.h"
+
+#include <string>
+#include <type_traits>
+#include <limits>
+
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+namespace {
+// clang-format off
+using IntegerTypes =
+    ::testing::Types<char,
+                     signed char, unsigned char,       // NOLINT(runtime/int)
+                     short,       unsigned short,      // NOLINT(runtime/int)
+                     int,         unsigned int,        // NOLINT(runtime/int)
+                     long,        unsigned long,       // NOLINT(runtime/int)
+                     long long,   unsigned long long,  // NOLINT(runtime/int)
+                     int8_t,      uint8_t,
+                     int16_t,     uint16_t,
+                     int32_t,     uint32_t,
+                     int64_t,     uint64_t>;
+// clang-format on
+
+template <typename T>
+class BasicNumberTest : public ::testing::Test {};
+
+TYPED_TEST_CASE_P(BasicNumberTest);
+
+TYPED_TEST_P(BasicNumberTest, TestValidNumbers) {
+  using T = TypeParam;
+  constexpr T min_value = std::numeric_limits<T>::lowest();
+  constexpr T max_value = std::numeric_limits<T>::max();
+  const std::string min_string = std::to_string(min_value);
+  const std::string max_string = std::to_string(max_value);
+  EXPECT_EQ(min_value, StringToNumber<T>(min_string));
+  EXPECT_EQ(min_value, StringToNumber<T>(min_string.c_str()));
+  EXPECT_EQ(max_value, StringToNumber<T>(max_string));
+  EXPECT_EQ(max_value, StringToNumber<T>(max_string.c_str()));
+  EXPECT_EQ(0, StringToNumber<T>("0"));
+  EXPECT_EQ(0, StringToNumber<T>("-0"));
+  EXPECT_EQ(0, StringToNumber<T>(std::string("-0000000000000")));
+}
+
+TYPED_TEST_P(BasicNumberTest, TestInvalidNumbers) {
+  using T = TypeParam;
+  // Value ranges aren't strictly enforced in this test, since that would either
+  // require doctoring specific strings for each data type, which is a hassle
+  // across platforms, or to be able to do addition of values larger than the
+  // largest type, which is another hassle.
+  constexpr T min_value = std::numeric_limits<T>::lowest();
+  constexpr T max_value = std::numeric_limits<T>::max();
+  // If the type supports negative values, make the large negative value
+  // approximately ten times larger. If the type is unsigned, just use -2.
+  const std::string too_low_string =
+      (min_value == 0) ? "-2" : (std::to_string(min_value) + "1");
+  // Make the large value approximately ten times larger than the maximum.
+  const std::string too_large_string = std::to_string(max_value) + "1";
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(too_low_string));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(too_low_string.c_str()));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(too_large_string));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(too_large_string.c_str()));
+}
+
+TYPED_TEST_P(BasicNumberTest, TestInvalidInputs) {
+  using T = TypeParam;
+  const char kInvalidCharArray[] = "Invalid string containing 47";
+  const char kPlusMinusCharArray[] = "+-100";
+  const char kNumberFollowedByCruft[] = "640x480";
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(kInvalidCharArray));
+  EXPECT_EQ(rtc::Optional<T>(),
+            StringToNumber<T>(std::string(kInvalidCharArray)));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(kPlusMinusCharArray));
+  EXPECT_EQ(rtc::Optional<T>(),
+            StringToNumber<T>(std::string(kPlusMinusCharArray)));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(kNumberFollowedByCruft));
+  EXPECT_EQ(rtc::Optional<T>(),
+            StringToNumber<T>(std::string(kNumberFollowedByCruft)));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(" 5"));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(" - 5"));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>("- 5"));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>(" -5"));
+  EXPECT_EQ(rtc::Optional<T>(), StringToNumber<T>("5 "));
+}
+
+REGISTER_TYPED_TEST_CASE_P(BasicNumberTest,
+                           TestValidNumbers,
+                           TestInvalidNumbers,
+                           TestInvalidInputs);
+
+}  // namespace
+
+INSTANTIATE_TYPED_TEST_CASE_P(StringToNumberTest_Integers,
+                              BasicNumberTest,
+                              IntegerTypes);
+
+TEST(StringToNumberTest, TestSpecificValues) {
+  EXPECT_EQ(rtc::Optional<uint8_t>(), StringToNumber<uint8_t>("256"));
+  EXPECT_EQ(rtc::Optional<uint8_t>(), StringToNumber<uint8_t>("-256"));
+  EXPECT_EQ(rtc::Optional<int8_t>(), StringToNumber<int8_t>("256"));
+  EXPECT_EQ(rtc::Optional<int8_t>(), StringToNumber<int8_t>("-256"));
+}
+
+}  // namespace rtc
diff --git a/base/stringencode.cc b/base/stringencode.cc
new file mode 100644
index 0000000..c7d206f
--- /dev/null
+++ b/base/stringencode.cc
@@ -0,0 +1,696 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/stringencode.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+
+/////////////////////////////////////////////////////////////////////////////
+// String Encoding Utilities
+/////////////////////////////////////////////////////////////////////////////
+
+size_t escape(char * buffer, size_t buflen,
+              const char * source, size_t srclen,
+              const char * illegal, char escape) {
+  RTC_DCHECK(buffer);  // TODO(grunell): estimate output size
+  if (buflen <= 0)
+    return 0;
+
+  size_t srcpos = 0, bufpos = 0;
+  while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+    char ch = source[srcpos++];
+    if ((ch == escape) || ::strchr(illegal, ch)) {
+      if (bufpos + 2 >= buflen)
+        break;
+      buffer[bufpos++] = escape;
+    }
+    buffer[bufpos++] = ch;
+  }
+
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+size_t unescape(char * buffer, size_t buflen,
+                const char * source, size_t srclen,
+                char escape) {
+  RTC_DCHECK(buffer);  // TODO(grunell): estimate output size
+  if (buflen <= 0)
+    return 0;
+
+  size_t srcpos = 0, bufpos = 0;
+  while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+    char ch = source[srcpos++];
+    if ((ch == escape) && (srcpos < srclen)) {
+      ch = source[srcpos++];
+    }
+    buffer[bufpos++] = ch;
+  }
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+size_t encode(char * buffer, size_t buflen,
+              const char * source, size_t srclen,
+              const char * illegal, char escape) {
+  RTC_DCHECK(buffer);  // TODO(grunell): estimate output size
+  if (buflen <= 0)
+    return 0;
+
+  size_t srcpos = 0, bufpos = 0;
+  while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+    char ch = source[srcpos++];
+    if ((ch != escape) && !::strchr(illegal, ch)) {
+      buffer[bufpos++] = ch;
+    } else if (bufpos + 3 >= buflen) {
+      break;
+    } else {
+      buffer[bufpos+0] = escape;
+      buffer[bufpos+1] = hex_encode((static_cast<unsigned char>(ch) >> 4) & 0xF);
+      buffer[bufpos+2] = hex_encode((static_cast<unsigned char>(ch)     ) & 0xF);
+      bufpos += 3;
+    }
+  }
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+size_t decode(char * buffer, size_t buflen,
+              const char * source, size_t srclen,
+              char escape) {
+  if (buflen <= 0)
+    return 0;
+
+  unsigned char h1, h2;
+  size_t srcpos = 0, bufpos = 0;
+  while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+    char ch = source[srcpos++];
+    if ((ch == escape)
+        && (srcpos + 1 < srclen)
+        && hex_decode(source[srcpos], &h1)
+        && hex_decode(source[srcpos+1], &h2)) {
+      buffer[bufpos++] = (h1 << 4) | h2;
+      srcpos += 2;
+    } else {
+      buffer[bufpos++] = ch;
+    }
+  }
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+const char* unsafe_filename_characters() {
+  // It might be better to have a single specification which is the union of
+  // all operating systems, unless one system is overly restrictive.
+#if defined(WEBRTC_WIN)
+  return "\\/:*?\"<>|";
+#else  // !WEBRTC_WIN
+  // TODO(grunell): Should this never be reached?
+  RTC_NOTREACHED();
+  return "";
+#endif  // !WEBRTC_WIN
+}
+
+const unsigned char URL_UNSAFE  = 0x1; // 0-33 "#$%&+,/:;<=>?@[\]^`{|} 127
+const unsigned char XML_UNSAFE  = 0x2; // "&'<>
+const unsigned char HTML_UNSAFE = 0x2; // "&'<>
+
+//  ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 6 5 7 8 9 : ; < = > ?
+//@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
+//` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
+
+const unsigned char ASCII_CLASS[128] = {
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,0,3,1,1,1,3,2,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,3,1,3,1,
+  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,
+  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,
+};
+
+size_t url_encode(char * buffer, size_t buflen,
+                  const char * source, size_t srclen) {
+  if (nullptr == buffer)
+    return srclen * 3 + 1;
+  if (buflen <= 0)
+    return 0;
+
+  size_t srcpos = 0, bufpos = 0;
+  while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+    unsigned char ch = source[srcpos++];
+    if ((ch < 128) && (ASCII_CLASS[ch] & URL_UNSAFE)) {
+      if (bufpos + 3 >= buflen) {
+        break;
+      }
+      buffer[bufpos+0] = '%';
+      buffer[bufpos+1] = hex_encode((ch >> 4) & 0xF);
+      buffer[bufpos+2] = hex_encode((ch     ) & 0xF);
+      bufpos += 3;
+    } else {
+      buffer[bufpos++] = ch;
+    }
+  }
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+size_t url_decode(char * buffer, size_t buflen,
+                  const char * source, size_t srclen) {
+  if (nullptr == buffer)
+    return srclen + 1;
+  if (buflen <= 0)
+    return 0;
+
+  unsigned char h1, h2;
+  size_t srcpos = 0, bufpos = 0;
+  while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+    unsigned char ch = source[srcpos++];
+    if (ch == '+') {
+      buffer[bufpos++] = ' ';
+    } else if ((ch == '%')
+               && (srcpos + 1 < srclen)
+               && hex_decode(source[srcpos], &h1)
+               && hex_decode(source[srcpos+1], &h2))
+    {
+      buffer[bufpos++] = (h1 << 4) | h2;
+      srcpos += 2;
+    } else {
+      buffer[bufpos++] = ch;
+    }
+  }
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+size_t utf8_decode(const char* source, size_t srclen, unsigned long* value) {
+  const unsigned char* s = reinterpret_cast<const unsigned char*>(source);
+  if ((s[0] & 0x80) == 0x00) {                    // Check s[0] == 0xxxxxxx
+    *value = s[0];
+    return 1;
+  }
+  if ((srclen < 2) || ((s[1] & 0xC0) != 0x80)) {  // Check s[1] != 10xxxxxx
+    return 0;
+  }
+  // Accumulate the trailer byte values in value16, and combine it with the
+  // relevant bits from s[0], once we've determined the sequence length.
+  unsigned long value16 = (s[1] & 0x3F);
+  if ((s[0] & 0xE0) == 0xC0) {                    // Check s[0] == 110xxxxx
+    *value = ((s[0] & 0x1F) << 6) | value16;
+    return 2;
+  }
+  if ((srclen < 3) || ((s[2] & 0xC0) != 0x80)) {  // Check s[2] != 10xxxxxx
+    return 0;
+  }
+  value16 = (value16 << 6) | (s[2] & 0x3F);
+  if ((s[0] & 0xF0) == 0xE0) {                    // Check s[0] == 1110xxxx
+    *value = ((s[0] & 0x0F) << 12) | value16;
+    return 3;
+  }
+  if ((srclen < 4) || ((s[3] & 0xC0) != 0x80)) {  // Check s[3] != 10xxxxxx
+    return 0;
+  }
+  value16 = (value16 << 6) | (s[3] & 0x3F);
+  if ((s[0] & 0xF8) == 0xF0) {                    // Check s[0] == 11110xxx
+    *value = ((s[0] & 0x07) << 18) | value16;
+    return 4;
+  }
+  return 0;
+}
+
+size_t utf8_encode(char* buffer, size_t buflen, unsigned long value) {
+  if ((value <= 0x7F) && (buflen >= 1)) {
+    buffer[0] = static_cast<unsigned char>(value);
+    return 1;
+  }
+  if ((value <= 0x7FF) && (buflen >= 2)) {
+    buffer[0] = 0xC0 | static_cast<unsigned char>(value >> 6);
+    buffer[1] = 0x80 | static_cast<unsigned char>(value & 0x3F);
+    return 2;
+  }
+  if ((value <= 0xFFFF) && (buflen >= 3)) {
+    buffer[0] = 0xE0 | static_cast<unsigned char>(value >> 12);
+    buffer[1] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
+    buffer[2] = 0x80 | static_cast<unsigned char>(value & 0x3F);
+    return 3;
+  }
+  if ((value <= 0x1FFFFF) && (buflen >= 4)) {
+    buffer[0] = 0xF0 | static_cast<unsigned char>(value >> 18);
+    buffer[1] = 0x80 | static_cast<unsigned char>((value >> 12) & 0x3F);
+    buffer[2] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
+    buffer[3] = 0x80 | static_cast<unsigned char>(value & 0x3F);
+    return 4;
+  }
+  return 0;
+}
+
+size_t html_encode(char * buffer, size_t buflen,
+                   const char * source, size_t srclen) {
+  RTC_DCHECK(buffer);  // TODO(grunell): estimate output size
+  if (buflen <= 0)
+    return 0;
+
+  size_t srcpos = 0, bufpos = 0;
+  while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+    unsigned char ch = source[srcpos];
+    if (ch < 128) {
+      srcpos += 1;
+      if (ASCII_CLASS[ch] & HTML_UNSAFE) {
+        const char * escseq = 0;
+        size_t esclen = 0;
+        switch (ch) {
+          case '<':  escseq = "&lt;";   esclen = 4; break;
+          case '>':  escseq = "&gt;";   esclen = 4; break;
+          case '\'': escseq = "&#39;";  esclen = 5; break;
+          case '\"': escseq = "&quot;"; esclen = 6; break;
+          case '&':  escseq = "&amp;";  esclen = 5; break;
+          default: RTC_NOTREACHED();
+        }
+        if (bufpos + esclen >= buflen) {
+          break;
+        }
+        memcpy(buffer + bufpos, escseq, esclen);
+        bufpos += esclen;
+      } else {
+        buffer[bufpos++] = ch;
+      }
+    } else {
+      // Largest value is 0x1FFFFF => &#2097151;  (10 characters)
+      const size_t kEscseqSize = 11;
+      char escseq[kEscseqSize];
+      unsigned long val;
+      if (size_t vallen = utf8_decode(&source[srcpos], srclen - srcpos, &val)) {
+        srcpos += vallen;
+      } else {
+        // Not a valid utf8 sequence, just use the raw character.
+        val = static_cast<unsigned char>(source[srcpos++]);
+      }
+      size_t esclen = sprintfn(escseq, kEscseqSize, "&#%lu;", val);
+      if (bufpos + esclen >= buflen) {
+        break;
+      }
+      memcpy(buffer + bufpos, escseq, esclen);
+      bufpos += esclen;
+    }
+  }
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+size_t html_decode(char * buffer, size_t buflen,
+                   const char * source, size_t srclen) {
+  RTC_DCHECK(buffer);  // TODO(grunell): estimate output size
+  return xml_decode(buffer, buflen, source, srclen);
+}
+
+size_t xml_encode(char * buffer, size_t buflen,
+                  const char * source, size_t srclen) {
+  RTC_DCHECK(buffer);  // TODO(grunell): estimate output size
+  if (buflen <= 0)
+    return 0;
+
+  size_t srcpos = 0, bufpos = 0;
+  while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+    unsigned char ch = source[srcpos++];
+    if ((ch < 128) && (ASCII_CLASS[ch] & XML_UNSAFE)) {
+      const char * escseq = 0;
+      size_t esclen = 0;
+      switch (ch) {
+        case '<':  escseq = "&lt;";   esclen = 4; break;
+        case '>':  escseq = "&gt;";   esclen = 4; break;
+        case '\'': escseq = "&apos;"; esclen = 6; break;
+        case '\"': escseq = "&quot;"; esclen = 6; break;
+        case '&':  escseq = "&amp;";  esclen = 5; break;
+        default: RTC_NOTREACHED();
+      }
+      if (bufpos + esclen >= buflen) {
+        break;
+      }
+      memcpy(buffer + bufpos, escseq, esclen);
+      bufpos += esclen;
+    } else {
+      buffer[bufpos++] = ch;
+    }
+  }
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+size_t xml_decode(char * buffer, size_t buflen,
+                  const char * source, size_t srclen) {
+  RTC_DCHECK(buffer);  // TODO(grunell): estimate output size
+  if (buflen <= 0)
+    return 0;
+
+  size_t srcpos = 0, bufpos = 0;
+  while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
+    unsigned char ch = source[srcpos++];
+    if (ch != '&') {
+      buffer[bufpos++] = ch;
+    } else if ((srcpos + 2 < srclen)
+               && (memcmp(source + srcpos, "lt;", 3) == 0)) {
+      buffer[bufpos++] = '<';
+      srcpos += 3;
+    } else if ((srcpos + 2 < srclen)
+               && (memcmp(source + srcpos, "gt;", 3) == 0)) {
+      buffer[bufpos++] = '>';
+      srcpos += 3;
+    } else if ((srcpos + 4 < srclen)
+               && (memcmp(source + srcpos, "apos;", 5) == 0)) {
+      buffer[bufpos++] = '\'';
+      srcpos += 5;
+    } else if ((srcpos + 4 < srclen)
+               && (memcmp(source + srcpos, "quot;", 5) == 0)) {
+      buffer[bufpos++] = '\"';
+      srcpos += 5;
+    } else if ((srcpos + 3 < srclen)
+               && (memcmp(source + srcpos, "amp;", 4) == 0)) {
+      buffer[bufpos++] = '&';
+      srcpos += 4;
+    } else if ((srcpos < srclen) && (source[srcpos] == '#')) {
+      int int_base = 10;
+      if ((srcpos + 1 < srclen) && (source[srcpos+1] == 'x')) {
+        int_base = 16;
+        srcpos += 1;
+      }
+      char * ptr;
+      // TODO(grunell): Fix hack (ptr may go past end of data)
+      unsigned long val = strtoul(source + srcpos + 1, &ptr, int_base);
+      if ((static_cast<size_t>(ptr - source) < srclen) && (*ptr == ';')) {
+        srcpos = ptr - source + 1;
+      } else {
+        // Not a valid escape sequence.
+        break;
+      }
+      if (size_t esclen = utf8_encode(buffer + bufpos, buflen - bufpos, val)) {
+        bufpos += esclen;
+      } else {
+        // Not enough room to encode the character, or illegal character
+        break;
+      }
+    } else {
+      // Unrecognized escape sequence.
+      break;
+    }
+  }
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+static const char HEX[] = "0123456789abcdef";
+
+char hex_encode(unsigned char val) {
+  RTC_DCHECK_LT(val, 16);
+  return (val < 16) ? HEX[val] : '!';
+}
+
+bool hex_decode(char ch, unsigned char* val) {
+  if ((ch >= '0') && (ch <= '9')) {
+    *val = ch - '0';
+  } else if ((ch >= 'A') && (ch <= 'Z')) {
+    *val = (ch - 'A') + 10;
+  } else if ((ch >= 'a') && (ch <= 'z')) {
+    *val = (ch - 'a') + 10;
+  } else {
+    return false;
+  }
+  return true;
+}
+
+size_t hex_encode(char* buffer, size_t buflen,
+                  const char* csource, size_t srclen) {
+  return hex_encode_with_delimiter(buffer, buflen, csource, srclen, 0);
+}
+
+size_t hex_encode_with_delimiter(char* buffer, size_t buflen,
+                                 const char* csource, size_t srclen,
+                                 char delimiter) {
+  RTC_DCHECK(buffer);  // TODO(grunell): estimate output size
+  if (buflen == 0)
+    return 0;
+
+  // Init and check bounds.
+  const unsigned char* bsource =
+      reinterpret_cast<const unsigned char*>(csource);
+  size_t srcpos = 0, bufpos = 0;
+  size_t needed = delimiter ? (srclen * 3) : (srclen * 2 + 1);
+  if (buflen < needed)
+    return 0;
+
+  while (srcpos < srclen) {
+    unsigned char ch = bsource[srcpos++];
+    buffer[bufpos  ] = hex_encode((ch >> 4) & 0xF);
+    buffer[bufpos+1] = hex_encode((ch     ) & 0xF);
+    bufpos += 2;
+
+    // Don't write a delimiter after the last byte.
+    if (delimiter && (srcpos < srclen)) {
+      buffer[bufpos] = delimiter;
+      ++bufpos;
+    }
+  }
+
+  // Null terminate.
+  buffer[bufpos] = '\0';
+  return bufpos;
+}
+
+std::string hex_encode(const std::string& str) {
+  return hex_encode(str.c_str(), str.size());
+}
+
+std::string hex_encode(const char* source, size_t srclen) {
+  return hex_encode_with_delimiter(source, srclen, 0);
+}
+
+std::string hex_encode_with_delimiter(const char* source, size_t srclen,
+                                      char delimiter) {
+  const size_t kBufferSize = srclen * 3;
+  char* buffer = STACK_ARRAY(char, kBufferSize);
+  size_t length = hex_encode_with_delimiter(buffer, kBufferSize,
+                                            source, srclen, delimiter);
+  RTC_DCHECK(srclen == 0 || length > 0);
+  return std::string(buffer, length);
+}
+
+size_t hex_decode(char * cbuffer, size_t buflen,
+                  const char * source, size_t srclen) {
+  return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0);
+}
+
+size_t hex_decode_with_delimiter(char* cbuffer, size_t buflen,
+                                 const char* source, size_t srclen,
+                                 char delimiter) {
+  RTC_DCHECK(cbuffer);  // TODO(grunell): estimate output size
+  if (buflen == 0)
+    return 0;
+
+  // Init and bounds check.
+  unsigned char* bbuffer = reinterpret_cast<unsigned char*>(cbuffer);
+  size_t srcpos = 0, bufpos = 0;
+  size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2;
+  if (buflen < needed)
+    return 0;
+
+  while (srcpos < srclen) {
+    if ((srclen - srcpos) < 2) {
+      // This means we have an odd number of bytes.
+      return 0;
+    }
+
+    unsigned char h1, h2;
+    if (!hex_decode(source[srcpos], &h1) ||
+        !hex_decode(source[srcpos + 1], &h2))
+      return 0;
+
+    bbuffer[bufpos++] = (h1 << 4) | h2;
+    srcpos += 2;
+
+    // Remove the delimiter if needed.
+    if (delimiter && (srclen - srcpos) > 1) {
+      if (source[srcpos] != delimiter)
+        return 0;
+      ++srcpos;
+    }
+  }
+
+  return bufpos;
+}
+
+size_t hex_decode(char* buffer, size_t buflen, const std::string& source) {
+  return hex_decode_with_delimiter(buffer, buflen, source, 0);
+}
+size_t hex_decode_with_delimiter(char* buffer, size_t buflen,
+                                 const std::string& source, char delimiter) {
+  return hex_decode_with_delimiter(buffer, buflen,
+                                   source.c_str(), source.length(), delimiter);
+}
+
+size_t transform(std::string& value, size_t maxlen, const std::string& source,
+                 Transform t) {
+  char* buffer = STACK_ARRAY(char, maxlen + 1);
+  size_t length = t(buffer, maxlen + 1, source.data(), source.length());
+  value.assign(buffer, length);
+  return length;
+}
+
+std::string s_transform(const std::string& source, Transform t) {
+  // Ask transformation function to approximate the destination size (returns upper bound)
+  size_t maxlen = t(nullptr, 0, source.data(), source.length());
+  char * buffer = STACK_ARRAY(char, maxlen);
+  size_t len = t(buffer, maxlen, source.data(), source.length());
+  std::string result(buffer, len);
+  return result;
+}
+
+size_t tokenize(const std::string& source, char delimiter,
+                std::vector<std::string>* fields) {
+  fields->clear();
+  size_t last = 0;
+  for (size_t i = 0; i < source.length(); ++i) {
+    if (source[i] == delimiter) {
+      if (i != last) {
+        fields->push_back(source.substr(last, i - last));
+      }
+      last = i + 1;
+    }
+  }
+  if (last != source.length()) {
+    fields->push_back(source.substr(last, source.length() - last));
+  }
+  return fields->size();
+}
+
+size_t tokenize_with_empty_tokens(const std::string& source,
+                                  char delimiter,
+                                  std::vector<std::string>* fields) {
+  fields->clear();
+  size_t last = 0;
+  for (size_t i = 0; i < source.length(); ++i) {
+    if (source[i] == delimiter) {
+      fields->push_back(source.substr(last, i - last));
+      last = i + 1;
+    }
+  }
+  fields->push_back(source.substr(last, source.length() - last));
+  return fields->size();
+}
+
+size_t tokenize_append(const std::string& source, char delimiter,
+                       std::vector<std::string>* fields) {
+  if (!fields) return 0;
+
+  std::vector<std::string> new_fields;
+  tokenize(source, delimiter, &new_fields);
+  fields->insert(fields->end(), new_fields.begin(), new_fields.end());
+  return fields->size();
+}
+
+size_t tokenize(const std::string& source, char delimiter, char start_mark,
+                char end_mark, std::vector<std::string>* fields) {
+  if (!fields) return 0;
+  fields->clear();
+
+  std::string remain_source = source;
+  while (!remain_source.empty()) {
+    size_t start_pos = remain_source.find(start_mark);
+    if (std::string::npos == start_pos) break;
+    std::string pre_mark;
+    if (start_pos > 0) {
+      pre_mark = remain_source.substr(0, start_pos - 1);
+    }
+
+    ++start_pos;
+    size_t end_pos = remain_source.find(end_mark, start_pos);
+    if (std::string::npos == end_pos) break;
+
+    // We have found the matching marks. First tokenize the pre-mask. Then add
+    // the marked part as a single field. Finally, loop back for the post-mark.
+    tokenize_append(pre_mark, delimiter, fields);
+    fields->push_back(remain_source.substr(start_pos, end_pos - start_pos));
+    remain_source = remain_source.substr(end_pos + 1);
+  }
+
+  return tokenize_append(remain_source, delimiter, fields);
+}
+
+bool tokenize_first(const std::string& source,
+                    const char delimiter,
+                    std::string* token,
+                    std::string* rest) {
+  // Find the first delimiter
+  size_t left_pos = source.find(delimiter);
+  if (left_pos == std::string::npos) {
+    return false;
+  }
+
+  // Look for additional occurrances of delimiter.
+  size_t right_pos = left_pos + 1;
+  while (source[right_pos] == delimiter) {
+    right_pos++;
+  }
+
+  *token = source.substr(0, left_pos);
+  *rest = source.substr(right_pos);
+  return true;
+}
+
+size_t split(const std::string& source, char delimiter,
+             std::vector<std::string>* fields) {
+  RTC_DCHECK(fields);
+  fields->clear();
+  size_t last = 0;
+  for (size_t i = 0; i < source.length(); ++i) {
+    if (source[i] == delimiter) {
+      fields->push_back(source.substr(last, i - last));
+      last = i + 1;
+    }
+  }
+  fields->push_back(source.substr(last, source.length() - last));
+  return fields->size();
+}
+
+char make_char_safe_for_filename(char c) {
+  if (c < 32)
+    return '_';
+
+  switch (c) {
+    case '<':
+    case '>':
+    case ':':
+    case '"':
+    case '/':
+    case '\\':
+    case '|':
+    case '*':
+    case '?':
+      return '_';
+
+    default:
+      return c;
+  }
+}
+
+/*
+void sprintf(std::string& value, size_t maxlen, const char * format, ...) {
+  char * buffer = STACK_ARRAY(char, maxlen + 1);
+  va_list args;
+  va_start(args, format);
+  value.assign(buffer, vsprintfn(buffer, maxlen + 1, format, args));
+  va_end(args);
+}
+*/
+
+/////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
diff --git a/base/stringencode.h b/base/stringencode.h
index 27b810e..8f78ad1 100644
--- a/base/stringencode.h
+++ b/base/stringencode.h
@@ -11,9 +11,214 @@
 #ifndef WEBRTC_BASE_STRINGENCODE_H_
 #define WEBRTC_BASE_STRINGENCODE_H_
 
+#include <sstream>
+#include <string>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/stringencode.h"
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+//////////////////////////////////////////////////////////////////////
+// String Encoding Utilities
+//////////////////////////////////////////////////////////////////////
+
+// Convert an unsigned value to it's utf8 representation.  Returns the length
+// of the encoded string, or 0 if the encoding is longer than buflen - 1.
+size_t utf8_encode(char* buffer, size_t buflen, unsigned long value);
+// Decode the utf8 encoded value pointed to by source.  Returns the number of
+// bytes used by the encoding, or 0 if the encoding is invalid.
+size_t utf8_decode(const char* source, size_t srclen, unsigned long* value);
+
+// Escaping prefixes illegal characters with the escape character.  Compact, but
+// illegal characters still appear in the string.
+size_t escape(char * buffer, size_t buflen,
+              const char * source, size_t srclen,
+              const char * illegal, char escape);
+// Note: in-place unescaping (buffer == source) is allowed.
+size_t unescape(char * buffer, size_t buflen,
+                const char * source, size_t srclen,
+                char escape);
+
+// Encoding replaces illegal characters with the escape character and 2 hex
+// chars, so it's a little less compact than escape, but completely removes
+// illegal characters.  note that hex digits should not be used as illegal
+// characters.
+size_t encode(char * buffer, size_t buflen,
+              const char * source, size_t srclen,
+              const char * illegal, char escape);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t decode(char * buffer, size_t buflen,
+              const char * source, size_t srclen,
+              char escape);
+
+// Returns a list of characters that may be unsafe for use in the name of a
+// file, suitable for passing to the 'illegal' member of escape or encode.
+const char* unsafe_filename_characters();
+
+// url_encode is an encode operation with a predefined set of illegal characters
+// and escape character (for use in URLs, obviously).
+size_t url_encode(char * buffer, size_t buflen,
+                  const char * source, size_t srclen);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t url_decode(char * buffer, size_t buflen,
+                  const char * source, size_t srclen);
+
+// html_encode prevents data embedded in html from containing markup.
+size_t html_encode(char * buffer, size_t buflen,
+                   const char * source, size_t srclen);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t html_decode(char * buffer, size_t buflen,
+                   const char * source, size_t srclen);
+
+// xml_encode makes data suitable for inside xml attributes and values.
+size_t xml_encode(char * buffer, size_t buflen,
+                  const char * source, size_t srclen);
+// Note: in-place decoding (buffer == source) is allowed.
+size_t xml_decode(char * buffer, size_t buflen,
+                  const char * source, size_t srclen);
+
+// Convert an unsigned value from 0 to 15 to the hex character equivalent...
+char hex_encode(unsigned char val);
+// ...and vice-versa.
+bool hex_decode(char ch, unsigned char* val);
+
+// hex_encode shows the hex representation of binary data in ascii.
+size_t hex_encode(char* buffer, size_t buflen,
+                  const char* source, size_t srclen);
+
+// hex_encode, but separate each byte representation with a delimiter.
+// |delimiter| == 0 means no delimiter
+// If the buffer is too short, we return 0
+size_t hex_encode_with_delimiter(char* buffer, size_t buflen,
+                                 const char* source, size_t srclen,
+                                 char delimiter);
+
+// Helper functions for hex_encode.
+std::string hex_encode(const std::string& str);
+std::string hex_encode(const char* source, size_t srclen);
+std::string hex_encode_with_delimiter(const char* source, size_t srclen,
+                                      char delimiter);
+
+// hex_decode converts ascii hex to binary.
+size_t hex_decode(char* buffer, size_t buflen,
+                  const char* source, size_t srclen);
+
+// hex_decode, assuming that there is a delimiter between every byte
+// pair.
+// |delimiter| == 0 means no delimiter
+// If the buffer is too short or the data is invalid, we return 0.
+size_t hex_decode_with_delimiter(char* buffer, size_t buflen,
+                                 const char* source, size_t srclen,
+                                 char delimiter);
+
+// Helper functions for hex_decode.
+size_t hex_decode(char* buffer, size_t buflen, const std::string& source);
+size_t hex_decode_with_delimiter(char* buffer, size_t buflen,
+                                 const std::string& source, char delimiter);
+
+// Apply any suitable string transform (including the ones above) to an STL
+// string.  Stack-allocated temporary space is used for the transformation,
+// so value and source may refer to the same string.
+typedef size_t (*Transform)(char * buffer, size_t buflen,
+                            const char * source, size_t srclen);
+size_t transform(std::string& value, size_t maxlen, const std::string& source,
+                 Transform t);
+
+// Return the result of applying transform t to source.
+std::string s_transform(const std::string& source, Transform t);
+
+// Convenience wrappers.
+inline std::string s_url_encode(const std::string& source) {
+  return s_transform(source, url_encode);
+}
+inline std::string s_url_decode(const std::string& source) {
+  return s_transform(source, url_decode);
+}
+
+// Splits the source string into multiple fields separated by delimiter,
+// with duplicates of delimiter creating empty fields.
+size_t split(const std::string& source, char delimiter,
+             std::vector<std::string>* fields);
+
+// Splits the source string into multiple fields separated by delimiter,
+// with duplicates of delimiter ignored.  Trailing delimiter ignored.
+size_t tokenize(const std::string& source, char delimiter,
+                std::vector<std::string>* fields);
+
+// Tokenize, including the empty tokens.
+size_t tokenize_with_empty_tokens(const std::string& source,
+                                  char delimiter,
+                                  std::vector<std::string>* fields);
+
+// Tokenize and append the tokens to fields. Return the new size of fields.
+size_t tokenize_append(const std::string& source, char delimiter,
+                       std::vector<std::string>* fields);
+
+// Splits the source string into multiple fields separated by delimiter, with
+// duplicates of delimiter ignored. Trailing delimiter ignored. A substring in
+// between the start_mark and the end_mark is treated as a single field. Return
+// the size of fields. For example, if source is "filename
+// \"/Library/Application Support/media content.txt\"", delimiter is ' ', and
+// the start_mark and end_mark are '"', this method returns two fields:
+// "filename" and "/Library/Application Support/media content.txt".
+size_t tokenize(const std::string& source, char delimiter, char start_mark,
+                char end_mark, std::vector<std::string>* fields);
+
+// Extract the first token from source as separated by delimiter, with
+// duplicates of delimiter ignored. Return false if the delimiter could not be
+// found, otherwise return true.
+bool tokenize_first(const std::string& source,
+                    const char delimiter,
+                    std::string* token,
+                    std::string* rest);
+
+// Safe sprintf to std::string
+//void sprintf(std::string& value, size_t maxlen, const char * format, ...)
+//     PRINTF_FORMAT(3);
+
+// Convert arbitrary values to/from a string.
+
+template <class T>
+static bool ToString(const T &t, std::string* s) {
+  RTC_DCHECK(s);
+  std::ostringstream oss;
+  oss << std::boolalpha << t;
+  *s = oss.str();
+  return !oss.fail();
+}
+
+template <class T>
+static bool FromString(const std::string& s, T* t) {
+  RTC_DCHECK(t);
+  std::istringstream iss(s);
+  iss >> std::boolalpha >> *t;
+  return !iss.fail();
+}
+
+// Inline versions of the string conversion routines.
+
+template<typename T>
+static inline std::string ToString(const T& val) {
+  std::string str; ToString(val, &str); return str;
+}
+
+template<typename T>
+static inline T FromString(const std::string& str) {
+  T val; FromString(str, &val); return val;
+}
+
+template<typename T>
+static inline T FromString(const T& defaultValue, const std::string& str) {
+  T val(defaultValue); FromString(str, &val); return val;
+}
+
+// simple function to strip out characters which shouldn't be
+// used in filenames
+char make_char_safe_for_filename(char c);
+
+//////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_STRINGENCODE_H__
diff --git a/base/stringencode_unittest.cc b/base/stringencode_unittest.cc
new file mode 100644
index 0000000..60929f3
--- /dev/null
+++ b/base/stringencode_unittest.cc
@@ -0,0 +1,448 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+
+TEST(Utf8EncodeTest, EncodeDecode) {
+  const struct Utf8Test {
+    const char* encoded;
+    size_t encsize, enclen;
+    unsigned long decoded;
+  } kTests[] = {
+      {"a    ",             5, 1, 'a'},
+      {"\x7F    ",          5, 1, 0x7F},
+      {"\xC2\x80   ",       5, 2, 0x80},
+      {"\xDF\xBF   ",       5, 2, 0x7FF},
+      {"\xE0\xA0\x80  ",    5, 3, 0x800},
+      {"\xEF\xBF\xBF  ",    5, 3, 0xFFFF},
+      {"\xF0\x90\x80\x80 ", 5, 4, 0x10000},
+      {"\xF0\x90\x80\x80 ", 3, 0, 0x10000},
+      {"\xF0\xF0\x80\x80 ", 5, 0, 0},
+      {"\xF0\x90\x80  ",    5, 0, 0},
+      {"\x90\x80\x80  ",    5, 0, 0},
+      {nullptr, 0, 0},
+  };
+  for (size_t i = 0; kTests[i].encoded; ++i) {
+    unsigned long val = 0;
+    ASSERT_EQ(kTests[i].enclen, utf8_decode(kTests[i].encoded,
+                                            kTests[i].encsize,
+                                            &val));
+    unsigned long result = (kTests[i].enclen == 0) ? 0 : kTests[i].decoded;
+    ASSERT_EQ(result, val);
+
+    if (kTests[i].decoded == 0) {
+      // Not an interesting encoding test case
+      continue;
+    }
+
+    char buffer[5];
+    memset(buffer, 0x01, arraysize(buffer));
+    ASSERT_EQ(kTests[i].enclen, utf8_encode(buffer,
+                                            kTests[i].encsize,
+                                            kTests[i].decoded));
+    ASSERT_TRUE(memcmp(buffer, kTests[i].encoded, kTests[i].enclen) == 0);
+    // Make sure remainder of buffer is unchanged
+    ASSERT_TRUE(memory_check(buffer + kTests[i].enclen,
+                             0x1,
+                             arraysize(buffer) - kTests[i].enclen));
+  }
+}
+
+class HexEncodeTest : public testing::Test {
+ public:
+  HexEncodeTest() : enc_res_(0), dec_res_(0) {
+    for (size_t i = 0; i < sizeof(data_); ++i) {
+      data_[i] = (i + 128) & 0xff;
+    }
+    memset(decoded_, 0x7f, sizeof(decoded_));
+  }
+
+  char data_[10];
+  char encoded_[31];
+  char decoded_[11];
+  size_t enc_res_;
+  size_t dec_res_;
+};
+
+// Test that we can convert to/from hex with no delimiter.
+TEST_F(HexEncodeTest, TestWithNoDelimiter) {
+  enc_res_ = hex_encode(encoded_, sizeof(encoded_), data_, sizeof(data_));
+  ASSERT_EQ(sizeof(data_) * 2, enc_res_);
+  ASSERT_STREQ("80818283848586878889", encoded_);
+  dec_res_ = hex_decode(decoded_, sizeof(decoded_), encoded_, enc_res_);
+  ASSERT_EQ(sizeof(data_), dec_res_);
+  ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_));
+}
+
+// Test that we can convert to/from hex with a colon delimiter.
+TEST_F(HexEncodeTest, TestWithDelimiter) {
+  enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(encoded_),
+                                       data_, sizeof(data_), ':');
+  ASSERT_EQ(sizeof(data_) * 3 - 1, enc_res_);
+  ASSERT_STREQ("80:81:82:83:84:85:86:87:88:89", encoded_);
+  dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_),
+                                       encoded_, enc_res_, ':');
+  ASSERT_EQ(sizeof(data_), dec_res_);
+  ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_));
+}
+
+// Test that encoding with one delimiter and decoding with another fails.
+TEST_F(HexEncodeTest, TestWithWrongDelimiter) {
+  enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(encoded_),
+                                       data_, sizeof(data_), ':');
+  ASSERT_EQ(sizeof(data_) * 3 - 1, enc_res_);
+  dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_),
+                                       encoded_, enc_res_, '/');
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Test that encoding without a delimiter and decoding with one fails.
+TEST_F(HexEncodeTest, TestExpectedDelimiter) {
+  enc_res_ = hex_encode(encoded_, sizeof(encoded_), data_, sizeof(data_));
+  ASSERT_EQ(sizeof(data_) * 2, enc_res_);
+  dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_),
+                                       encoded_, enc_res_, ':');
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Test that encoding with a delimiter and decoding without one fails.
+TEST_F(HexEncodeTest, TestExpectedNoDelimiter) {
+  enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(encoded_),
+                                       data_, sizeof(data_), ':');
+  ASSERT_EQ(sizeof(data_) * 3 - 1, enc_res_);
+  dec_res_ = hex_decode(decoded_, sizeof(decoded_), encoded_, enc_res_);
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Test that we handle a zero-length buffer with no delimiter.
+TEST_F(HexEncodeTest, TestZeroLengthNoDelimiter) {
+  enc_res_ = hex_encode(encoded_, sizeof(encoded_), "", 0);
+  ASSERT_EQ(0U, enc_res_);
+  dec_res_ = hex_decode(decoded_, sizeof(decoded_), encoded_, enc_res_);
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Test that we handle a zero-length buffer with a delimiter.
+TEST_F(HexEncodeTest, TestZeroLengthWithDelimiter) {
+  enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(encoded_), "", 0, ':');
+  ASSERT_EQ(0U, enc_res_);
+  dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_),
+                                       encoded_, enc_res_, ':');
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Test the std::string variants that take no delimiter.
+TEST_F(HexEncodeTest, TestHelpersNoDelimiter) {
+  std::string result = hex_encode(data_, sizeof(data_));
+  ASSERT_EQ("80818283848586878889", result);
+  dec_res_ = hex_decode(decoded_, sizeof(decoded_), result);
+  ASSERT_EQ(sizeof(data_), dec_res_);
+  ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_));
+}
+
+// Test the std::string variants that use a delimiter.
+TEST_F(HexEncodeTest, TestHelpersWithDelimiter) {
+  std::string result = hex_encode_with_delimiter(data_, sizeof(data_), ':');
+  ASSERT_EQ("80:81:82:83:84:85:86:87:88:89", result);
+  dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), result, ':');
+  ASSERT_EQ(sizeof(data_), dec_res_);
+  ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_));
+}
+
+// Test that encoding into a too-small output buffer (without delimiter) fails.
+TEST_F(HexEncodeTest, TestEncodeTooShort) {
+  enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(data_) * 2,
+                                       data_, sizeof(data_), 0);
+  ASSERT_EQ(0U, enc_res_);
+}
+
+// Test that encoding into a too-small output buffer (with delimiter) fails.
+TEST_F(HexEncodeTest, TestEncodeWithDelimiterTooShort) {
+  enc_res_ = hex_encode_with_delimiter(encoded_, sizeof(data_) * 3 - 1,
+                                       data_, sizeof(data_), ':');
+  ASSERT_EQ(0U, enc_res_);
+}
+
+// Test that decoding into a too-small output buffer fails.
+TEST_F(HexEncodeTest, TestDecodeTooShort) {
+  dec_res_ = hex_decode_with_delimiter(decoded_, 4, "0123456789", 10, 0);
+  ASSERT_EQ(0U, dec_res_);
+  ASSERT_EQ(0x7f, decoded_[4]);
+}
+
+// Test that decoding non-hex data fails.
+TEST_F(HexEncodeTest, TestDecodeBogusData) {
+  dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), "xyz", 3, 0);
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Test that decoding an odd number of hex characters fails.
+TEST_F(HexEncodeTest, TestDecodeOddHexDigits) {
+  dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), "012", 3, 0);
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Test that decoding a string with too many delimiters fails.
+TEST_F(HexEncodeTest, TestDecodeWithDelimiterTooManyDelimiters) {
+  dec_res_ = hex_decode_with_delimiter(decoded_, 4, "01::23::45::67", 14, ':');
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Test that decoding a string with a leading delimiter fails.
+TEST_F(HexEncodeTest, TestDecodeWithDelimiterLeadingDelimiter) {
+  dec_res_ = hex_decode_with_delimiter(decoded_, 4, ":01:23:45:67", 12, ':');
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Test that decoding a string with a trailing delimiter fails.
+TEST_F(HexEncodeTest, TestDecodeWithDelimiterTrailingDelimiter) {
+  dec_res_ = hex_decode_with_delimiter(decoded_, 4, "01:23:45:67:", 12, ':');
+  ASSERT_EQ(0U, dec_res_);
+}
+
+// Tests counting substrings.
+TEST(TokenizeTest, CountSubstrings) {
+  std::vector<std::string> fields;
+
+  EXPECT_EQ(5ul, tokenize("one two three four five", ' ', &fields));
+  fields.clear();
+  EXPECT_EQ(1ul, tokenize("one", ' ', &fields));
+
+  // Extra spaces should be ignored.
+  fields.clear();
+  EXPECT_EQ(5ul, tokenize("  one    two  three    four five  ", ' ', &fields));
+  fields.clear();
+  EXPECT_EQ(1ul, tokenize("  one  ", ' ', &fields));
+  fields.clear();
+  EXPECT_EQ(0ul, tokenize(" ", ' ', &fields));
+}
+
+// Tests comparing substrings.
+TEST(TokenizeTest, CompareSubstrings) {
+  std::vector<std::string> fields;
+
+  tokenize("find middle one", ' ', &fields);
+  ASSERT_EQ(3ul, fields.size());
+  ASSERT_STREQ("middle", fields.at(1).c_str());
+  fields.clear();
+
+  // Extra spaces should be ignored.
+  tokenize("  find   middle  one    ", ' ', &fields);
+  ASSERT_EQ(3ul, fields.size());
+  ASSERT_STREQ("middle", fields.at(1).c_str());
+  fields.clear();
+  tokenize(" ", ' ', &fields);
+  ASSERT_EQ(0ul, fields.size());
+}
+
+TEST(TokenizeTest, TokenizeAppend) {
+  ASSERT_EQ(0ul, tokenize_append("A B C", ' ', nullptr));
+
+  std::vector<std::string> fields;
+
+  tokenize_append("A B C", ' ', &fields);
+  ASSERT_EQ(3ul, fields.size());
+  ASSERT_STREQ("B", fields.at(1).c_str());
+
+  tokenize_append("D E", ' ', &fields);
+  ASSERT_EQ(5ul, fields.size());
+  ASSERT_STREQ("B", fields.at(1).c_str());
+  ASSERT_STREQ("E", fields.at(4).c_str());
+}
+
+TEST(TokenizeTest, TokenizeWithMarks) {
+  ASSERT_EQ(0ul, tokenize("D \"A B", ' ', '(', ')', nullptr));
+
+  std::vector<std::string> fields;
+  tokenize("A B C", ' ', '"', '"', &fields);
+  ASSERT_EQ(3ul, fields.size());
+  ASSERT_STREQ("C", fields.at(2).c_str());
+
+  tokenize("\"A B\" C", ' ', '"', '"', &fields);
+  ASSERT_EQ(2ul, fields.size());
+  ASSERT_STREQ("A B", fields.at(0).c_str());
+
+  tokenize("D \"A B\" C", ' ', '"', '"', &fields);
+  ASSERT_EQ(3ul, fields.size());
+  ASSERT_STREQ("D", fields.at(0).c_str());
+  ASSERT_STREQ("A B", fields.at(1).c_str());
+
+  tokenize("D \"A B\" C \"E F\"", ' ', '"', '"', &fields);
+  ASSERT_EQ(4ul, fields.size());
+  ASSERT_STREQ("D", fields.at(0).c_str());
+  ASSERT_STREQ("A B", fields.at(1).c_str());
+  ASSERT_STREQ("E F", fields.at(3).c_str());
+
+  // No matching marks.
+  tokenize("D \"A B", ' ', '"', '"', &fields);
+  ASSERT_EQ(3ul, fields.size());
+  ASSERT_STREQ("D", fields.at(0).c_str());
+  ASSERT_STREQ("\"A", fields.at(1).c_str());
+
+  tokenize("D (A B) C (E F) G", ' ', '(', ')', &fields);
+  ASSERT_EQ(5ul, fields.size());
+  ASSERT_STREQ("D", fields.at(0).c_str());
+  ASSERT_STREQ("A B", fields.at(1).c_str());
+  ASSERT_STREQ("E F", fields.at(3).c_str());
+}
+
+TEST(TokenizeTest, TokenizeWithEmptyTokens) {
+  std::vector<std::string> fields;
+  EXPECT_EQ(3ul, tokenize_with_empty_tokens("a.b.c", '.', &fields));
+  EXPECT_EQ("a", fields[0]);
+  EXPECT_EQ("b", fields[1]);
+  EXPECT_EQ("c", fields[2]);
+
+  EXPECT_EQ(3ul, tokenize_with_empty_tokens("..c", '.', &fields));
+  EXPECT_TRUE(fields[0].empty());
+  EXPECT_TRUE(fields[1].empty());
+  EXPECT_EQ("c", fields[2]);
+
+  EXPECT_EQ(1ul, tokenize_with_empty_tokens("", '.', &fields));
+  EXPECT_TRUE(fields[0].empty());
+}
+
+TEST(TokenizeFirstTest, NoLeadingSpaces) {
+  std::string token;
+  std::string rest;
+
+  ASSERT_TRUE(tokenize_first("A &*${}", ' ', &token, &rest));
+  ASSERT_STREQ("A", token.c_str());
+  ASSERT_STREQ("&*${}", rest.c_str());
+
+  ASSERT_TRUE(tokenize_first("A B& *${}", ' ', &token, &rest));
+  ASSERT_STREQ("A", token.c_str());
+  ASSERT_STREQ("B& *${}", rest.c_str());
+
+  ASSERT_TRUE(tokenize_first("A    B& *${}    ", ' ', &token, &rest));
+  ASSERT_STREQ("A", token.c_str());
+  ASSERT_STREQ("B& *${}    ", rest.c_str());
+}
+
+TEST(TokenizeFirstTest, LeadingSpaces) {
+  std::string token;
+  std::string rest;
+
+  ASSERT_TRUE(tokenize_first("     A B C", ' ', &token, &rest));
+  ASSERT_STREQ("", token.c_str());
+  ASSERT_STREQ("A B C", rest.c_str());
+
+  ASSERT_TRUE(tokenize_first("     A    B   C    ", ' ', &token, &rest));
+  ASSERT_STREQ("", token.c_str());
+  ASSERT_STREQ("A    B   C    ", rest.c_str());
+}
+
+TEST(TokenizeFirstTest, SingleToken) {
+  std::string token;
+  std::string rest;
+
+  // In the case where we cannot find delimiter the whole string is a token.
+  ASSERT_FALSE(tokenize_first("ABC", ' ', &token, &rest));
+
+  ASSERT_TRUE(tokenize_first("ABC    ", ' ', &token, &rest));
+  ASSERT_STREQ("ABC", token.c_str());
+  ASSERT_STREQ("", rest.c_str());
+
+  ASSERT_TRUE(tokenize_first("    ABC    ", ' ', &token, &rest));
+  ASSERT_STREQ("", token.c_str());
+  ASSERT_STREQ("ABC    ", rest.c_str());
+}
+
+// Tests counting substrings.
+TEST(SplitTest, CountSubstrings) {
+  std::vector<std::string> fields;
+
+  EXPECT_EQ(5ul, split("one,two,three,four,five", ',', &fields));
+  fields.clear();
+  EXPECT_EQ(1ul, split("one", ',', &fields));
+
+  // Empty fields between commas count.
+  fields.clear();
+  EXPECT_EQ(5ul, split("one,,three,four,five", ',', &fields));
+  fields.clear();
+  EXPECT_EQ(3ul, split(",three,", ',', &fields));
+  fields.clear();
+  EXPECT_EQ(1ul, split("", ',', &fields));
+}
+
+// Tests comparing substrings.
+TEST(SplitTest, CompareSubstrings) {
+  std::vector<std::string> fields;
+
+  split("find,middle,one", ',', &fields);
+  ASSERT_EQ(3ul, fields.size());
+  ASSERT_STREQ("middle", fields.at(1).c_str());
+  fields.clear();
+
+  // Empty fields between commas count.
+  split("find,,middle,one", ',', &fields);
+  ASSERT_EQ(4ul, fields.size());
+  ASSERT_STREQ("middle", fields.at(2).c_str());
+  fields.clear();
+  split("", ',', &fields);
+  ASSERT_EQ(1ul, fields.size());
+  ASSERT_STREQ("", fields.at(0).c_str());
+}
+
+TEST(BoolTest, DecodeValid) {
+  bool value;
+  EXPECT_TRUE(FromString("true", &value));
+  EXPECT_TRUE(value);
+  EXPECT_TRUE(FromString("true,", &value));
+  EXPECT_TRUE(value);
+  EXPECT_TRUE(FromString("true , true", &value));
+  EXPECT_TRUE(value);
+  EXPECT_TRUE(FromString("true ,\n false", &value));
+  EXPECT_TRUE(value);
+  EXPECT_TRUE(FromString("  true  \n", &value));
+  EXPECT_TRUE(value);
+
+  EXPECT_TRUE(FromString("false", &value));
+  EXPECT_FALSE(value);
+  EXPECT_TRUE(FromString("  false ", &value));
+  EXPECT_FALSE(value);
+  EXPECT_TRUE(FromString("  false, ", &value));
+  EXPECT_FALSE(value);
+
+  EXPECT_TRUE(FromString<bool>("true\n"));
+  EXPECT_FALSE(FromString<bool>("false\n"));
+}
+
+TEST(BoolTest, DecodeInvalid) {
+  bool value;
+  EXPECT_FALSE(FromString("True", &value));
+  EXPECT_FALSE(FromString("TRUE", &value));
+  EXPECT_FALSE(FromString("False", &value));
+  EXPECT_FALSE(FromString("FALSE", &value));
+  EXPECT_FALSE(FromString("0", &value));
+  EXPECT_FALSE(FromString("1", &value));
+  EXPECT_FALSE(FromString("0,", &value));
+  EXPECT_FALSE(FromString("1,", &value));
+  EXPECT_FALSE(FromString("1,0", &value));
+  EXPECT_FALSE(FromString("1.", &value));
+  EXPECT_FALSE(FromString("1.0", &value));
+  EXPECT_FALSE(FromString("", &value));
+  EXPECT_FALSE(FromString<bool>("false\nfalse"));
+}
+
+TEST(BoolTest, RoundTrip) {
+  bool value;
+  EXPECT_TRUE(FromString(ToString(true), &value));
+  EXPECT_TRUE(value);
+  EXPECT_TRUE(FromString(ToString(false), &value));
+  EXPECT_FALSE(value);
+}
+
+}  // namespace rtc
diff --git a/base/stringize_macros.h b/base/stringize_macros.h
index 5f8a5b1..7e2f44d 100644
--- a/base/stringize_macros.h
+++ b/base/stringize_macros.h
@@ -18,9 +18,21 @@
 #ifndef WEBRTC_BASE_STRINGIZE_MACROS_H_
 #define WEBRTC_BASE_STRINGIZE_MACROS_H_
 
+// This is not very useful as it does not expand defined symbols if
+// called directly. Use its counterpart without the _NO_EXPANSION
+// suffix, below.
+#define STRINGIZE_NO_EXPANSION(x) #x
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/stringize_macros.h"
+// Use this to quote the provided parameter, first expanding it if it
+// is a preprocessor symbol.
+//
+// For example, if:
+//   #define A FOO
+//   #define B(x) myobj->FunctionCall(x)
+//
+// Then:
+//   STRINGIZE(A) produces "FOO"
+//   STRINGIZE(B(y)) produces "myobj->FunctionCall(y)"
+#define STRINGIZE(x) STRINGIZE_NO_EXPANSION(x)
 
 #endif  // WEBRTC_BASE_STRINGIZE_MACROS_H_
diff --git a/base/stringize_macros_unittest.cc b/base/stringize_macros_unittest.cc
new file mode 100644
index 0000000..d0ba113
--- /dev/null
+++ b/base/stringize_macros_unittest.cc
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/stringize_macros.h"
+
+#include "webrtc/test/gtest.h"
+
+// Macros as per documentation in header file.
+#define PREPROCESSOR_UTIL_UNITTEST_A FOO
+#define PREPROCESSOR_UTIL_UNITTEST_B(x) myobj->FunctionCall(x)
+#define PREPROCESSOR_UTIL_UNITTEST_C "foo"
+
+TEST(StringizeTest, Ansi) {
+  EXPECT_STREQ(
+      "PREPROCESSOR_UTIL_UNITTEST_A",
+      STRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_A));
+  EXPECT_STREQ(
+      "PREPROCESSOR_UTIL_UNITTEST_B(y)",
+      STRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+  EXPECT_STREQ(
+      "PREPROCESSOR_UTIL_UNITTEST_C",
+      STRINGIZE_NO_EXPANSION(PREPROCESSOR_UTIL_UNITTEST_C));
+
+  EXPECT_STREQ("FOO", STRINGIZE(PREPROCESSOR_UTIL_UNITTEST_A));
+  EXPECT_STREQ("myobj->FunctionCall(y)",
+               STRINGIZE(PREPROCESSOR_UTIL_UNITTEST_B(y)));
+  EXPECT_STREQ("\"foo\"", STRINGIZE(PREPROCESSOR_UTIL_UNITTEST_C));
+}
diff --git a/base/stringutils.cc b/base/stringutils.cc
new file mode 100644
index 0000000..eca1362
--- /dev/null
+++ b/base/stringutils.cc
@@ -0,0 +1,133 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+
+bool memory_check(const void* memory, int c, size_t count) {
+  const char* char_memory = static_cast<const char*>(memory);
+  char char_c = static_cast<char>(c);
+  for (size_t i = 0; i < count; ++i) {
+    if (char_memory[i] != char_c) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool string_match(const char* target, const char* pattern) {
+  while (*pattern) {
+    if (*pattern == '*') {
+      if (!*++pattern) {
+        return true;
+      }
+      while (*target) {
+        if ((toupper(*pattern) == toupper(*target))
+            && string_match(target + 1, pattern + 1)) {
+          return true;
+        }
+        ++target;
+      }
+      return false;
+    } else {
+      if (toupper(*pattern) != toupper(*target)) {
+        return false;
+      }
+      ++target;
+      ++pattern;
+    }
+  }
+  return !*target;
+}
+
+#if defined(WEBRTC_WIN)
+int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n,
+                         CharacterTransformation transformation) {
+  wchar_t c1, c2;
+  while (true) {
+    if (n-- == 0) return 0;
+    c1 = transformation(*s1);
+    // Double check that characters are not UTF-8
+    RTC_DCHECK_LT(*s2, 128);
+    // Note: *s2 gets implicitly promoted to wchar_t
+    c2 = transformation(*s2);
+    if (c1 != c2) return (c1 < c2) ? -1 : 1;
+    if (!c1) return 0;
+    ++s1;
+    ++s2;
+  }
+}
+
+size_t asccpyn(wchar_t* buffer, size_t buflen,
+               const char* source, size_t srclen) {
+  if (buflen <= 0)
+    return 0;
+
+  if (srclen == SIZE_UNKNOWN) {
+    srclen = strlenn(source, buflen - 1);
+  } else if (srclen >= buflen) {
+    srclen = buflen - 1;
+  }
+#if RTC_DCHECK_IS_ON
+  // Double check that characters are not UTF-8
+  for (size_t pos = 0; pos < srclen; ++pos)
+    RTC_DCHECK_LT(source[pos], 128);
+#endif
+  std::copy(source, source + srclen, buffer);
+  buffer[srclen] = 0;
+  return srclen;
+}
+
+#endif  // WEBRTC_WIN
+
+void replace_substrs(const char *search,
+                     size_t search_len,
+                     const char *replace,
+                     size_t replace_len,
+                     std::string *s) {
+  size_t pos = 0;
+  while ((pos = s->find(search, pos, search_len)) != std::string::npos) {
+    s->replace(pos, search_len, replace, replace_len);
+    pos += replace_len;
+  }
+}
+
+bool starts_with(const char *s1, const char *s2) {
+  return strncmp(s1, s2, strlen(s2)) == 0;
+}
+
+bool ends_with(const char *s1, const char *s2) {
+  size_t s1_length = strlen(s1);
+  size_t s2_length = strlen(s2);
+
+  if (s2_length > s1_length) {
+    return false;
+  }
+
+  const char* start = s1 + (s1_length - s2_length);
+  return strncmp(start, s2, s2_length) == 0;
+}
+
+static const char kWhitespace[] = " \n\r\t";
+
+std::string string_trim(const std::string& s) {
+  std::string::size_type first = s.find_first_not_of(kWhitespace);
+  std::string::size_type last  = s.find_last_not_of(kWhitespace);
+
+  if (first == std::string::npos || last == std::string::npos) {
+    return std::string("");
+  }
+
+  return s.substr(first, last - first + 1);
+}
+
+}  // namespace rtc
diff --git a/base/stringutils.h b/base/stringutils.h
index e3b5e07..4c241f0 100644
--- a/base/stringutils.h
+++ b/base/stringutils.h
@@ -8,12 +8,309 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_STRINGUTILS_H_
-#define WEBRTC_BASE_STRINGUTILS_H_
+#ifndef WEBRTC_BASE_STRINGUTILS_H__
+#define WEBRTC_BASE_STRINGUTILS_H__
 
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/stringutils.h"
+#if defined(WEBRTC_WIN)
+#include <malloc.h>
+#include <wchar.h>
+#define alloca _alloca
+#endif  // WEBRTC_WIN 
 
-#endif // WEBRTC_BASE_STRINGUTILS_H_
+#if defined(WEBRTC_POSIX)
+#ifdef BSD
+#include <stdlib.h>
+#else  // BSD
+#include <alloca.h>
+#endif  // !BSD
+#endif  // WEBRTC_POSIX
+
+#include <string>
+
+///////////////////////////////////////////////////////////////////////////////
+// Generic string/memory utilities
+///////////////////////////////////////////////////////////////////////////////
+
+#define STACK_ARRAY(TYPE, LEN) static_cast<TYPE*>(::alloca((LEN)*sizeof(TYPE)))
+
+namespace rtc {
+
+// Complement to memset.  Verifies memory consists of count bytes of value c.
+bool memory_check(const void* memory, int c, size_t count);
+
+// Determines whether the simple wildcard pattern matches target.
+// Alpha characters in pattern match case-insensitively.
+// Asterisks in pattern match 0 or more characters.
+// Ex: string_match("www.TEST.GOOGLE.COM", "www.*.com") -> true
+bool string_match(const char* target, const char* pattern);
+
+}  // namespace rtc
+
+///////////////////////////////////////////////////////////////////////////////
+// Rename a bunch of common string functions so they are consistent across
+// platforms and between char and wchar_t variants.
+// Here is the full list of functions that are unified:
+//  strlen, strcmp, stricmp, strncmp, strnicmp
+//  strchr, vsnprintf, strtoul, tolowercase
+// tolowercase is like tolower, but not compatible with end-of-file value
+//
+// It's not clear if we will ever use wchar_t strings on unix.  In theory,
+// all strings should be Utf8 all the time, except when interfacing with Win32
+// APIs that require Utf16.
+///////////////////////////////////////////////////////////////////////////////
+
+inline char tolowercase(char c) {
+  return static_cast<char>(tolower(c));
+}
+
+#if defined(WEBRTC_WIN)
+
+inline size_t strlen(const wchar_t* s) {
+  return wcslen(s);
+}
+inline int strcmp(const wchar_t* s1, const wchar_t* s2) {
+  return wcscmp(s1, s2);
+}
+inline int stricmp(const wchar_t* s1, const wchar_t* s2) {
+  return _wcsicmp(s1, s2);
+}
+inline int strncmp(const wchar_t* s1, const wchar_t* s2, size_t n) {
+  return wcsncmp(s1, s2, n);
+}
+inline int strnicmp(const wchar_t* s1, const wchar_t* s2, size_t n) {
+  return _wcsnicmp(s1, s2, n);
+}
+inline const wchar_t* strchr(const wchar_t* s, wchar_t c) {
+  return wcschr(s, c);
+}
+inline const wchar_t* strstr(const wchar_t* haystack, const wchar_t* needle) {
+  return wcsstr(haystack, needle);
+}
+#ifndef vsnprintf
+inline int vsnprintf(wchar_t* buf, size_t n, const wchar_t* fmt, va_list args) {
+  return _vsnwprintf(buf, n, fmt, args);
+}
+#endif // !vsnprintf
+inline unsigned long strtoul(const wchar_t* snum, wchar_t** end, int base) {
+  return wcstoul(snum, end, base);
+}
+inline wchar_t tolowercase(wchar_t c) {
+  return static_cast<wchar_t>(towlower(c));
+}
+
+#endif  // WEBRTC_WIN 
+
+#if defined(WEBRTC_POSIX)
+
+inline int _stricmp(const char* s1, const char* s2) {
+  return strcasecmp(s1, s2);
+}
+inline int _strnicmp(const char* s1, const char* s2, size_t n) {
+  return strncasecmp(s1, s2, n);
+}
+
+#endif // WEBRTC_POSIX
+
+///////////////////////////////////////////////////////////////////////////////
+// Traits simplifies porting string functions to be CTYPE-agnostic
+///////////////////////////////////////////////////////////////////////////////
+
+namespace rtc {
+
+const size_t SIZE_UNKNOWN = static_cast<size_t>(-1);
+
+template<class CTYPE>
+struct Traits {
+  // STL string type
+  //typedef XXX string;
+  // Null-terminated string
+  //inline static const CTYPE* empty_str();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// String utilities which work with char or wchar_t
+///////////////////////////////////////////////////////////////////////////////
+
+template <class CTYPE>
+inline const CTYPE* nonnull(const CTYPE* str, const CTYPE* def_str = nullptr) {
+  return str ? str : (def_str ? def_str : Traits<CTYPE>::empty_str());
+}
+
+template<class CTYPE>
+const CTYPE* strchr(const CTYPE* str, const CTYPE* chs) {
+  for (size_t i=0; str[i]; ++i) {
+    for (size_t j=0; chs[j]; ++j) {
+      if (str[i] == chs[j]) {
+        return str + i;
+      }
+    }
+  }
+  return 0;
+}
+
+template<class CTYPE>
+const CTYPE* strchrn(const CTYPE* str, size_t slen, CTYPE ch) {
+  for (size_t i=0; i<slen && str[i]; ++i) {
+    if (str[i] == ch) {
+      return str + i;
+    }
+  }
+  return 0;
+}
+
+template<class CTYPE>
+size_t strlenn(const CTYPE* buffer, size_t buflen) {
+  size_t bufpos = 0;
+  while (buffer[bufpos] && (bufpos < buflen)) {
+    ++bufpos;
+  }
+  return bufpos;
+}
+
+// Safe versions of strncpy, strncat, snprintf and vsnprintf that always
+// null-terminate.
+
+template<class CTYPE>
+size_t strcpyn(CTYPE* buffer, size_t buflen,
+               const CTYPE* source, size_t srclen = SIZE_UNKNOWN) {
+  if (buflen <= 0)
+    return 0;
+
+  if (srclen == SIZE_UNKNOWN) {
+    srclen = strlenn(source, buflen - 1);
+  } else if (srclen >= buflen) {
+    srclen = buflen - 1;
+  }
+  memcpy(buffer, source, srclen * sizeof(CTYPE));
+  buffer[srclen] = 0;
+  return srclen;
+}
+
+template<class CTYPE>
+size_t strcatn(CTYPE* buffer, size_t buflen,
+               const CTYPE* source, size_t srclen = SIZE_UNKNOWN) {
+  if (buflen <= 0)
+    return 0;
+
+  size_t bufpos = strlenn(buffer, buflen - 1);
+  return bufpos + strcpyn(buffer + bufpos, buflen - bufpos, source, srclen);
+}
+
+// Some compilers (clang specifically) require vsprintfn be defined before
+// sprintfn.
+template<class CTYPE>
+size_t vsprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format,
+                 va_list args) {
+  int len = vsnprintf(buffer, buflen, format, args);
+  if ((len < 0) || (static_cast<size_t>(len) >= buflen)) {
+    len = static_cast<int>(buflen - 1);
+    buffer[len] = 0;
+  }
+  return len;
+}
+
+template<class CTYPE>
+size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...);
+template<class CTYPE>
+size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...) {
+  va_list args;
+  va_start(args, format);
+  size_t len = vsprintfn(buffer, buflen, format, args);
+  va_end(args);
+  return len;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Allow safe comparing and copying ascii (not UTF-8) with both wide and
+// non-wide character strings.
+///////////////////////////////////////////////////////////////////////////////
+
+inline int asccmp(const char* s1, const char* s2) {
+  return strcmp(s1, s2);
+}
+inline int ascicmp(const char* s1, const char* s2) {
+  return _stricmp(s1, s2);
+}
+inline int ascncmp(const char* s1, const char* s2, size_t n) {
+  return strncmp(s1, s2, n);
+}
+inline int ascnicmp(const char* s1, const char* s2, size_t n) {
+  return _strnicmp(s1, s2, n);
+}
+inline size_t asccpyn(char* buffer, size_t buflen,
+                      const char* source, size_t srclen = SIZE_UNKNOWN) {
+  return strcpyn(buffer, buflen, source, srclen);
+}
+
+#if defined(WEBRTC_WIN)
+
+typedef wchar_t(*CharacterTransformation)(wchar_t);
+inline wchar_t identity(wchar_t c) { return c; }
+int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n,
+                         CharacterTransformation transformation);
+
+inline int asccmp(const wchar_t* s1, const char* s2) {
+  return ascii_string_compare(s1, s2, static_cast<size_t>(-1), identity);
+}
+inline int ascicmp(const wchar_t* s1, const char* s2) {
+  return ascii_string_compare(s1, s2, static_cast<size_t>(-1), tolowercase);
+}
+inline int ascncmp(const wchar_t* s1, const char* s2, size_t n) {
+  return ascii_string_compare(s1, s2, n, identity);
+}
+inline int ascnicmp(const wchar_t* s1, const char* s2, size_t n) {
+  return ascii_string_compare(s1, s2, n, tolowercase);
+}
+size_t asccpyn(wchar_t* buffer, size_t buflen,
+               const char* source, size_t srclen = SIZE_UNKNOWN);
+
+#endif  // WEBRTC_WIN 
+
+///////////////////////////////////////////////////////////////////////////////
+// Traits<char> specializations
+///////////////////////////////////////////////////////////////////////////////
+
+template<>
+struct Traits<char> {
+  typedef std::string string;
+  inline static const char* empty_str() { return ""; }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Traits<wchar_t> specializations (Windows only, currently)
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(WEBRTC_WIN)
+
+template<>
+struct Traits<wchar_t> {
+  typedef std::wstring string;
+  inline static const wchar_t* empty_str() { return L""; }
+};
+
+#endif  // WEBRTC_WIN 
+
+// Replaces all occurrences of "search" with "replace".
+void replace_substrs(const char *search,
+                     size_t search_len,
+                     const char *replace,
+                     size_t replace_len,
+                     std::string *s);
+
+// True iff s1 starts with s2.
+bool starts_with(const char *s1, const char *s2);
+
+// True iff s1 ends with s2.
+bool ends_with(const char *s1, const char *s2);
+
+// Remove leading and trailing whitespaces.
+std::string string_trim(const std::string& s);
+
+}  // namespace rtc
+
+#endif // WEBRTC_BASE_STRINGUTILS_H__
diff --git a/base/stringutils_unittest.cc b/base/stringutils_unittest.cc
new file mode 100644
index 0000000..7d5b8e2
--- /dev/null
+++ b/base/stringutils_unittest.cc
@@ -0,0 +1,108 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/stringutils.h"
+
+namespace rtc {
+
+// Tests for string_match().
+
+TEST(string_matchTest, Matches) {
+  EXPECT_TRUE( string_match("A.B.C.D", "a.b.c.d"));
+  EXPECT_TRUE( string_match("www.TEST.GOOGLE.COM", "www.*.com"));
+  EXPECT_TRUE( string_match("127.0.0.1",  "12*.0.*1"));
+  EXPECT_TRUE( string_match("127.1.0.21", "12*.0.*1"));
+  EXPECT_FALSE(string_match("127.0.0.0",  "12*.0.*1"));
+  EXPECT_FALSE(string_match("127.0.0.0",  "12*.0.*1"));
+  EXPECT_FALSE(string_match("127.1.1.21", "12*.0.*1"));
+}
+
+// It's not clear if we will ever use wchar_t strings on unix.  In theory,
+// all strings should be Utf8 all the time, except when interfacing with Win32
+// APIs that require Utf16.
+
+#if defined(WEBRTC_WIN)
+
+// Tests for ascii_string_compare().
+
+// Tests null input.
+TEST(ascii_string_compareTest, NullInput) {
+  // The following results in an access violation in
+  // ascii_string_compare.  Is this a bug or by design?  stringutils.h
+  // should document the expected behavior in this case.
+
+  // EXPECT_EQ(0, ascii_string_compare(nullptr, nullptr, 1, identity));
+}
+
+// Tests comparing two strings of different lengths.
+TEST(ascii_string_compareTest, DifferentLengths) {
+  EXPECT_EQ(-1, ascii_string_compare(L"Test", "Test1", 5, identity));
+}
+
+// Tests the case where the buffer size is smaller than the string
+// lengths.
+TEST(ascii_string_compareTest, SmallBuffer) {
+  EXPECT_EQ(0, ascii_string_compare(L"Test", "Test1", 3, identity));
+}
+
+// Tests the case where the buffer is not full.
+TEST(ascii_string_compareTest, LargeBuffer) {
+  EXPECT_EQ(0, ascii_string_compare(L"Test", "Test", 10, identity));
+}
+
+// Tests comparing two eqaul strings.
+TEST(ascii_string_compareTest, Equal) {
+  EXPECT_EQ(0, ascii_string_compare(L"Test", "Test", 5, identity));
+  EXPECT_EQ(0, ascii_string_compare(L"TeSt", "tEsT", 5, tolowercase));
+}
+
+// Tests comparing a smller string to a larger one.
+TEST(ascii_string_compareTest, LessThan) {
+  EXPECT_EQ(-1, ascii_string_compare(L"abc", "abd", 4, identity));
+  EXPECT_EQ(-1, ascii_string_compare(L"ABC", "abD", 5, tolowercase));
+}
+
+// Tests comparing a larger string to a smaller one.
+TEST(ascii_string_compareTest, GreaterThan) {
+  EXPECT_EQ(1, ascii_string_compare(L"xyz", "xy", 5, identity));
+  EXPECT_EQ(1, ascii_string_compare(L"abc", "ABB", 5, tolowercase));
+}
+#endif  // WEBRTC_WIN 
+
+TEST(string_trim_Test, Trimming) {
+  EXPECT_EQ("temp", string_trim("\n\r\t temp \n\r\t"));
+  EXPECT_EQ("temp\n\r\t temp", string_trim(" temp\n\r\t temp "));
+  EXPECT_EQ("temp temp", string_trim("temp temp"));
+  EXPECT_EQ("", string_trim(" \r\n\t"));
+  EXPECT_EQ("", string_trim(""));
+}
+
+TEST(string_startsTest, StartsWith) {
+  EXPECT_TRUE(starts_with("foobar", "foo"));
+  EXPECT_TRUE(starts_with("foobar", "foobar"));
+  EXPECT_TRUE(starts_with("foobar", ""));
+  EXPECT_TRUE(starts_with("", ""));
+  EXPECT_FALSE(starts_with("foobar", "bar"));
+  EXPECT_FALSE(starts_with("foobar", "foobarbaz"));
+  EXPECT_FALSE(starts_with("", "f"));
+}
+
+TEST(string_endsTest, EndsWith) {
+  EXPECT_TRUE(ends_with("foobar", "bar"));
+  EXPECT_TRUE(ends_with("foobar", "foobar"));
+  EXPECT_TRUE(ends_with("foobar", ""));
+  EXPECT_TRUE(ends_with("", ""));
+  EXPECT_FALSE(ends_with("foobar", "foo"));
+  EXPECT_FALSE(ends_with("foobar", "foobarbaz"));
+  EXPECT_FALSE(ends_with("", "f"));
+}
+
+} // namespace rtc
diff --git a/base/swap_queue.h b/base/swap_queue.h
index 7111147..acb16b4 100644
--- a/base/swap_queue.h
+++ b/base/swap_queue.h
@@ -11,9 +11,201 @@
 #ifndef WEBRTC_BASE_SWAP_QUEUE_H_
 #define WEBRTC_BASE_SWAP_QUEUE_H_
 
+#include <algorithm>
+#include <utility>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/swap_queue.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/criticalsection.h"
+
+namespace webrtc {
+
+namespace internal {
+
+// (Internal; please don't use outside this file.)
+template <typename T>
+bool NoopSwapQueueItemVerifierFunction(const T&) {
+  return true;
+}
+
+}  // namespace internal
+
+// Functor to use when supplying a verifier function for the queue.
+template <typename T,
+          bool (*QueueItemVerifierFunction)(const T&) =
+              internal::NoopSwapQueueItemVerifierFunction>
+class SwapQueueItemVerifier {
+ public:
+  bool operator()(const T& t) const { return QueueItemVerifierFunction(t); }
+};
+
+// This class is a fixed-size queue. A producer calls Insert() to insert
+// an element of type T at the back of the queue, and a consumer calls
+// Remove() to remove an element from the front of the queue. It's safe
+// for the producer(s) and the consumer(s) to access the queue
+// concurrently, from different threads.
+//
+// To avoid the construction, copying, and destruction of Ts that a naive
+// queue implementation would require, for each "full" T passed from
+// producer to consumer, SwapQueue<T> passes an "empty" T in the other
+// direction (an "empty" T is one that contains nothing of value for the
+// consumer). This bidirectional movement is implemented with swap().
+//
+// // Create queue:
+// Bottle proto(568);  // Prepare an empty Bottle. Heap allocates space for
+//                     // 568 ml.
+// SwapQueue<Bottle> q(N, proto);  // Init queue with N copies of proto.
+//                                 // Each copy allocates on the heap.
+// // Producer pseudo-code:
+// Bottle b(568); // Prepare an empty Bottle. Heap allocates space for 568 ml.
+// loop {
+//   b.Fill(amount);  // Where amount <= 568 ml.
+//   q.Insert(&b);    // Swap our full Bottle for an empty one from q.
+// }
+//
+// // Consumer pseudo-code:
+// Bottle b(568);  // Prepare an empty Bottle. Heap allocates space for 568 ml.
+// loop {
+//   q.Remove(&b); // Swap our empty Bottle for the next-in-line full Bottle.
+//   Drink(&b);
+// }
+//
+// For a well-behaved Bottle class, there are no allocations in the
+// producer, since it just fills an empty Bottle that's already large
+// enough; no deallocations in the consumer, since it returns each empty
+// Bottle to the queue after having drunk it; and no copies along the
+// way, since the queue uses swap() everywhere to move full Bottles in
+// one direction and empty ones in the other.
+template <typename T, typename QueueItemVerifier = SwapQueueItemVerifier<T>>
+class SwapQueue {
+ public:
+  // Creates a queue of size size and fills it with default constructed Ts.
+  explicit SwapQueue(size_t size) : queue_(size) {
+    RTC_DCHECK(VerifyQueueSlots());
+  }
+
+  // Same as above and accepts an item verification functor.
+  SwapQueue(size_t size, const QueueItemVerifier& queue_item_verifier)
+      : queue_item_verifier_(queue_item_verifier), queue_(size) {
+    RTC_DCHECK(VerifyQueueSlots());
+  }
+
+  // Creates a queue of size size and fills it with copies of prototype.
+  SwapQueue(size_t size, const T& prototype) : queue_(size, prototype) {
+    RTC_DCHECK(VerifyQueueSlots());
+  }
+
+  // Same as above and accepts an item verification functor.
+  SwapQueue(size_t size,
+            const T& prototype,
+            const QueueItemVerifier& queue_item_verifier)
+      : queue_item_verifier_(queue_item_verifier), queue_(size, prototype) {
+    RTC_DCHECK(VerifyQueueSlots());
+  }
+
+  // Resets the queue to have zero content wile maintaining the queue size.
+  void Clear() {
+    rtc::CritScope cs(&crit_queue_);
+    next_write_index_ = 0;
+    next_read_index_ = 0;
+    num_elements_ = 0;
+  }
+
+  // Inserts a "full" T at the back of the queue by swapping *input with an
+  // "empty" T from the queue.
+  // Returns true if the item was inserted or false if not (the queue was full).
+  // When specified, the T given in *input must pass the ItemVerifier() test.
+  // The contents of *input after the call are then also guaranteed to pass the
+  // ItemVerifier() test.
+  bool Insert(T* input) RTC_WARN_UNUSED_RESULT {
+    RTC_DCHECK(input);
+
+    rtc::CritScope cs(&crit_queue_);
+
+    RTC_DCHECK(queue_item_verifier_(*input));
+
+    if (num_elements_ == queue_.size()) {
+      return false;
+    }
+
+    using std::swap;
+    swap(*input, queue_[next_write_index_]);
+
+    ++next_write_index_;
+    if (next_write_index_ == queue_.size()) {
+      next_write_index_ = 0;
+    }
+
+    ++num_elements_;
+
+    RTC_DCHECK_LT(next_write_index_, queue_.size());
+    RTC_DCHECK_LE(num_elements_, queue_.size());
+
+    return true;
+  }
+
+  // Removes the frontmost "full" T from the queue by swapping it with
+  // the "empty" T in *output.
+  // Returns true if an item could be removed or false if not (the queue was
+  // empty). When specified, The T given in *output must pass the ItemVerifier()
+  // test and the contents of *output after the call are then also guaranteed to
+  // pass the ItemVerifier() test.
+  bool Remove(T* output) RTC_WARN_UNUSED_RESULT {
+    RTC_DCHECK(output);
+
+    rtc::CritScope cs(&crit_queue_);
+
+    RTC_DCHECK(queue_item_verifier_(*output));
+
+    if (num_elements_ == 0) {
+      return false;
+    }
+
+    using std::swap;
+    swap(*output, queue_[next_read_index_]);
+
+    ++next_read_index_;
+    if (next_read_index_ == queue_.size()) {
+      next_read_index_ = 0;
+    }
+
+    --num_elements_;
+
+    RTC_DCHECK_LT(next_read_index_, queue_.size());
+    RTC_DCHECK_LE(num_elements_, queue_.size());
+
+    return true;
+  }
+
+ private:
+  // Verify that the queue slots complies with the ItemVerifier test.
+  bool VerifyQueueSlots() {
+    rtc::CritScope cs(&crit_queue_);
+    for (const auto& v : queue_) {
+      RTC_DCHECK(queue_item_verifier_(v));
+    }
+    return true;
+  }
+
+  rtc::CriticalSection crit_queue_;
+
+  // TODO(peah): Change this to use std::function() once we can use C++11 std
+  // lib.
+  QueueItemVerifier queue_item_verifier_ GUARDED_BY(crit_queue_);
+
+  // (next_read_index_ + num_elements_) % queue_.size() =
+  //  next_write_index_
+  size_t next_write_index_ GUARDED_BY(crit_queue_) = 0;
+  size_t next_read_index_ GUARDED_BY(crit_queue_) = 0;
+  size_t num_elements_ GUARDED_BY(crit_queue_) = 0;
+
+  // queue_.size() is constant.
+  std::vector<T> queue_ GUARDED_BY(crit_queue_);
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(SwapQueue);
+};
+
+}  // namespace webrtc
 
 #endif  // WEBRTC_BASE_SWAP_QUEUE_H_
diff --git a/base/swap_queue_unittest.cc b/base/swap_queue_unittest.cc
new file mode 100644
index 0000000..ff00626
--- /dev/null
+++ b/base/swap_queue_unittest.cc
@@ -0,0 +1,225 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/swap_queue.h"
+
+#include <vector>
+
+#include "webrtc/test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+// Test parameter for the basic sample based SwapQueue Tests.
+const size_t kChunkSize = 3;
+
+// Queue item verification function for the vector test.
+bool LengthVerifierFunction(const std::vector<int>& v) {
+  return v.size() == kChunkSize;
+}
+
+// Queue item verifier for the vector test.
+class LengthVerifierFunctor {
+ public:
+  explicit LengthVerifierFunctor(size_t length) : length_(length) {}
+
+  bool operator()(const std::vector<int>& v) const {
+    return v.size() == length_;
+  }
+
+ private:
+  size_t length_;
+};
+
+}  // anonymous namespace
+
+TEST(SwapQueueTest, BasicOperation) {
+  std::vector<int> i(kChunkSize, 0);
+  SwapQueue<std::vector<int>> queue(2, i);
+
+  EXPECT_TRUE(queue.Insert(&i));
+  EXPECT_EQ(i.size(), kChunkSize);
+  EXPECT_TRUE(queue.Insert(&i));
+  EXPECT_EQ(i.size(), kChunkSize);
+  EXPECT_TRUE(queue.Remove(&i));
+  EXPECT_EQ(i.size(), kChunkSize);
+  EXPECT_TRUE(queue.Remove(&i));
+  EXPECT_EQ(i.size(), kChunkSize);
+}
+
+TEST(SwapQueueTest, FullQueue) {
+  SwapQueue<int> queue(2);
+
+  // Fill the queue.
+  int i = 0;
+  EXPECT_TRUE(queue.Insert(&i));
+  i = 1;
+  EXPECT_TRUE(queue.Insert(&i));
+
+  // Ensure that the value is not swapped when doing an Insert
+  // on a full queue.
+  i = 2;
+  EXPECT_FALSE(queue.Insert(&i));
+  EXPECT_EQ(i, 2);
+
+  // Ensure that the Insert didn't overwrite anything in the queue.
+  EXPECT_TRUE(queue.Remove(&i));
+  EXPECT_EQ(i, 0);
+  EXPECT_TRUE(queue.Remove(&i));
+  EXPECT_EQ(i, 1);
+}
+
+TEST(SwapQueueTest, EmptyQueue) {
+  SwapQueue<int> queue(2);
+  int i = 0;
+  EXPECT_FALSE(queue.Remove(&i));
+  EXPECT_TRUE(queue.Insert(&i));
+  EXPECT_TRUE(queue.Remove(&i));
+  EXPECT_FALSE(queue.Remove(&i));
+}
+
+TEST(SwapQueueTest, Clear) {
+  SwapQueue<int> queue(2);
+  int i = 0;
+
+  // Fill the queue.
+  EXPECT_TRUE(queue.Insert(&i));
+  EXPECT_TRUE(queue.Insert(&i));
+
+  // Ensure full queue.
+  EXPECT_FALSE(queue.Insert(&i));
+
+  // Empty the queue.
+  queue.Clear();
+
+  // Ensure that the queue is empty
+  EXPECT_FALSE(queue.Remove(&i));
+
+  // Ensure that the queue is no longer full.
+  EXPECT_TRUE(queue.Insert(&i));
+}
+
+TEST(SwapQueueTest, SuccessfulItemVerifyFunction) {
+  std::vector<int> template_element(kChunkSize);
+  SwapQueue<std::vector<int>,
+            SwapQueueItemVerifier<std::vector<int>, LengthVerifierFunction>>
+      queue(2, template_element);
+  std::vector<int> valid_chunk(kChunkSize, 0);
+
+  EXPECT_TRUE(queue.Insert(&valid_chunk));
+  EXPECT_EQ(valid_chunk.size(), kChunkSize);
+  EXPECT_TRUE(queue.Remove(&valid_chunk));
+  EXPECT_EQ(valid_chunk.size(), kChunkSize);
+}
+
+TEST(SwapQueueTest, SuccessfulItemVerifyFunctor) {
+  std::vector<int> template_element(kChunkSize);
+  LengthVerifierFunctor verifier(kChunkSize);
+  SwapQueue<std::vector<int>, LengthVerifierFunctor> queue(2, template_element,
+                                                           verifier);
+  std::vector<int> valid_chunk(kChunkSize, 0);
+
+  EXPECT_TRUE(queue.Insert(&valid_chunk));
+  EXPECT_EQ(valid_chunk.size(), kChunkSize);
+  EXPECT_TRUE(queue.Remove(&valid_chunk));
+  EXPECT_EQ(valid_chunk.size(), kChunkSize);
+}
+
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+TEST(SwapQueueTest, UnsuccessfulItemVerifyFunctor) {
+  // Queue item verifier for the test.
+  auto minus_2_verifier = [](const int& i) { return i > -2; };
+  SwapQueue<int, decltype(minus_2_verifier)> queue(2, minus_2_verifier);
+
+  int valid_value = 1;
+  int invalid_value = -4;
+  EXPECT_TRUE(queue.Insert(&valid_value));
+  EXPECT_TRUE(queue.Remove(&valid_value));
+  bool result;
+  EXPECT_DEATH(result = queue.Insert(&invalid_value), "");
+}
+
+TEST(SwapQueueTest, UnSuccessfulItemVerifyInsert) {
+  std::vector<int> template_element(kChunkSize);
+  SwapQueue<std::vector<int>,
+            SwapQueueItemVerifier<std::vector<int>, &LengthVerifierFunction>>
+      queue(2, template_element);
+  std::vector<int> invalid_chunk(kChunkSize - 1, 0);
+  bool result;
+  EXPECT_DEATH(result = queue.Insert(&invalid_chunk), "");
+}
+
+TEST(SwapQueueTest, UnSuccessfulItemVerifyRemove) {
+  std::vector<int> template_element(kChunkSize);
+  SwapQueue<std::vector<int>,
+            SwapQueueItemVerifier<std::vector<int>, &LengthVerifierFunction>>
+      queue(2, template_element);
+  std::vector<int> invalid_chunk(kChunkSize - 1, 0);
+  std::vector<int> valid_chunk(kChunkSize, 0);
+  EXPECT_TRUE(queue.Insert(&valid_chunk));
+  EXPECT_EQ(valid_chunk.size(), kChunkSize);
+  bool result;
+  EXPECT_DEATH(result = queue.Remove(&invalid_chunk), "");
+}
+#endif
+
+TEST(SwapQueueTest, VectorContentTest) {
+  const size_t kQueueSize = 10;
+  const size_t kFrameLength = 160;
+  const size_t kDataLength = kQueueSize * kFrameLength;
+  std::vector<int16_t> buffer_reader(kFrameLength, 0);
+  std::vector<int16_t> buffer_writer(kFrameLength, 0);
+  SwapQueue<std::vector<int16_t>> queue(kQueueSize,
+                                        std::vector<int16_t>(kFrameLength));
+  std::vector<int16_t> samples(kDataLength);
+
+  for (size_t k = 0; k < kDataLength; k++) {
+    samples[k] = k % 9;
+  }
+
+  for (size_t k = 0; k < kQueueSize; k++) {
+    buffer_writer.clear();
+    buffer_writer.insert(buffer_writer.end(), &samples[0] + k * kFrameLength,
+                         &samples[0] + (k + 1) * kFrameLength);
+
+    EXPECT_TRUE(queue.Insert(&buffer_writer));
+  }
+
+  for (size_t k = 0; k < kQueueSize; k++) {
+    EXPECT_TRUE(queue.Remove(&buffer_reader));
+
+    for (size_t j = 0; j < buffer_reader.size(); j++) {
+      EXPECT_EQ(buffer_reader[j], samples[k * kFrameLength + j]);
+    }
+  }
+}
+
+TEST(SwapQueueTest, ZeroSlotQueue) {
+  SwapQueue<int> queue(0);
+  int i = 42;
+  EXPECT_FALSE(queue.Insert(&i));
+  EXPECT_FALSE(queue.Remove(&i));
+  EXPECT_EQ(i, 42);
+}
+
+TEST(SwapQueueTest, OneSlotQueue) {
+  SwapQueue<int> queue(1);
+  int i = 42;
+  EXPECT_TRUE(queue.Insert(&i));
+  i = 43;
+  EXPECT_FALSE(queue.Insert(&i));
+  EXPECT_EQ(i, 43);
+  EXPECT_TRUE(queue.Remove(&i));
+  EXPECT_EQ(i, 42);
+  EXPECT_FALSE(queue.Remove(&i));
+}
+
+}  // namespace webrtc
diff --git a/base/task_queue.h b/base/task_queue.h
index 12f5cbb..15b31aa 100644
--- a/base/task_queue.h
+++ b/base/task_queue.h
@@ -11,9 +11,296 @@
 #ifndef WEBRTC_BASE_TASK_QUEUE_H_
 #define WEBRTC_BASE_TASK_QUEUE_H_
 
+#include <list>
+#include <memory>
+#include <queue>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/task_queue.h"
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_BUILD_LIBEVENT)
+#include <dispatch/dispatch.h>
+#endif
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/criticalsection.h"
+
+#if defined(WEBRTC_WIN) || defined(WEBRTC_BUILD_LIBEVENT)
+#include "webrtc/base/platform_thread.h"
+#endif
+
+#if defined(WEBRTC_BUILD_LIBEVENT)
+#include "webrtc/base/refcountedobject.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+
+struct event_base;
+struct event;
+#endif
+
+namespace rtc {
+
+// Base interface for asynchronously executed tasks.
+// The interface basically consists of a single function, Run(), that executes
+// on the target queue.  For more details see the Run() method and TaskQueue.
+class QueuedTask {
+ public:
+  QueuedTask() {}
+  virtual ~QueuedTask() {}
+
+  // Main routine that will run when the task is executed on the desired queue.
+  // The task should return |true| to indicate that it should be deleted or
+  // |false| to indicate that the queue should consider ownership of the task
+  // having been transferred.  Returning |false| can be useful if a task has
+  // re-posted itself to a different queue or is otherwise being re-used.
+  virtual bool Run() = 0;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(QueuedTask);
+};
+
+// Simple implementation of QueuedTask for use with rtc::Bind and lambdas.
+template <class Closure>
+class ClosureTask : public QueuedTask {
+ public:
+  explicit ClosureTask(const Closure& closure) : closure_(closure) {}
+
+ private:
+  bool Run() override {
+    closure_();
+    return true;
+  }
+
+  Closure closure_;
+};
+
+// Extends ClosureTask to also allow specifying cleanup code.
+// This is useful when using lambdas if guaranteeing cleanup, even if a task
+// was dropped (queue is too full), is required.
+template <class Closure, class Cleanup>
+class ClosureTaskWithCleanup : public ClosureTask<Closure> {
+ public:
+  ClosureTaskWithCleanup(const Closure& closure, Cleanup cleanup)
+      : ClosureTask<Closure>(closure), cleanup_(cleanup) {}
+  ~ClosureTaskWithCleanup() { cleanup_(); }
+
+ private:
+  Cleanup cleanup_;
+};
+
+// Convenience function to construct closures that can be passed directly
+// to methods that support std::unique_ptr<QueuedTask> but not template
+// based parameters.
+template <class Closure>
+static std::unique_ptr<QueuedTask> NewClosure(const Closure& closure) {
+  return std::unique_ptr<QueuedTask>(new ClosureTask<Closure>(closure));
+}
+
+template <class Closure, class Cleanup>
+static std::unique_ptr<QueuedTask> NewClosure(const Closure& closure,
+                                              const Cleanup& cleanup) {
+  return std::unique_ptr<QueuedTask>(
+      new ClosureTaskWithCleanup<Closure, Cleanup>(closure, cleanup));
+}
+
+// Implements a task queue that asynchronously executes tasks in a way that
+// guarantees that they're executed in FIFO order and that tasks never overlap.
+// Tasks may always execute on the same worker thread and they may not.
+// To DCHECK that tasks are executing on a known task queue, use IsCurrent().
+//
+// Here are some usage examples:
+//
+//   1) Asynchronously running a lambda:
+//
+//     class MyClass {
+//       ...
+//       TaskQueue queue_("MyQueue");
+//     };
+//
+//     void MyClass::StartWork() {
+//       queue_.PostTask([]() { Work(); });
+//     ...
+//
+//   2) Doing work asynchronously on a worker queue and providing a notification
+//      callback on the current queue, when the work has been done:
+//
+//     void MyClass::StartWorkAndLetMeKnowWhenDone(
+//         std::unique_ptr<QueuedTask> callback) {
+//       DCHECK(TaskQueue::Current()) << "Need to be running on a queue";
+//       queue_.PostTaskAndReply([]() { Work(); }, std::move(callback));
+//     }
+//     ...
+//     my_class->StartWorkAndLetMeKnowWhenDone(
+//         NewClosure([]() { LOG(INFO) << "The work is done!";}));
+//
+//   3) Posting a custom task on a timer.  The task posts itself again after
+//      every running:
+//
+//     class TimerTask : public QueuedTask {
+//      public:
+//       TimerTask() {}
+//      private:
+//       bool Run() override {
+//         ++count_;
+//         TaskQueue::Current()->PostDelayedTask(
+//             std::unique_ptr<QueuedTask>(this), 1000);
+//         // Ownership has been transferred to the next occurance,
+//         // so return false to prevent from being deleted now.
+//         return false;
+//       }
+//       int count_ = 0;
+//     };
+//     ...
+//     queue_.PostDelayedTask(
+//         std::unique_ptr<QueuedTask>(new TimerTask()), 1000);
+//
+// For more examples, see task_queue_unittests.cc.
+//
+// A note on destruction:
+//
+// When a TaskQueue is deleted, pending tasks will not be executed but they will
+// be deleted.  The deletion of tasks may happen asynchronously after the
+// TaskQueue itself has been deleted or it may happen synchronously while the
+// TaskQueue instance is being deleted.  This may vary from one OS to the next
+// so assumptions about lifetimes of pending tasks should not be made.
+class LOCKABLE TaskQueue {
+ public:
+  // TaskQueue priority levels. On some platforms these will map to thread
+  // priorities, on others such as Mac and iOS, GCD queue priorities.
+  enum class Priority {
+    NORMAL = 0,
+    HIGH,
+    LOW,
+  };
+
+  explicit TaskQueue(const char* queue_name,
+                     Priority priority = Priority::NORMAL);
+  ~TaskQueue();
+
+  static TaskQueue* Current();
+
+  // Used for DCHECKing the current queue.
+  static bool IsCurrent(const char* queue_name);
+  bool IsCurrent() const;
+
+  // TODO(tommi): For better debuggability, implement RTC_FROM_HERE.
+
+  // Ownership of the task is passed to PostTask.
+  void PostTask(std::unique_ptr<QueuedTask> task);
+  void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
+                        std::unique_ptr<QueuedTask> reply,
+                        TaskQueue* reply_queue);
+  void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
+                        std::unique_ptr<QueuedTask> reply);
+
+  // Schedules a task to execute a specified number of milliseconds from when
+  // the call is made. The precision should be considered as "best effort"
+  // and in some cases, such as on Windows when all high precision timers have
+  // been used up, can be off by as much as 15 millseconds (although 8 would be
+  // more likely). This can be mitigated by limiting the use of delayed tasks.
+  void PostDelayedTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds);
+
+  template <class Closure>
+  void PostTask(const Closure& closure) {
+    PostTask(std::unique_ptr<QueuedTask>(new ClosureTask<Closure>(closure)));
+  }
+
+  // See documentation above for performance expectations.
+  template <class Closure>
+  void PostDelayedTask(const Closure& closure, uint32_t milliseconds) {
+    PostDelayedTask(
+        std::unique_ptr<QueuedTask>(new ClosureTask<Closure>(closure)),
+        milliseconds);
+  }
+
+  template <class Closure1, class Closure2>
+  void PostTaskAndReply(const Closure1& task,
+                        const Closure2& reply,
+                        TaskQueue* reply_queue) {
+    PostTaskAndReply(
+        std::unique_ptr<QueuedTask>(new ClosureTask<Closure1>(task)),
+        std::unique_ptr<QueuedTask>(new ClosureTask<Closure2>(reply)),
+        reply_queue);
+  }
+
+  template <class Closure>
+  void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
+                        const Closure& reply) {
+    PostTaskAndReply(std::move(task), std::unique_ptr<QueuedTask>(
+                                          new ClosureTask<Closure>(reply)));
+  }
+
+  template <class Closure>
+  void PostTaskAndReply(const Closure& task,
+                        std::unique_ptr<QueuedTask> reply) {
+    PostTaskAndReply(
+        std::unique_ptr<QueuedTask>(new ClosureTask<Closure>(task)),
+        std::move(reply));
+  }
+
+  template <class Closure1, class Closure2>
+  void PostTaskAndReply(const Closure1& task, const Closure2& reply) {
+    PostTaskAndReply(
+        std::unique_ptr<QueuedTask>(new ClosureTask<Closure1>(task)),
+        std::unique_ptr<QueuedTask>(new ClosureTask<Closure2>(reply)));
+  }
+
+ private:
+#if defined(WEBRTC_BUILD_LIBEVENT)
+  static void ThreadMain(void* context);
+  static void OnWakeup(int socket, short flags, void* context);  // NOLINT
+  static void RunTask(int fd, short flags, void* context);       // NOLINT
+  static void RunTimer(int fd, short flags, void* context);      // NOLINT
+
+  class ReplyTaskOwner;
+  class PostAndReplyTask;
+  class SetTimerTask;
+
+  typedef RefCountedObject<ReplyTaskOwner> ReplyTaskOwnerRef;
+
+  void PrepareReplyTask(scoped_refptr<ReplyTaskOwnerRef> reply_task);
+
+  struct QueueContext;
+
+  int wakeup_pipe_in_ = -1;
+  int wakeup_pipe_out_ = -1;
+  event_base* event_base_;
+  std::unique_ptr<event> wakeup_event_;
+  PlatformThread thread_;
+  rtc::CriticalSection pending_lock_;
+  std::list<std::unique_ptr<QueuedTask>> pending_ GUARDED_BY(pending_lock_);
+  std::list<scoped_refptr<ReplyTaskOwnerRef>> pending_replies_
+      GUARDED_BY(pending_lock_);
+#elif defined(WEBRTC_MAC)
+  struct QueueContext;
+  struct TaskContext;
+  struct PostTaskAndReplyContext;
+  dispatch_queue_t queue_;
+  QueueContext* const context_;
+#elif defined(WEBRTC_WIN)
+  class ThreadState;
+  void RunPendingTasks();
+  static void ThreadMain(void* context);
+
+  class WorkerThread : public PlatformThread {
+   public:
+    WorkerThread(ThreadRunFunction func,
+                 void* obj,
+                 const char* thread_name,
+                 ThreadPriority priority)
+        : PlatformThread(func, obj, thread_name, priority) {}
+
+    bool QueueAPC(PAPCFUNC apc_function, ULONG_PTR data) {
+      return PlatformThread::QueueAPC(apc_function, data);
+    }
+  };
+  WorkerThread thread_;
+  rtc::CriticalSection pending_lock_;
+  std::queue<std::unique_ptr<QueuedTask>> pending_ GUARDED_BY(pending_lock_);
+  HANDLE in_queue_;
+#else
+#error not supported.
+#endif
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(TaskQueue);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_TASK_QUEUE_H_
diff --git a/base/task_queue_gcd.cc b/base/task_queue_gcd.cc
new file mode 100644
index 0000000..296da16
--- /dev/null
+++ b/base/task_queue_gcd.cc
@@ -0,0 +1,186 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+// This file contains the implementation of TaskQueue for Mac and iOS.
+// The implementation uses Grand Central Dispatch queues (GCD) to
+// do the actual task queuing.
+
+#include "webrtc/base/task_queue.h"
+
+#include <string.h>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/task_queue_posix.h"
+
+namespace rtc {
+namespace {
+
+using Priority = TaskQueue::Priority;
+
+int TaskQueuePriorityToGCD(Priority priority) {
+  switch (priority) {
+    case Priority::NORMAL:
+      return DISPATCH_QUEUE_PRIORITY_DEFAULT;
+    case Priority::HIGH:
+      return DISPATCH_QUEUE_PRIORITY_HIGH;
+    case Priority::LOW:
+      return DISPATCH_QUEUE_PRIORITY_LOW;
+  }
+}
+}
+
+using internal::GetQueuePtrTls;
+using internal::AutoSetCurrentQueuePtr;
+
+struct TaskQueue::QueueContext {
+  explicit QueueContext(TaskQueue* q) : queue(q), is_active(true) {}
+
+  static void SetNotActive(void* context) {
+    QueueContext* qc = static_cast<QueueContext*>(context);
+    qc->is_active = false;
+  }
+
+  static void DeleteContext(void* context) {
+    QueueContext* qc = static_cast<QueueContext*>(context);
+    delete qc;
+  }
+
+  TaskQueue* const queue;
+  bool is_active;
+};
+
+struct TaskQueue::TaskContext {
+  TaskContext(QueueContext* queue_ctx, std::unique_ptr<QueuedTask> task)
+      : queue_ctx(queue_ctx), task(std::move(task)) {}
+  virtual ~TaskContext() {}
+
+  static void RunTask(void* context) {
+    std::unique_ptr<TaskContext> tc(static_cast<TaskContext*>(context));
+    if (tc->queue_ctx->is_active) {
+      AutoSetCurrentQueuePtr set_current(tc->queue_ctx->queue);
+      if (!tc->task->Run())
+        tc->task.release();
+    }
+  }
+
+  QueueContext* const queue_ctx;
+  std::unique_ptr<QueuedTask> task;
+};
+
+// Special case context for holding two tasks, a |first_task| + the task
+// that's owned by the parent struct, TaskContext, that then becomes the
+// second (i.e. 'reply') task.
+struct TaskQueue::PostTaskAndReplyContext : public TaskQueue::TaskContext {
+  explicit PostTaskAndReplyContext(QueueContext* first_queue_ctx,
+                                   std::unique_ptr<QueuedTask> first_task,
+                                   QueueContext* second_queue_ctx,
+                                   std::unique_ptr<QueuedTask> second_task)
+      : TaskContext(second_queue_ctx, std::move(second_task)),
+        first_queue_ctx(first_queue_ctx),
+        first_task(std::move(first_task)),
+        reply_queue_(second_queue_ctx->queue->queue_) {
+    // Retain the reply queue for as long as this object lives.
+    // If we don't, we may have memory leaks and/or failures.
+    dispatch_retain(reply_queue_);
+  }
+  ~PostTaskAndReplyContext() override { dispatch_release(reply_queue_); }
+
+  static void RunTask(void* context) {
+    auto* rc = static_cast<PostTaskAndReplyContext*>(context);
+    if (rc->first_queue_ctx->is_active) {
+      AutoSetCurrentQueuePtr set_current(rc->first_queue_ctx->queue);
+      if (!rc->first_task->Run())
+        rc->first_task.release();
+    }
+    // Post the reply task.  This hands the work over to the parent struct.
+    // This task will eventually delete |this|.
+    dispatch_async_f(rc->reply_queue_, rc, &TaskContext::RunTask);
+  }
+
+  QueueContext* const first_queue_ctx;
+  std::unique_ptr<QueuedTask> first_task;
+  dispatch_queue_t reply_queue_;
+};
+
+TaskQueue::TaskQueue(const char* queue_name, Priority priority /*= NORMAL*/)
+    : queue_(dispatch_queue_create(queue_name, DISPATCH_QUEUE_SERIAL)),
+      context_(new QueueContext(this)) {
+  RTC_DCHECK(queue_name);
+  RTC_CHECK(queue_);
+  dispatch_set_context(queue_, context_);
+  // Assign a finalizer that will delete the context when the last reference
+  // to the queue is released.  This may run after the TaskQueue object has
+  // been deleted.
+  dispatch_set_finalizer_f(queue_, &QueueContext::DeleteContext);
+
+  dispatch_set_target_queue(
+      queue_, dispatch_get_global_queue(TaskQueuePriorityToGCD(priority), 0));
+}
+
+TaskQueue::~TaskQueue() {
+  RTC_DCHECK(!IsCurrent());
+  // Implementation/behavioral note:
+  // Dispatch queues are reference counted via calls to dispatch_retain and
+  // dispatch_release. Pending blocks submitted to a queue also hold a
+  // reference to the queue until they have finished. Once all references to a
+  // queue have been released, the queue will be deallocated by the system.
+  // This is why we check the context before running tasks.
+
+  // Use dispatch_sync to set the context to null to guarantee that there's not
+  // a race between checking the context and using it from a task.
+  dispatch_sync_f(queue_, context_, &QueueContext::SetNotActive);
+  dispatch_release(queue_);
+}
+
+// static
+TaskQueue* TaskQueue::Current() {
+  return static_cast<TaskQueue*>(pthread_getspecific(GetQueuePtrTls()));
+}
+
+// static
+bool TaskQueue::IsCurrent(const char* queue_name) {
+  TaskQueue* current = Current();
+  return current &&
+         strcmp(queue_name, dispatch_queue_get_label(current->queue_)) == 0;
+}
+
+bool TaskQueue::IsCurrent() const {
+  RTC_DCHECK(queue_);
+  return this == Current();
+}
+
+void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) {
+  auto* context = new TaskContext(context_, std::move(task));
+  dispatch_async_f(queue_, context, &TaskContext::RunTask);
+}
+
+void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task,
+                                uint32_t milliseconds) {
+  auto* context = new TaskContext(context_, std::move(task));
+  dispatch_after_f(
+      dispatch_time(DISPATCH_TIME_NOW, milliseconds * NSEC_PER_MSEC), queue_,
+      context, &TaskContext::RunTask);
+}
+
+void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
+                                 std::unique_ptr<QueuedTask> reply,
+                                 TaskQueue* reply_queue) {
+  auto* context = new PostTaskAndReplyContext(
+      context_, std::move(task), reply_queue->context_, std::move(reply));
+  dispatch_async_f(queue_, context, &PostTaskAndReplyContext::RunTask);
+}
+
+void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
+                                 std::unique_ptr<QueuedTask> reply) {
+  return PostTaskAndReply(std::move(task), std::move(reply), Current());
+}
+
+}  // namespace rtc
diff --git a/base/task_queue_libevent.cc b/base/task_queue_libevent.cc
new file mode 100644
index 0000000..1376ea3
--- /dev/null
+++ b/base/task_queue_libevent.cc
@@ -0,0 +1,429 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/task_queue.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "base/third_party/libevent/event.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/task_queue_posix.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+using internal::GetQueuePtrTls;
+using internal::AutoSetCurrentQueuePtr;
+
+namespace {
+static const char kQuit = 1;
+static const char kRunTask = 2;
+static const char kRunReplyTask = 3;
+
+using Priority = TaskQueue::Priority;
+
+// This ignores the SIGPIPE signal on the calling thread.
+// This signal can be fired when trying to write() to a pipe that's being
+// closed or while closing a pipe that's being written to.
+// We can run into that situation (e.g. reply tasks that don't get a chance to
+// run because the task queue is being deleted) so we ignore this signal and
+// continue as normal.
+// As a side note for this implementation, it would be great if we could safely
+// restore the sigmask, but unfortunately the operation of restoring it, can
+// itself actually cause SIGPIPE to be signaled :-| (e.g. on MacOS)
+// The SIGPIPE signal by default causes the process to be terminated, so we
+// don't want to risk that.
+// An alternative to this approach is to ignore the signal for the whole
+// process:
+//   signal(SIGPIPE, SIG_IGN);
+void IgnoreSigPipeSignalOnCurrentThread() {
+  sigset_t sigpipe_mask;
+  sigemptyset(&sigpipe_mask);
+  sigaddset(&sigpipe_mask, SIGPIPE);
+  pthread_sigmask(SIG_BLOCK, &sigpipe_mask, nullptr);
+}
+
+struct TimerEvent {
+  explicit TimerEvent(std::unique_ptr<QueuedTask> task)
+      : task(std::move(task)) {}
+  ~TimerEvent() { event_del(&ev); }
+  event ev;
+  std::unique_ptr<QueuedTask> task;
+};
+
+bool SetNonBlocking(int fd) {
+  const int flags = fcntl(fd, F_GETFL);
+  RTC_CHECK(flags != -1);
+  return (flags & O_NONBLOCK) || fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1;
+}
+
+// TODO(tommi): This is a hack to support two versions of libevent that we're
+// compatible with.  The method we really want to call is event_assign(),
+// since event_set() has been marked as deprecated (and doesn't accept
+// passing event_base__ as a parameter).  However, the version of libevent
+// that we have in Chromium, doesn't have event_assign(), so we need to call
+// event_set() there.
+void EventAssign(struct event* ev,
+                 struct event_base* base,
+                 int fd,
+                 short events,
+                 void (*callback)(int, short, void*),
+                 void* arg) {
+#if defined(_EVENT2_EVENT_H_)
+  RTC_CHECK_EQ(0, event_assign(ev, base, fd, events, callback, arg));
+#else
+  event_set(ev, fd, events, callback, arg);
+  RTC_CHECK_EQ(0, event_base_set(base, ev));
+#endif
+}
+
+ThreadPriority TaskQueuePriorityToThreadPriority(Priority priority) {
+  switch (priority) {
+    case Priority::HIGH:
+      return kRealtimePriority;
+    case Priority::LOW:
+      return kLowPriority;
+    case Priority::NORMAL:
+      return kNormalPriority;
+    default:
+      RTC_NOTREACHED();
+      break;
+  }
+  return kNormalPriority;
+}
+}  // namespace
+
+struct TaskQueue::QueueContext {
+  explicit QueueContext(TaskQueue* q) : queue(q), is_active(true) {}
+  TaskQueue* queue;
+  bool is_active;
+  // Holds a list of events pending timers for cleanup when the loop exits.
+  std::list<TimerEvent*> pending_timers_;
+};
+
+// Posting a reply task is tricky business. This class owns the reply task
+// and a reference to it is held by both the reply queue and the first task.
+// Here's an outline of what happens when dealing with a reply task.
+// * The ReplyTaskOwner owns the |reply_| task.
+// * One ref owned by PostAndReplyTask
+// * One ref owned by the reply TaskQueue
+// * ReplyTaskOwner has a flag |run_task_| initially set to false.
+// * ReplyTaskOwner has a method: HasOneRef() (provided by RefCountedObject).
+// * After successfully running the original |task_|, PostAndReplyTask() calls
+//   set_should_run_task(). This sets |run_task_| to true.
+// * In PostAndReplyTask's dtor:
+//   * It releases its reference to ReplyTaskOwner (important to do this first).
+//   * Sends (write()) a kRunReplyTask message to the reply queue's pipe.
+// * PostAndReplyTask doesn't care if write() fails, but when it does:
+//   * The reply queue is gone.
+//   * ReplyTaskOwner has already been deleted and the reply task too.
+// * If write() succeeds:
+//   * ReplyQueue receives the kRunReplyTask message
+//   * Goes through all pending tasks, finding the first that HasOneRef()
+//   * Calls ReplyTaskOwner::Run()
+//     * if set_should_run_task() was called, the reply task will be run
+//   * Release the reference to ReplyTaskOwner
+//   * ReplyTaskOwner and associated |reply_| are deleted.
+class TaskQueue::ReplyTaskOwner {
+ public:
+  ReplyTaskOwner(std::unique_ptr<QueuedTask> reply)
+      : reply_(std::move(reply)) {}
+
+  void Run() {
+    RTC_DCHECK(reply_);
+    if (run_task_) {
+      if (!reply_->Run())
+        reply_.release();
+    }
+    reply_.reset();
+  }
+
+  void set_should_run_task() {
+    RTC_DCHECK(!run_task_);
+    run_task_ = true;
+  }
+
+ private:
+  std::unique_ptr<QueuedTask> reply_;
+  bool run_task_ = false;
+};
+
+class TaskQueue::PostAndReplyTask : public QueuedTask {
+ public:
+  PostAndReplyTask(std::unique_ptr<QueuedTask> task,
+                   std::unique_ptr<QueuedTask> reply,
+                   TaskQueue* reply_queue,
+                   int reply_pipe)
+      : task_(std::move(task)),
+        reply_pipe_(reply_pipe),
+        reply_task_owner_(
+            new RefCountedObject<ReplyTaskOwner>(std::move(reply))) {
+    reply_queue->PrepareReplyTask(reply_task_owner_);
+  }
+
+  ~PostAndReplyTask() override {
+    reply_task_owner_ = nullptr;
+    IgnoreSigPipeSignalOnCurrentThread();
+    // Send a signal to the reply queue that the reply task can run now.
+    // Depending on whether |set_should_run_task()| was called by the
+    // PostAndReplyTask(), the reply task may or may not actually run.
+    // In either case, it will be deleted.
+    char message = kRunReplyTask;
+    write(reply_pipe_, &message, sizeof(message));
+  }
+
+ private:
+  bool Run() override {
+    if (!task_->Run())
+      task_.release();
+    reply_task_owner_->set_should_run_task();
+    return true;
+  }
+
+  std::unique_ptr<QueuedTask> task_;
+  int reply_pipe_;
+  scoped_refptr<RefCountedObject<ReplyTaskOwner>> reply_task_owner_;
+};
+
+class TaskQueue::SetTimerTask : public QueuedTask {
+ public:
+  SetTimerTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds)
+      : task_(std::move(task)),
+        milliseconds_(milliseconds),
+        posted_(Time32()) {}
+
+ private:
+  bool Run() override {
+    // Compensate for the time that has passed since construction
+    // and until we got here.
+    uint32_t post_time = Time32() - posted_;
+    TaskQueue::Current()->PostDelayedTask(
+        std::move(task_),
+        post_time > milliseconds_ ? 0 : milliseconds_ - post_time);
+    return true;
+  }
+
+  std::unique_ptr<QueuedTask> task_;
+  const uint32_t milliseconds_;
+  const uint32_t posted_;
+};
+
+TaskQueue::TaskQueue(const char* queue_name, Priority priority /*= NORMAL*/)
+    : event_base_(event_base_new()),
+      wakeup_event_(new event()),
+      thread_(&TaskQueue::ThreadMain,
+              this,
+              queue_name,
+              TaskQueuePriorityToThreadPriority(priority)) {
+  RTC_DCHECK(queue_name);
+  int fds[2];
+  RTC_CHECK(pipe(fds) == 0);
+  SetNonBlocking(fds[0]);
+  SetNonBlocking(fds[1]);
+  wakeup_pipe_out_ = fds[0];
+  wakeup_pipe_in_ = fds[1];
+
+  EventAssign(wakeup_event_.get(), event_base_, wakeup_pipe_out_,
+              EV_READ | EV_PERSIST, OnWakeup, this);
+  event_add(wakeup_event_.get(), 0);
+  thread_.Start();
+}
+
+TaskQueue::~TaskQueue() {
+  RTC_DCHECK(!IsCurrent());
+  struct timespec ts;
+  char message = kQuit;
+  while (write(wakeup_pipe_in_, &message, sizeof(message)) != sizeof(message)) {
+    // The queue is full, so we have no choice but to wait and retry.
+    RTC_CHECK_EQ(EAGAIN, errno);
+    ts.tv_sec = 0;
+    ts.tv_nsec = 1000000;
+    nanosleep(&ts, nullptr);
+  }
+
+  thread_.Stop();
+
+  event_del(wakeup_event_.get());
+
+  IgnoreSigPipeSignalOnCurrentThread();
+
+  close(wakeup_pipe_in_);
+  close(wakeup_pipe_out_);
+  wakeup_pipe_in_ = -1;
+  wakeup_pipe_out_ = -1;
+
+  event_base_free(event_base_);
+}
+
+// static
+TaskQueue* TaskQueue::Current() {
+  QueueContext* ctx =
+      static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
+  return ctx ? ctx->queue : nullptr;
+}
+
+// static
+bool TaskQueue::IsCurrent(const char* queue_name) {
+  TaskQueue* current = Current();
+  return current && current->thread_.name().compare(queue_name) == 0;
+}
+
+bool TaskQueue::IsCurrent() const {
+  return IsThreadRefEqual(thread_.GetThreadRef(), CurrentThreadRef());
+}
+
+void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) {
+  RTC_DCHECK(task.get());
+  // libevent isn't thread safe.  This means that we can't use methods such
+  // as event_base_once to post tasks to the worker thread from a different
+  // thread.  However, we can use it when posting from the worker thread itself.
+  if (IsCurrent()) {
+    if (event_base_once(event_base_, -1, EV_TIMEOUT, &TaskQueue::RunTask,
+                        task.get(), nullptr) == 0) {
+      task.release();
+    }
+  } else {
+    QueuedTask* task_id = task.get();  // Only used for comparison.
+    {
+      CritScope lock(&pending_lock_);
+      pending_.push_back(std::move(task));
+    }
+    char message = kRunTask;
+    if (write(wakeup_pipe_in_, &message, sizeof(message)) != sizeof(message)) {
+      LOG(WARNING) << "Failed to queue task.";
+      CritScope lock(&pending_lock_);
+      pending_.remove_if([task_id](std::unique_ptr<QueuedTask>& t) {
+        return t.get() == task_id;
+      });
+    }
+  }
+}
+
+void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task,
+                                uint32_t milliseconds) {
+  if (IsCurrent()) {
+    TimerEvent* timer = new TimerEvent(std::move(task));
+    EventAssign(&timer->ev, event_base_, -1, 0, &TaskQueue::RunTimer, timer);
+    QueueContext* ctx =
+        static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
+    ctx->pending_timers_.push_back(timer);
+    timeval tv = {milliseconds / 1000, (milliseconds % 1000) * 1000};
+    event_add(&timer->ev, &tv);
+  } else {
+    PostTask(std::unique_ptr<QueuedTask>(
+        new SetTimerTask(std::move(task), milliseconds)));
+  }
+}
+
+void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
+                                 std::unique_ptr<QueuedTask> reply,
+                                 TaskQueue* reply_queue) {
+  std::unique_ptr<QueuedTask> wrapper_task(
+      new PostAndReplyTask(std::move(task), std::move(reply), reply_queue,
+                           reply_queue->wakeup_pipe_in_));
+  PostTask(std::move(wrapper_task));
+}
+
+void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
+                                 std::unique_ptr<QueuedTask> reply) {
+  return PostTaskAndReply(std::move(task), std::move(reply), Current());
+}
+
+// static
+void TaskQueue::ThreadMain(void* context) {
+  TaskQueue* me = static_cast<TaskQueue*>(context);
+
+  QueueContext queue_context(me);
+  pthread_setspecific(GetQueuePtrTls(), &queue_context);
+
+  while (queue_context.is_active)
+    event_base_loop(me->event_base_, 0);
+
+  pthread_setspecific(GetQueuePtrTls(), nullptr);
+
+  for (TimerEvent* timer : queue_context.pending_timers_)
+    delete timer;
+}
+
+// static
+void TaskQueue::OnWakeup(int socket, short flags, void* context) {  // NOLINT
+  QueueContext* ctx =
+      static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
+  RTC_DCHECK(ctx->queue->wakeup_pipe_out_ == socket);
+  char buf;
+  RTC_CHECK(sizeof(buf) == read(socket, &buf, sizeof(buf)));
+  switch (buf) {
+    case kQuit:
+      ctx->is_active = false;
+      event_base_loopbreak(ctx->queue->event_base_);
+      break;
+    case kRunTask: {
+      std::unique_ptr<QueuedTask> task;
+      {
+        CritScope lock(&ctx->queue->pending_lock_);
+        RTC_DCHECK(!ctx->queue->pending_.empty());
+        task = std::move(ctx->queue->pending_.front());
+        ctx->queue->pending_.pop_front();
+        RTC_DCHECK(task.get());
+      }
+      if (!task->Run())
+        task.release();
+      break;
+    }
+    case kRunReplyTask: {
+      scoped_refptr<ReplyTaskOwnerRef> reply_task;
+      {
+        CritScope lock(&ctx->queue->pending_lock_);
+        for (auto it = ctx->queue->pending_replies_.begin();
+             it != ctx->queue->pending_replies_.end(); ++it) {
+          if ((*it)->HasOneRef()) {
+            reply_task = std::move(*it);
+            ctx->queue->pending_replies_.erase(it);
+            break;
+          }
+        }
+      }
+      reply_task->Run();
+      break;
+    }
+    default:
+      RTC_NOTREACHED();
+      break;
+  }
+}
+
+// static
+void TaskQueue::RunTask(int fd, short flags, void* context) {  // NOLINT
+  auto* task = static_cast<QueuedTask*>(context);
+  if (task->Run())
+    delete task;
+}
+
+// static
+void TaskQueue::RunTimer(int fd, short flags, void* context) {  // NOLINT
+  TimerEvent* timer = static_cast<TimerEvent*>(context);
+  if (!timer->task->Run())
+    timer->task.release();
+  QueueContext* ctx =
+      static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
+  ctx->pending_timers_.remove(timer);
+  delete timer;
+}
+
+void TaskQueue::PrepareReplyTask(scoped_refptr<ReplyTaskOwnerRef> reply_task) {
+  RTC_DCHECK(reply_task);
+  CritScope lock(&pending_lock_);
+  pending_replies_.push_back(std::move(reply_task));
+}
+
+}  // namespace rtc
diff --git a/base/task_queue_posix.cc b/base/task_queue_posix.cc
new file mode 100644
index 0000000..3b00ac8
--- /dev/null
+++ b/base/task_queue_posix.cc
@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/task_queue_posix.h"
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/task_queue.h"
+
+namespace rtc {
+namespace internal {
+pthread_key_t g_queue_ptr_tls = 0;
+
+void InitializeTls() {
+  RTC_CHECK(pthread_key_create(&g_queue_ptr_tls, nullptr) == 0);
+}
+
+pthread_key_t GetQueuePtrTls() {
+  static pthread_once_t init_once = PTHREAD_ONCE_INIT;
+  RTC_CHECK(pthread_once(&init_once, &InitializeTls) == 0);
+  return g_queue_ptr_tls;
+}
+
+AutoSetCurrentQueuePtr::AutoSetCurrentQueuePtr(TaskQueue* q)
+    : prev_(TaskQueue::Current()) {
+  pthread_setspecific(GetQueuePtrTls(), q);
+}
+
+AutoSetCurrentQueuePtr::~AutoSetCurrentQueuePtr() {
+  pthread_setspecific(GetQueuePtrTls(), prev_);
+}
+
+}  // namespace internal
+}  // namespace rtc
diff --git a/base/task_queue_posix.h b/base/task_queue_posix.h
index 6cb51a0..b677b78 100644
--- a/base/task_queue_posix.h
+++ b/base/task_queue_posix.h
@@ -11,9 +11,26 @@
 #ifndef WEBRTC_BASE_TASK_QUEUE_POSIX_H_
 #define WEBRTC_BASE_TASK_QUEUE_POSIX_H_
 
+#include <pthread.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/task_queue_posix.h"
+namespace rtc {
+
+class TaskQueue;
+
+namespace internal {
+
+class AutoSetCurrentQueuePtr {
+ public:
+  explicit AutoSetCurrentQueuePtr(TaskQueue* q);
+  ~AutoSetCurrentQueuePtr();
+
+ private:
+  TaskQueue* const prev_;
+};
+
+pthread_key_t GetQueuePtrTls();
+
+}  // namespace internal
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_TASK_QUEUE_POSIX_H_
diff --git a/base/task_queue_unittest.cc b/base/task_queue_unittest.cc
new file mode 100644
index 0000000..856249d
--- /dev/null
+++ b/base/task_queue_unittest.cc
@@ -0,0 +1,331 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#if defined(WEBRTC_WIN)
+// clang-format off
+#include <windows.h>  // Must come first.
+#include <mmsystem.h>
+// clang-format on
+#endif
+
+#include <memory>
+#include <vector>
+
+#include "webrtc/base/bind.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/task_queue.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+namespace {
+// Noop on all platforms except Windows, where it turns on high precision
+// multimedia timers which increases the precision of TimeMillis() while in
+// scope.
+class EnableHighResTimers {
+ public:
+#if !defined(WEBRTC_WIN)
+  EnableHighResTimers() {}
+#else
+  EnableHighResTimers() : enabled_(timeBeginPeriod(1) == TIMERR_NOERROR) {}
+  ~EnableHighResTimers() {
+    if (enabled_)
+      timeEndPeriod(1);
+  }
+
+ private:
+  const bool enabled_;
+#endif
+};
+}
+
+namespace {
+void CheckCurrent(const char* expected_queue, Event* signal, TaskQueue* queue) {
+  EXPECT_TRUE(TaskQueue::IsCurrent(expected_queue));
+  EXPECT_TRUE(queue->IsCurrent());
+  if (signal)
+    signal->Set();
+}
+
+}  // namespace
+
+TEST(TaskQueueTest, Construct) {
+  static const char kQueueName[] = "Construct";
+  TaskQueue queue(kQueueName);
+  EXPECT_FALSE(queue.IsCurrent());
+}
+
+TEST(TaskQueueTest, PostAndCheckCurrent) {
+  static const char kQueueName[] = "PostAndCheckCurrent";
+  Event event(false, false);
+  TaskQueue queue(kQueueName);
+
+  // We're not running a task, so there shouldn't be a current queue.
+  EXPECT_FALSE(queue.IsCurrent());
+  EXPECT_FALSE(TaskQueue::Current());
+
+  queue.PostTask(Bind(&CheckCurrent, kQueueName, &event, &queue));
+  EXPECT_TRUE(event.Wait(1000));
+}
+
+TEST(TaskQueueTest, PostCustomTask) {
+  static const char kQueueName[] = "PostCustomImplementation";
+  Event event(false, false);
+  TaskQueue queue(kQueueName);
+
+  class CustomTask : public QueuedTask {
+   public:
+    explicit CustomTask(Event* event) : event_(event) {}
+
+   private:
+    bool Run() override {
+      event_->Set();
+      return false;  // Never allows the task to be deleted by the queue.
+    }
+
+    Event* const event_;
+  } my_task(&event);
+
+  // Please don't do this in production code! :)
+  queue.PostTask(std::unique_ptr<QueuedTask>(&my_task));
+  EXPECT_TRUE(event.Wait(1000));
+}
+
+TEST(TaskQueueTest, PostLambda) {
+  static const char kQueueName[] = "PostLambda";
+  Event event(false, false);
+  TaskQueue queue(kQueueName);
+
+  queue.PostTask([&event]() { event.Set(); });
+  EXPECT_TRUE(event.Wait(1000));
+}
+
+TEST(TaskQueueTest, PostDelayedZero) {
+  static const char kQueueName[] = "PostDelayedZero";
+  Event event(false, false);
+  TaskQueue queue(kQueueName);
+
+  queue.PostDelayedTask([&event]() { event.Set(); }, 0);
+  EXPECT_TRUE(event.Wait(1000));
+}
+
+TEST(TaskQueueTest, PostFromQueue) {
+  static const char kQueueName[] = "PostFromQueue";
+  Event event(false, false);
+  TaskQueue queue(kQueueName);
+
+  queue.PostTask(
+      [&event, &queue]() { queue.PostTask([&event]() { event.Set(); }); });
+  EXPECT_TRUE(event.Wait(1000));
+}
+
+TEST(TaskQueueTest, PostDelayed) {
+  static const char kQueueName[] = "PostDelayed";
+  Event event(false, false);
+  TaskQueue queue(kQueueName, TaskQueue::Priority::HIGH);
+
+  uint32_t start = Time();
+  queue.PostDelayedTask(Bind(&CheckCurrent, kQueueName, &event, &queue), 100);
+  EXPECT_TRUE(event.Wait(1000));
+  uint32_t end = Time();
+  // These tests are a little relaxed due to how "powerful" our test bots can
+  // be.  Most recently we've seen windows bots fire the callback after 94-99ms,
+  // which is why we have a little bit of leeway backwards as well.
+  EXPECT_GE(end - start, 90u);
+  EXPECT_NEAR(end - start, 190u, 100u);  // Accept 90-290.
+}
+
+// This task needs to be run manually due to the slowness of some of our bots.
+// TODO(tommi): Can we run this on the perf bots?
+TEST(TaskQueueTest, DISABLED_PostDelayedHighRes) {
+  EnableHighResTimers high_res_scope;
+
+  static const char kQueueName[] = "PostDelayedHighRes";
+  Event event(false, false);
+  TaskQueue queue(kQueueName, TaskQueue::Priority::HIGH);
+
+  uint32_t start = Time();
+  queue.PostDelayedTask(Bind(&CheckCurrent, kQueueName, &event, &queue), 3);
+  EXPECT_TRUE(event.Wait(1000));
+  uint32_t end = TimeMillis();
+  // These tests are a little relaxed due to how "powerful" our test bots can
+  // be.  Most recently we've seen windows bots fire the callback after 94-99ms,
+  // which is why we have a little bit of leeway backwards as well.
+  EXPECT_GE(end - start, 3u);
+  EXPECT_NEAR(end - start, 3, 3u);
+}
+
+TEST(TaskQueueTest, PostMultipleDelayed) {
+  static const char kQueueName[] = "PostMultipleDelayed";
+  TaskQueue queue(kQueueName);
+
+  std::vector<std::unique_ptr<Event>> events;
+  for (int i = 0; i < 100; ++i) {
+    events.push_back(std::unique_ptr<Event>(new Event(false, false)));
+    queue.PostDelayedTask(
+        Bind(&CheckCurrent, kQueueName, events.back().get(), &queue), i);
+  }
+
+  for (const auto& e : events)
+    EXPECT_TRUE(e->Wait(1000));
+}
+
+TEST(TaskQueueTest, PostDelayedAfterDestruct) {
+  static const char kQueueName[] = "PostDelayedAfterDestruct";
+  Event event(false, false);
+  {
+    TaskQueue queue(kQueueName);
+    queue.PostDelayedTask(Bind(&CheckCurrent, kQueueName, &event, &queue), 100);
+  }
+  EXPECT_FALSE(event.Wait(200));  // Task should not run.
+}
+
+TEST(TaskQueueTest, PostAndReply) {
+  static const char kPostQueue[] = "PostQueue";
+  static const char kReplyQueue[] = "ReplyQueue";
+  Event event(false, false);
+  TaskQueue post_queue(kPostQueue);
+  TaskQueue reply_queue(kReplyQueue);
+
+  post_queue.PostTaskAndReply(
+      Bind(&CheckCurrent, kPostQueue, nullptr, &post_queue),
+      Bind(&CheckCurrent, kReplyQueue, &event, &reply_queue), &reply_queue);
+  EXPECT_TRUE(event.Wait(1000));
+}
+
+TEST(TaskQueueTest, PostAndReuse) {
+  static const char kPostQueue[] = "PostQueue";
+  static const char kReplyQueue[] = "ReplyQueue";
+  Event event(false, false);
+  TaskQueue post_queue(kPostQueue);
+  TaskQueue reply_queue(kReplyQueue);
+
+  int call_count = 0;
+
+  class ReusedTask : public QueuedTask {
+   public:
+    ReusedTask(int* counter, TaskQueue* reply_queue, Event* event)
+        : counter_(counter), reply_queue_(reply_queue), event_(event) {
+      EXPECT_EQ(0, *counter_);
+    }
+
+   private:
+    bool Run() override {
+      if (++(*counter_) == 1) {
+        std::unique_ptr<QueuedTask> myself(this);
+        reply_queue_->PostTask(std::move(myself));
+        // At this point, the object is owned by reply_queue_ and it's
+        // theoratically possible that the object has been deleted (e.g. if
+        // posting wasn't possible).  So, don't touch any member variables here.
+
+        // Indicate to the current queue that ownership has been transferred.
+        return false;
+      } else {
+        EXPECT_EQ(2, *counter_);
+        EXPECT_TRUE(reply_queue_->IsCurrent());
+        event_->Set();
+        return true;  // Indicate that the object should be deleted.
+      }
+    }
+
+    int* const counter_;
+    TaskQueue* const reply_queue_;
+    Event* const event_;
+  };
+
+  std::unique_ptr<QueuedTask> task(
+      new ReusedTask(&call_count, &reply_queue, &event));
+
+  post_queue.PostTask(std::move(task));
+  EXPECT_TRUE(event.Wait(1000));
+}
+
+TEST(TaskQueueTest, PostAndReplyLambda) {
+  static const char kPostQueue[] = "PostQueue";
+  static const char kReplyQueue[] = "ReplyQueue";
+  Event event(false, false);
+  TaskQueue post_queue(kPostQueue);
+  TaskQueue reply_queue(kReplyQueue);
+
+  bool my_flag = false;
+  post_queue.PostTaskAndReply([&my_flag]() { my_flag = true; },
+                              [&event]() { event.Set(); }, &reply_queue);
+  EXPECT_TRUE(event.Wait(1000));
+  EXPECT_TRUE(my_flag);
+}
+
+// This test covers a particular bug that we had in the libevent implementation
+// where we could hit a deadlock while trying to post a reply task to a queue
+// that was being deleted.  The test isn't guaranteed to hit that case but it's
+// written in a way that makes it likely and by running with --gtest_repeat=1000
+// the bug would occur. Alas, now it should be fixed.
+TEST(TaskQueueTest, PostAndReplyDeadlock) {
+  Event event(false, false);
+  TaskQueue post_queue("PostQueue");
+  TaskQueue reply_queue("ReplyQueue");
+
+  post_queue.PostTaskAndReply([&event]() { event.Set(); }, []() {},
+                              &reply_queue);
+  EXPECT_TRUE(event.Wait(1000));
+}
+
+void TestPostTaskAndReply(TaskQueue* work_queue,
+                          const char* work_queue_name,
+                          Event* event) {
+  ASSERT_FALSE(work_queue->IsCurrent());
+  work_queue->PostTaskAndReply(
+      Bind(&CheckCurrent, work_queue_name, nullptr, work_queue),
+      NewClosure([event]() { event->Set(); }));
+}
+
+// Does a PostTaskAndReply from within a task to post and reply to the current
+// queue.  All in all there will be 3 tasks posted and run.
+TEST(TaskQueueTest, PostAndReply2) {
+  static const char kQueueName[] = "PostAndReply2";
+  static const char kWorkQueueName[] = "PostAndReply2_Worker";
+  Event event(false, false);
+  TaskQueue queue(kQueueName);
+  TaskQueue work_queue(kWorkQueueName);
+
+  queue.PostTask(
+      Bind(&TestPostTaskAndReply, &work_queue, kWorkQueueName, &event));
+  EXPECT_TRUE(event.Wait(1000));
+}
+
+// Tests posting more messages than a queue can queue up.
+// In situations like that, tasks will get dropped.
+TEST(TaskQueueTest, PostALot) {
+  // To destruct the event after the queue has gone out of scope.
+  Event event(false, false);
+
+  int tasks_executed = 0;
+  int tasks_cleaned_up = 0;
+  static const int kTaskCount = 0xffff;
+
+  {
+    static const char kQueueName[] = "PostALot";
+    TaskQueue queue(kQueueName);
+
+    // On linux, the limit of pending bytes in the pipe buffer is 0xffff.
+    // So here we post a total of 0xffff+1 messages, which triggers a failure
+    // case inside of the libevent queue implementation.
+
+    queue.PostTask([&event]() { event.Wait(Event::kForever); });
+    for (int i = 0; i < kTaskCount; ++i)
+      queue.PostTask(NewClosure([&tasks_executed]() { ++tasks_executed; },
+                                [&tasks_cleaned_up]() { ++tasks_cleaned_up; }));
+    event.Set();  // Unblock the first task.
+  }
+
+  EXPECT_GE(tasks_cleaned_up, tasks_executed);
+  EXPECT_EQ(kTaskCount, tasks_cleaned_up);
+}
+
+}  // namespace rtc
diff --git a/base/task_queue_win.cc b/base/task_queue_win.cc
new file mode 100644
index 0000000..a149dd8
--- /dev/null
+++ b/base/task_queue_win.cc
@@ -0,0 +1,415 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/task_queue.h"
+
+#include <mmsystem.h>
+#include <string.h>
+
+#include <algorithm>
+#include <queue>
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/safe_conversions.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+namespace {
+#define WM_RUN_TASK WM_USER + 1
+#define WM_QUEUE_DELAYED_TASK WM_USER + 2
+
+using Priority = TaskQueue::Priority;
+
+DWORD g_queue_ptr_tls = 0;
+
+BOOL CALLBACK InitializeTls(PINIT_ONCE init_once, void* param, void** context) {
+  g_queue_ptr_tls = TlsAlloc();
+  return TRUE;
+}
+
+DWORD GetQueuePtrTls() {
+  static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
+  ::InitOnceExecuteOnce(&init_once, InitializeTls, nullptr, nullptr);
+  return g_queue_ptr_tls;
+}
+
+struct ThreadStartupData {
+  Event* started;
+  void* thread_context;
+};
+
+void CALLBACK InitializeQueueThread(ULONG_PTR param) {
+  MSG msg;
+  ::PeekMessage(&msg, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
+  ThreadStartupData* data = reinterpret_cast<ThreadStartupData*>(param);
+  ::TlsSetValue(GetQueuePtrTls(), data->thread_context);
+  data->started->Set();
+}
+
+ThreadPriority TaskQueuePriorityToThreadPriority(Priority priority) {
+  switch (priority) {
+    case Priority::HIGH:
+      return kRealtimePriority;
+    case Priority::LOW:
+      return kLowPriority;
+    case Priority::NORMAL:
+      return kNormalPriority;
+    default:
+      RTC_NOTREACHED();
+      break;
+  }
+  return kNormalPriority;
+}
+
+int64_t GetTick() {
+  static const UINT kPeriod = 1;
+  bool high_res = (timeBeginPeriod(kPeriod) == TIMERR_NOERROR);
+  int64_t ret = TimeMillis();
+  if (high_res)
+    timeEndPeriod(kPeriod);
+  return ret;
+}
+
+class DelayedTaskInfo {
+ public:
+  // Default ctor needed to support priority_queue::pop().
+  DelayedTaskInfo() {}
+  DelayedTaskInfo(uint32_t milliseconds, std::unique_ptr<QueuedTask> task)
+      : due_time_(GetTick() + milliseconds), task_(std::move(task)) {}
+  DelayedTaskInfo(DelayedTaskInfo&&) = default;
+
+  // Implement for priority_queue.
+  bool operator>(const DelayedTaskInfo& other) const {
+    return due_time_ > other.due_time_;
+  }
+
+  // Required by priority_queue::pop().
+  DelayedTaskInfo& operator=(DelayedTaskInfo&& other) = default;
+
+  // See below for why this method is const.
+  void Run() const {
+    RTC_DCHECK(due_time_);
+    task_->Run() ? task_.reset() : static_cast<void>(task_.release());
+  }
+
+  int64_t due_time() const { return due_time_; }
+
+ private:
+  int64_t due_time_ = 0;  // Absolute timestamp in milliseconds.
+
+  // |task| needs to be mutable because std::priority_queue::top() returns
+  // a const reference and a key in an ordered queue must not be changed.
+  // There are two basic workarounds, one using const_cast, which would also
+  // make the key (|due_time|), non-const and the other is to make the non-key
+  // (|task|), mutable.
+  // Because of this, the |task| variable is made private and can only be
+  // mutated by calling the |Run()| method.
+  mutable std::unique_ptr<QueuedTask> task_;
+};
+
+class MultimediaTimer {
+ public:
+  // Note: We create an event that requires manual reset.
+  MultimediaTimer() : event_(::CreateEvent(nullptr, true, false, nullptr)) {}
+
+  ~MultimediaTimer() {
+    Cancel();
+    ::CloseHandle(event_);
+  }
+
+  bool StartOneShotTimer(UINT delay_ms) {
+    RTC_DCHECK_EQ(0, timer_id_);
+    RTC_DCHECK(event_ != nullptr);
+    timer_id_ =
+        ::timeSetEvent(delay_ms, 0, reinterpret_cast<LPTIMECALLBACK>(event_), 0,
+                       TIME_ONESHOT | TIME_CALLBACK_EVENT_SET);
+    return timer_id_ != 0;
+  }
+
+  void Cancel() {
+    ::ResetEvent(event_);
+    if (timer_id_) {
+      ::timeKillEvent(timer_id_);
+      timer_id_ = 0;
+    }
+  }
+
+  HANDLE* event_for_wait() { return &event_; }
+
+ private:
+  HANDLE event_ = nullptr;
+  MMRESULT timer_id_ = 0;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(MultimediaTimer);
+};
+
+}  // namespace
+
+class TaskQueue::ThreadState {
+ public:
+  explicit ThreadState(HANDLE in_queue) : in_queue_(in_queue) {}
+  ~ThreadState() {}
+
+  void RunThreadMain();
+
+ private:
+  bool ProcessQueuedMessages();
+  void RunDueTasks();
+  void ScheduleNextTimer();
+  void CancelTimers();
+
+  // Since priority_queue<> by defult orders items in terms of
+  // largest->smallest, using std::less<>, and we want smallest->largest,
+  // we would like to use std::greater<> here. Alas it's only available in
+  // C++14 and later, so we roll our own compare template that that relies on
+  // operator<().
+  template <typename T>
+  struct greater {
+    bool operator()(const T& l, const T& r) { return l > r; }
+  };
+
+  MultimediaTimer timer_;
+  std::priority_queue<DelayedTaskInfo,
+                      std::vector<DelayedTaskInfo>,
+                      greater<DelayedTaskInfo>>
+      timer_tasks_;
+  UINT_PTR timer_id_ = 0;
+  HANDLE in_queue_;
+};
+
+TaskQueue::TaskQueue(const char* queue_name, Priority priority /*= NORMAL*/)
+    : thread_(&TaskQueue::ThreadMain,
+              this,
+              queue_name,
+              TaskQueuePriorityToThreadPriority(priority)),
+       in_queue_(::CreateEvent(nullptr, true, false, nullptr)) {
+  RTC_DCHECK(queue_name);
+  RTC_DCHECK(in_queue_);
+  thread_.Start();
+  Event event(false, false);
+  ThreadStartupData startup = {&event, this};
+  RTC_CHECK(thread_.QueueAPC(&InitializeQueueThread,
+                             reinterpret_cast<ULONG_PTR>(&startup)));
+  event.Wait(Event::kForever);
+}
+
+TaskQueue::~TaskQueue() {
+  RTC_DCHECK(!IsCurrent());
+  while (!::PostThreadMessage(thread_.GetThreadRef(), WM_QUIT, 0, 0)) {
+    RTC_CHECK_EQ(ERROR_NOT_ENOUGH_QUOTA, ::GetLastError());
+    Sleep(1);
+  }
+  thread_.Stop();
+  ::CloseHandle(in_queue_);
+}
+
+// static
+TaskQueue* TaskQueue::Current() {
+  return static_cast<TaskQueue*>(::TlsGetValue(GetQueuePtrTls()));
+}
+
+// static
+bool TaskQueue::IsCurrent(const char* queue_name) {
+  TaskQueue* current = Current();
+  return current && current->thread_.name().compare(queue_name) == 0;
+}
+
+bool TaskQueue::IsCurrent() const {
+  return IsThreadRefEqual(thread_.GetThreadRef(), CurrentThreadRef());
+}
+
+void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) {
+  rtc::CritScope lock(&pending_lock_);
+  pending_.push(std::move(task));
+  ::SetEvent(in_queue_);
+}
+
+void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task,
+                                uint32_t milliseconds) {
+  if (!milliseconds) {
+    PostTask(std::move(task));
+    return;
+  }
+
+  // TODO(tommi): Avoid this allocation.  It is currently here since
+  // the timestamp stored in the task info object, is a 64bit timestamp
+  // and WPARAM is 32bits in 32bit builds.  Otherwise, we could pass the
+  // task pointer and timestamp as LPARAM and WPARAM.
+  auto* task_info = new DelayedTaskInfo(milliseconds, std::move(task));
+  if (!::PostThreadMessage(thread_.GetThreadRef(), WM_QUEUE_DELAYED_TASK, 0,
+                           reinterpret_cast<LPARAM>(task_info))) {
+    delete task_info;
+  }
+}
+
+void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
+                                 std::unique_ptr<QueuedTask> reply,
+                                 TaskQueue* reply_queue) {
+  QueuedTask* task_ptr = task.release();
+  QueuedTask* reply_task_ptr = reply.release();
+  DWORD reply_thread_id = reply_queue->thread_.GetThreadRef();
+  PostTask([task_ptr, reply_task_ptr, reply_thread_id]() {
+    if (task_ptr->Run())
+      delete task_ptr;
+    // If the thread's message queue is full, we can't queue the task and will
+    // have to drop it (i.e. delete).
+    if (!::PostThreadMessage(reply_thread_id, WM_RUN_TASK, 0,
+                             reinterpret_cast<LPARAM>(reply_task_ptr))) {
+      delete reply_task_ptr;
+    }
+  });
+}
+
+void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
+                                 std::unique_ptr<QueuedTask> reply) {
+  return PostTaskAndReply(std::move(task), std::move(reply), Current());
+}
+
+void TaskQueue::RunPendingTasks() {
+  while (true) {
+    std::unique_ptr<QueuedTask> task;
+    {
+      rtc::CritScope lock(&pending_lock_);
+      if (pending_.empty())
+        break;
+      task = std::move(pending_.front());
+      pending_.pop();
+    }
+
+    if (!task->Run())
+      task.release();
+  }
+}
+
+// static
+void TaskQueue::ThreadMain(void* context) {
+  ThreadState state(static_cast<TaskQueue*>(context)->in_queue_);
+  state.RunThreadMain();
+}
+
+void TaskQueue::ThreadState::RunThreadMain() {
+  HANDLE handles[2] = { *timer_.event_for_wait(), in_queue_ };
+  while (true) {
+    // Make sure we do an alertable wait as that's required to allow APCs to run
+    // (e.g. required for InitializeQueueThread and stopping the thread in
+    // PlatformThread).
+    DWORD result = ::MsgWaitForMultipleObjectsEx(
+        arraysize(handles), handles, INFINITE, QS_ALLEVENTS, MWMO_ALERTABLE);
+    RTC_CHECK_NE(WAIT_FAILED, result);
+    if (result == (WAIT_OBJECT_0 + 2)) {
+      // There are messages in the message queue that need to be handled.
+      if (!ProcessQueuedMessages())
+        break;
+    }
+
+    if (result == WAIT_OBJECT_0 || (!timer_tasks_.empty() &&
+        ::WaitForSingleObject(*timer_.event_for_wait(), 0) == WAIT_OBJECT_0)) {
+      // The multimedia timer was signaled.
+      timer_.Cancel();
+      RunDueTasks();
+      ScheduleNextTimer();
+    }
+
+    if (result == (WAIT_OBJECT_0 + 1)) {
+      ::ResetEvent(in_queue_);
+      TaskQueue::Current()->RunPendingTasks();
+    }
+  }
+}
+
+bool TaskQueue::ThreadState::ProcessQueuedMessages() {
+  MSG msg = {};
+  // To protect against overly busy message queues, we limit the time
+  // we process tasks to a few milliseconds. If we don't do that, there's
+  // a chance that timer tasks won't ever run.
+  static const int kMaxTaskProcessingTimeMs = 500;
+  auto start = GetTick();
+  while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) &&
+         msg.message != WM_QUIT) {
+    if (!msg.hwnd) {
+      switch (msg.message) {
+        // TODO(tommi): Stop using this way of queueing tasks.
+        case WM_RUN_TASK: {
+          QueuedTask* task = reinterpret_cast<QueuedTask*>(msg.lParam);
+          if (task->Run())
+            delete task;
+          break;
+        }
+        case WM_QUEUE_DELAYED_TASK: {
+          std::unique_ptr<DelayedTaskInfo> info(
+              reinterpret_cast<DelayedTaskInfo*>(msg.lParam));
+          bool need_to_schedule_timers =
+              timer_tasks_.empty() ||
+              timer_tasks_.top().due_time() > info->due_time();
+          timer_tasks_.emplace(std::move(*info.get()));
+          if (need_to_schedule_timers) {
+            CancelTimers();
+            ScheduleNextTimer();
+          }
+          break;
+        }
+        case WM_TIMER: {
+          RTC_DCHECK_EQ(timer_id_, msg.wParam);
+          ::KillTimer(nullptr, msg.wParam);
+          timer_id_ = 0;
+          RunDueTasks();
+          ScheduleNextTimer();
+          break;
+        }
+        default:
+          RTC_NOTREACHED();
+          break;
+      }
+    } else {
+      ::TranslateMessage(&msg);
+      ::DispatchMessage(&msg);
+    }
+
+    if (GetTick() > start + kMaxTaskProcessingTimeMs)
+      break;
+  }
+  return msg.message != WM_QUIT;
+}
+
+void TaskQueue::ThreadState::RunDueTasks() {
+  RTC_DCHECK(!timer_tasks_.empty());
+  auto now = GetTick();
+  do {
+    const auto& top = timer_tasks_.top();
+    if (top.due_time() > now)
+      break;
+    top.Run();
+    timer_tasks_.pop();
+  } while (!timer_tasks_.empty());
+}
+
+void TaskQueue::ThreadState::ScheduleNextTimer() {
+  RTC_DCHECK_EQ(timer_id_, 0);
+  if (timer_tasks_.empty())
+    return;
+
+  const auto& next_task = timer_tasks_.top();
+  int64_t delay_ms = std::max(0ll, next_task.due_time() - GetTick());
+  uint32_t milliseconds = rtc::dchecked_cast<uint32_t>(delay_ms);
+  if (!timer_.StartOneShotTimer(milliseconds))
+    timer_id_ = ::SetTimer(nullptr, 0, milliseconds, nullptr);
+}
+
+void TaskQueue::ThreadState::CancelTimers() {
+  timer_.Cancel();
+  if (timer_id_) {
+    ::KillTimer(nullptr, timer_id_);
+    timer_id_ = 0;
+  }
+}
+
+}  // namespace rtc
diff --git a/base/template_util.h b/base/template_util.h
index 9a05643..f3565a4 100644
--- a/base/template_util.h
+++ b/base/template_util.h
@@ -13,9 +13,115 @@
 #ifndef WEBRTC_BASE_TEMPLATE_UTIL_H_
 #define WEBRTC_BASE_TEMPLATE_UTIL_H_
 
+#include <stddef.h>  // For size_t.
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/template_util.h"
+namespace rtc {
+
+// Template definitions from tr1.
+
+template<class T, T v>
+struct integral_constant {
+  static const T value = v;
+  typedef T value_type;
+  typedef integral_constant<T, v> type;
+};
+
+template <class T, T v> const T integral_constant<T, v>::value;
+
+typedef integral_constant<bool, true> true_type;
+typedef integral_constant<bool, false> false_type;
+
+template <class T> struct is_pointer : false_type {};
+template <class T> struct is_pointer<T*> : true_type {};
+
+template <class T, class U> struct is_same : public false_type {};
+template <class T> struct is_same<T, T> : true_type {};
+
+template<class> struct is_array : public false_type {};
+template<class T, size_t n> struct is_array<T[n]> : public true_type {};
+template<class T> struct is_array<T[]> : public true_type {};
+
+template <class T> struct is_non_const_reference : false_type {};
+template <class T> struct is_non_const_reference<T&> : true_type {};
+template <class T> struct is_non_const_reference<const T&> : false_type {};
+
+template <class T> struct is_void : false_type {};
+template <> struct is_void<void> : true_type {};
+
+// Helper useful for converting a tuple to variadic template function
+// arguments.
+//
+// sequence_generator<3>::type will be sequence<0, 1, 2>.
+template <int...>
+struct sequence {};
+template <int N, int... S>
+struct sequence_generator : sequence_generator<N - 1, N - 1, S...> {};
+template <int... S>
+struct sequence_generator<0, S...> {
+  typedef sequence<S...> type;
+};
+
+namespace internal {
+
+// Types YesType and NoType are guaranteed such that sizeof(YesType) <
+// sizeof(NoType).
+typedef char YesType;
+
+struct NoType {
+  YesType dummy[2];
+};
+
+// This class is an implementation detail for is_convertible, and you
+// don't need to know how it works to use is_convertible. For those
+// who care: we declare two different functions, one whose argument is
+// of type To and one with a variadic argument list. We give them
+// return types of different size, so we can use sizeof to trick the
+// compiler into telling us which function it would have chosen if we
+// had called it with an argument of type From.  See Alexandrescu's
+// _Modern C++ Design_ for more details on this sort of trick.
+
+struct ConvertHelper {
+  template <typename To>
+  static YesType Test(To);
+
+  template <typename To>
+  static NoType Test(...);
+
+  template <typename From>
+  static From& Create();
+};
+
+// Used to determine if a type is a struct/union/class. Inspired by Boost's
+// is_class type_trait implementation.
+struct IsClassHelper {
+  template <typename C>
+  static YesType Test(void(C::*)(void));
+
+  template <typename C>
+  static NoType Test(...);
+};
+
+}  // namespace internal
+
+// Inherits from true_type if From is convertible to To, false_type otherwise.
+//
+// Note that if the type is convertible, this will be a true_type REGARDLESS
+// of whether or not the conversion would emit a warning.
+template <typename From, typename To>
+struct is_convertible
+    : integral_constant<bool,
+                        sizeof(internal::ConvertHelper::Test<To>(
+                                   internal::ConvertHelper::Create<From>())) ==
+                        sizeof(internal::YesType)> {
+};
+
+template <typename T>
+struct is_class
+    : integral_constant<bool,
+                        sizeof(internal::IsClassHelper::Test<T>(0)) ==
+                            sizeof(internal::YesType)> {
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_TEMPLATE_UTIL_H_
diff --git a/base/testbase64.h b/base/testbase64.h
index fc9846f..39dd00c 100644
--- a/base/testbase64.h
+++ b/base/testbase64.h
@@ -1,19 +1,5 @@
-/*
- *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
+/* This file was generated by googleclient/talk/binary2header.sh */
 
-#ifndef WEBRTC_BASE_TESTBASE64_H_
-#define WEBRTC_BASE_TESTBASE64_H_
-
-
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/testbase64.h"
-
-#endif  // WEBRTC_BASE_TESTBASE64_H_
+static unsigned char testbase64[] = {
+0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xe1, 0x0d, 0x07, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x9e, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xbe, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xc3, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xcc, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd4, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xdc, 0x01, 0x32, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x3c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x04, 0x02, 0x13, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x02, 0xc4, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x53, 0x4f, 0x4e, 0x59, 0x00, 0x44, 0x53, 0x43, 0x2d, 0x50, 0x32, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x37, 0x2e, 0x30, 0x00, 0x32, 0x30, 0x30, 0x37, 0x3a, 0x30, 0x31, 0x3a, 0x33, 0x30, 0x20, 0x32, 0x33, 0x3a, 0x31, 0x30, 0x3a, 0x30, 0x34, 0x00, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x58, 0x20, 0x31, 0x30, 0x2e, 0x34, 0x2e, 0x38, 0x00, 0x00, 0x1c, 0x82, 0x9a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x6a, 0x82, 0x9d, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x72, 0x88, 0x22, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x88, 0x27, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x64, 0x00, 0x00, 0x90, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x02, 0x7a, 0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x02, 0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xa2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xaa, 0x92, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xb2, 0x92, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x92, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x92, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0xba, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0xa0, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0a, 0x32, 0x30, 0x30, 0x37, 0x3a, 0x30, 0x31, 0x3a, 0x32, 0x30, 0x20, 0x32, 0x33, 0x3a, 0x30, 0x35, 0x3a, 0x35, 0x32, 0x00, 0x32, 0x30, 0x30, 0x37, 0x3a, 0x30, 0x31, 0x3a, 0x32, 0x30, 0x20, 0x32, 0x33, 0x3a, 0x30, 0x35, 0x3a, 0x35, 0x32, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x12, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x1a, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x22, 0x02, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x09, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xed, 0x00, 0x0c, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x5f, 0x43, 0x4d, 0x00, 0x02, 0xff, 0xee, 0x00, 0x0e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0c, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0c, 0x09, 0x09, 0x0c, 0x11, 0x0b, 0x0a, 0x0b, 0x11, 0x15, 0x0f, 0x0c, 0x0c, 0x0f, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x01, 0x0d, 0x0b, 0x0b, 0x0d, 0x0e, 0x0d, 0x10, 0x0e, 0x0e, 0x10, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0x64, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x07, 0xff, 0xc4, 0x01, 0x3f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0c, 0x33, 0x01, 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xa1, 0xb1, 0x42, 0x23, 0x24, 0x15, 0x52, 0xc1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xd1, 0x43, 0x07, 0x25, 0x92, 0x53, 0xf0, 0xe1, 0xf1, 0x63, 0x73, 0x35, 0x16, 0xa2, 0xb2, 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xc2, 0xa3, 0x74, 0x36, 0x17, 0xd2, 0x55, 0xe2, 0x65, 0xf2, 0xb3, 0x84, 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x27, 0x94, 0xa4, 0x85, 0xb4, 0x95, 0xc4, 0xd4, 0xe4, 0xf4, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xa1, 0xb1, 0x42, 0x23, 0xc1, 0x52, 0xd1, 0xf0, 0x33, 0x24, 0x62, 0xe1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, 0x63, 0x73, 0x34, 0xf1, 0x25, 0x06, 0x16, 0xa2, 0xb2, 0x83, 0x07, 0x26, 0x35, 0xc2, 0xd2, 0x44, 0x93, 0x54, 0xa3, 0x17, 0x64, 0x45, 0x55, 0x36, 0x74, 0x65, 0xe2, 0xf2, 0xb3, 0x84, 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x94, 0xa4, 0x85, 0xb4, 0x95, 0xc4, 0xd4, 0xe4, 0xf4, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf2, 0xed, 0xb2, 0x8d, 0x4d, 0x45, 0xcd, 0x2f, 0x3f, 0x44, 0x68, 0x93, 0xc3, 0x58, 0xc8, 0xf1, 0x1f, 0x8a, 0x33, 0x86, 0xda, 0x58, 0xc1, 0xa0, 0x02, 0x4f, 0xc4, 0xa1, 0x69, 0xa5, 0x9b, 0x5b, 0x4b, 0x84, 0x73, 0xdf, 0xc9, 0x15, 0xf8, 0xe3, 0xd1, 0x0e, 0x07, 0x93, 0xf3, 0xd1, 0x0f, 0x1c, 0x17, 0xef, 0x2e, 0x3b, 0x5b, 0xdc, 0xff, 0x00, 0xdf, 0x42, 0xbf, 0x8f, 0x8e, 0xdc, 0x82, 0xca, 0xd8, 0x37, 0x11, 0xa9, 0x3d, 0x82, 0x69, 0x2b, 0xc4, 0x6d, 0xc9, 0x75, 0x25, 0xbc, 0xf7, 0xec, 0xa1, 0xb5, 0x74, 0x19, 0x5d, 0x2e, 0x8a, 0x9a, 0x4b, 0x89, 0x7d, 0xc4, 0x68, 0xc6, 0xf6, 0xfe, 0xb2, 0xa0, 0x30, 0x1d, 0x60, 0x86, 0x88, 0x8d, 0x49, 0x3e, 0x01, 0x11, 0x20, 0xa3, 0x8c, 0xb9, 0xb1, 0xaa, 0x62, 0xad, 0xbf, 0x18, 0x97, 0x43, 0x47, 0x1d, 0xd2, 0xaf, 0x04, 0xd9, 0xb8, 0xc8, 0x0d, 0x68, 0xe4, 0xf7, 0x3e, 0x48, 0xf1, 0x05, 0xbc, 0x25, 0xaa, 0x07, 0x71, 0xd9, 0x14, 0x78, 0xf6, 0x49, 0xb5, 0x90, 0xfd, 0xa7, 0xc6, 0x14, 0xfd, 0x1b, 0x1c, 0xff, 0x00, 0x4d, 0x8d, 0x2e, 0x73, 0x8c, 0x35, 0xa3, 0x52, 0x4f, 0x92, 0x48, 0xa6, 0x1a, 0x24, 0xb6, 0x2a, 0xfa, 0xa5, 0x9e, 0x60, 0x64, 0x39, 0x94, 0x13, 0xcb, 0x27, 0x73, 0x80, 0xf3, 0x0c, 0xf6, 0xff, 0x00, 0xd2, 0x5a, 0x78, 0xbf, 0x53, 0x71, 0xf6, 0x01, 0x75, 0xb6, 0x97, 0x6a, 0x25, 0xa1, 0xad, 0x1f, 0xf4, 0xb7, 0x23, 0x48, 0xb7, 0x94, 0x84, 0x97, 0x5b, 0xff, 0x00, 0x32, 0xa9, 0xdd, 0xfc, 0xed, 0x9b, 0x7e, 0x0d, 0x9e, 0x52, 0x4a, 0x95, 0x61, 0xff, 0xd0, 0xf3, 0x3b, 0xa7, 0x70, 0xee, 0x01, 0x8f, 0xb9, 0x59, 0xfa, 0x7e, 0xdf, 0xe4, 0xc8, 0xf9, 0x2a, 0xc2, 0x5c, 0x63, 0xc3, 0x54, 0x67, 0x87, 0x6e, 0x10, 0x35, 0x68, 0xd4, 0x79, 0x1e, 0x53, 0x4a, 0xe0, 0xdc, 0xe9, 0xb8, 0x1f, 0x6a, 0xda, 0x6c, 0x25, 0x94, 0x37, 0xb0, 0xd0, 0xb8, 0xad, 0x67, 0xe4, 0x55, 0x8a, 0x5b, 0x8b, 0x82, 0xc0, 0x6f, 0x76, 0x80, 0x34, 0x49, 0x05, 0x2e, 0x9e, 0xc6, 0x1c, 0x66, 0x31, 0xba, 0x10, 0x23, 0xe0, 0xaf, 0xe1, 0x61, 0x53, 0x43, 0x8d, 0x81, 0xb3, 0x67, 0xef, 0x9e, 0x49, 0x2a, 0x12, 0x6c, 0xb6, 0x63, 0x1a, 0x0c, 0x31, 0xba, 0x55, 0xcd, 0xac, 0xfa, 0x8e, 0xdf, 0x91, 0x6e, 0x91, 0xd9, 0xb3, 0xc9, 0x73, 0x90, 0x7a, 0xab, 0x6a, 0xc2, 0xa4, 0x60, 0xe2, 0x8f, 0xd2, 0x38, 0x03, 0x7d, 0x9e, 0x0d, 0xff, 0x00, 0xcc, 0xd6, 0xd3, 0x6b, 0x71, 0x67, 0xd2, 0x3e, 0x64, 0x72, 0xab, 0xdb, 0x8d, 0x54, 0x39, 0xc5, 0x83, 0x6b, 0x3d, 0xee, 0x2e, 0xd4, 0x92, 0x3c, 0x4a, 0x56, 0xba, 0xb4, 0x79, 0x5c, 0xf7, 0xb2, 0x96, 0x6c, 0x8d, 0xaf, 0x80, 0x48, 0x3c, 0xf0, 0xb2, 0x1f, 0x63, 0x9c, 0xe9, 0x3f, 0x24, 0x5c, 0xdb, 0xdd, 0x76, 0x43, 0xde, 0xfd, 0x5c, 0xe3, 0x24, 0xfc, 0x50, 0x00, 0x93, 0x0a, 0x78, 0x8a, 0x0d, 0x49, 0xca, 0xcf, 0x93, 0x63, 0x1b, 0x7d, 0xd7, 0x57, 0x50, 0xd5, 0xef, 0x70, 0x6b, 0x4f, 0xc7, 0x45, 0xdb, 0x74, 0x9e, 0x8d, 0x5e, 0x33, 0x83, 0xd8, 0x37, 0xdd, 0xc3, 0xac, 0x3d, 0xbf, 0x92, 0xc5, 0x5b, 0xea, 0xbf, 0xd5, 0x62, 0xc0, 0xdc, 0xbc, 0xbd, 0x2d, 0x22, 0x5a, 0xcf, 0xdd, 0x69, 0xff, 0x00, 0xd1, 0x8e, 0x5d, 0xa5, 0x38, 0xb5, 0xb0, 0x00, 0xc6, 0xc4, 0x24, 0x4a, 0xd6, 0x8d, 0x18, 0x04, 0x49, 0x88, 0x9e, 0x55, 0xd6, 0x61, 0xb0, 0xc1, 0x70, 0x32, 0xdd, 0x3c, 0x95, 0xda, 0xf1, 0xfe, 0xf5, 0x62, 0xbc, 0x76, 0x8e, 0x75, 0x28, 0x02, 0xa2, 0xe7, 0x7d, 0x92, 0xb9, 0x84, 0x96, 0x96, 0xda, 0xf7, 0x70, 0x12, 0x4e, 0x5a, 0xff, 0x00, 0xff, 0xd1, 0xf3, 0x7a, 0x21, 0xaf, 0xde, 0xef, 0xa2, 0x22, 0x55, 0xfc, 0x5a, 0xbd, 0x42, 0xfb, 0x08, 0xfa, 0x67, 0x4f, 0x82, 0xcd, 0x6d, 0x85, 0xc0, 0x56, 0x3b, 0x90, 0xb7, 0xf0, 0x2a, 0x0e, 0x63, 0x58, 0x3b, 0xf2, 0xa3, 0x9e, 0x8c, 0xb8, 0x86, 0xbe, 0x49, 0xf1, 0x2c, 0x0c, 0x86, 0xb4, 0x4c, 0x69, 0xe4, 0xaf, 0x6e, 0xcc, 0x6b, 0x7d, 0x46, 0xb3, 0x70, 0xec, 0x38, 0x51, 0x7d, 0x02, 0x8a, 0xc7, 0xa6, 0xd9, 0x20, 0x68, 0x0f, 0x8f, 0x8a, 0xcf, 0xc9, 0xc2, 0xea, 0x59, 0x5b, 0x48, 0xb0, 0x91, 0xae, 0xe6, 0xc9, 0x03, 0xc9, 0x30, 0x51, 0x66, 0xd4, 0x0d, 0xad, 0xbd, 0x5f, 0x53, 0xcc, 0x6b, 0xb6, 0x90, 0x5a, 0x3b, 0x83, 0x0b, 0x43, 0x17, 0x31, 0xd6, 0xc3, 0x6e, 0x12, 0x3b, 0x79, 0xac, 0xc1, 0x89, 0x47, 0xd9, 0xe8, 0x63, 0x98, 0x45, 0xed, 0x6c, 0x5a, 0xf1, 0xa0, 0x27, 0xc5, 0x5b, 0xc3, 0x6f, 0xa6, 0xe0, 0x1c, 0x7d, 0xb3, 0xa2, 0x69, 0x34, 0x7b, 0xae, 0x1a, 0x8d, 0x45, 0x17, 0x9d, 0xeb, 0xfd, 0x21, 0xd8, 0xb9, 0xae, 0xb5, 0x80, 0xbb, 0x1e, 0xd2, 0x5c, 0xd7, 0x78, 0x13, 0xf9, 0xae, 0x4b, 0xea, 0xc7, 0x4a, 0x39, 0xbd, 0x55, 0xb3, 0xed, 0x66, 0x38, 0xf5, 0x09, 0x22, 0x41, 0x23, 0xe8, 0x37, 0xfb, 0x4b, 0xa1, 0xeb, 0xd6, 0xfe, 0x88, 0x31, 0xbf, 0x41, 0xc0, 0xee, 0xd2, 0x74, 0x02, 0x78, 0x53, 0xfa, 0x97, 0x43, 0x19, 0x85, 0x65, 0xff, 0x00, 0x9d, 0x71, 0x33, 0xe4, 0x1a, 0x7d, 0x8d, 0x53, 0x42, 0x56, 0x35, 0x6b, 0xe5, 0x80, 0x06, 0xc7, 0x57, 0xa7, 0xc4, 0xa9, 0xdb, 0xb6, 0x81, 0x1f, 0xeb, 0xd9, 0x69, 0x56, 0xc2, 0xd0, 0x00, 0xe5, 0x55, 0xc0, 0x12, 0xc2, 0xd7, 0x4e, 0xa2, 0x5a, 0x7c, 0x0a, 0xd0, 0x63, 0x9a, 0xd1, 0xaf, 0xd2, 0xe2, 0x3c, 0x12, 0x62, 0x66, 0xc6, 0x42, 0x23, 0x5a, 0x49, 0x8f, 0x10, 0xa2, 0xd2, 0x3e, 0x28, 0x9d, 0xc4, 0x88, 0x09, 0x29, 0x16, 0xc3, 0x3c, 0x24, 0x8d, 0xe6, 0x92, 0x72, 0x1f, 0xff, 0xd2, 0xf3, 0xbb, 0xb0, 0xfe, 0xcb, 0x99, 0xe9, 0xce, 0xf6, 0x88, 0x2d, 0x77, 0x91, 0x5b, 0x3d, 0x3d, 0xd0, 0xe6, 0x90, 0xa9, 0x65, 0x57, 0x38, 0x95, 0xdd, 0xcb, 0x9a, 0x7d, 0xce, 0xf2, 0x3f, 0x44, 0x23, 0x60, 0x58, 0x76, 0xe9, 0xca, 0x8c, 0xea, 0x1b, 0x31, 0x02, 0x32, 0x23, 0xea, 0xee, 0xb1, 0xcd, 0xb0, 0xc7, 0x87, 0x74, 0x7a, 0xeb, 0x70, 0x1a, 0x71, 0xe1, 0xfe, 0xe4, 0x1c, 0x1d, 0xae, 0xe5, 0x69, 0xd8, 0xfa, 0x99, 0x50, 0x0d, 0x1a, 0xf7, 0x2a, 0x3a, 0x0c, 0xf4, 0x1a, 0x8e, 0xc7, 0x27, 0x5d, 0xbf, 0x18, 0x41, 0xdc, 0xc2, 0xf0, 0x7f, 0x74, 0xf6, 0x3a, 0x22, 0x66, 0xdb, 0x68, 0xc6, 0x80, 0x48, 0x6b, 0x88, 0x06, 0x39, 0x0d, 0xee, 0xaa, 0x1f, 0xb3, 0xd5, 0x1b, 0x83, 0xd8, 0x3b, 0x38, 0x8f, 0x69, 0xfe, 0xdf, 0xd1, 0x4d, 0x29, 0xa1, 0x4c, 0x7a, 0xf4, 0xbf, 0xa7, 0x92, 0xcf, 0xa5, 0x20, 0x08, 0xf3, 0xf6, 0xff, 0x00, 0x15, 0xbb, 0xd1, 0x31, 0xd9, 0x5e, 0x3d, 0x75, 0x56, 0x36, 0x88, 0x00, 0x81, 0xe0, 0x16, 0x5e, 0x55, 0x74, 0x3f, 0x00, 0x9d, 0xe0, 0xcc, 0x69, 0xe7, 0x3a, 0x2d, 0xbe, 0x90, 0x00, 0xa9, 0xae, 0xef, 0x1f, 0x95, 0x4b, 0x0d, 0x9a, 0xdc, 0xc7, 0x45, 0xfe, 0xb1, 0x7d, 0x60, 0xa7, 0xa1, 0xe0, 0x1f, 0x4e, 0x1d, 0x99, 0x69, 0x02, 0x9a, 0xcf, 0x1f, 0xca, 0x7b, 0xbf, 0x90, 0xc5, 0xc2, 0xb3, 0xeb, 0x57, 0xd6, 0x03, 0x6b, 0xae, 0x39, 0xb6, 0x82, 0xe3, 0x31, 0xa1, 0x68, 0xf2, 0x6b, 0x5c, 0x12, 0xfa, 0xe1, 0x91, 0x66, 0x47, 0x5d, 0xb8, 0x3b, 0x4f, 0x44, 0x36, 0xb6, 0x8f, 0x28, 0xdd, 0xff, 0x00, 0x7e, 0x46, 0xab, 0x12, 0x2b, 0x65, 0x55, 0x32, 0xa7, 0x62, 0xb6, 0xbd, 0xf7, 0x64, 0x10, 0xdb, 0x03, 0x9f, 0x1b, 0x9e, 0xc7, 0xd9, 0xb8, 0x3b, 0x1f, 0x67, 0xf3, 0x6c, 0x52, 0x80, 0xd7, 0x7d, 0x0f, 0xea, 0x7f, 0x5d, 0x1d, 0x67, 0xa6, 0x0b, 0x1e, 0x47, 0xda, 0x69, 0x3b, 0x2e, 0x03, 0xc7, 0xf3, 0x5f, 0x1f, 0xf0, 0x8b, 0xa1, 0x02, 0x46, 0xba, 0x79, 0xaf, 0x32, 0xff, 0x00, 0x16, 0xad, 0xca, 0x1d, 0x57, 0x2a, 0xdc, 0x79, 0x18, 0x41, 0xb0, 0xf6, 0x9e, 0xe4, 0x9f, 0xd0, 0x8f, 0xeb, 0x31, 0xab, 0xd2, 0x83, 0xa4, 0xcb, 0x8c, 0xb8, 0xa0, 0x42, 0x12, 0x7b, 0x67, 0x9f, 0x2f, 0xf5, 0x09, 0x26, 0x96, 0xc4, 0xce, 0xa9, 0x20, 0xa7, 0xff, 0xd3, 0xf3, 0x2f, 0xb4, 0x5d, 0xe9, 0x0a, 0xb7, 0x9f, 0x4c, 0x19, 0xdb, 0x3a, 0x2d, 0x5e, 0x94, 0xfd, 0xc4, 0xb7, 0xc5, 0x62, 0xf9, 0x2b, 0xfd, 0x2e, 0xe3, 0x5d, 0xe0, 0x7c, 0x13, 0x48, 0xd1, 0x92, 0x12, 0xa9, 0x0b, 0x7a, 0xbc, 0x2d, 0xc2, 0x7f, 0x92, 0x60, 0xab, 0x4e, 0x79, 0x2e, 0x00, 0xf0, 0xaa, 0xe1, 0xda, 0x3d, 0x43, 0xfc, 0xad, 0x55, 0xbb, 0x80, 0x79, 0x81, 0xa0, 0xe6, 0x54, 0x32, 0x6d, 0x02, 0xbe, 0xf3, 0x61, 0x81, 0xa8, 0x44, 0x14, 0x03, 0x59, 0x0e, 0x1c, 0xf6, 0x1f, 0xdc, 0xb2, 0xec, 0xa3, 0x23, 0x77, 0xe8, 0x6e, 0x70, 0xf2, 0x25, 0x1f, 0x1f, 0x17, 0xa9, 0x6d, 0x71, 0x36, 0x97, 0x47, 0x00, 0xa4, 0x02, 0xe0, 0x2c, 0x7c, 0xc1, 0xab, 0xd5, 0x31, 0x85, 0x35, 0xd4, 0xe6, 0x13, 0x02, 0xd6, 0x4b, 0x67, 0x48, 0x2b, 0xa9, 0xe9, 0x2e, 0x02, 0xb6, 0x4f, 0x82, 0xe5, 0x7a, 0x95, 0x19, 0xc6, 0x87, 0x3d, 0xfb, 0xa2, 0xb8, 0x79, 0x1e, 0x4d, 0x3b, 0x96, 0xcf, 0x4f, 0xbd, 0xcd, 0xa2, 0xa2, 0x1f, 0xa0, 0x82, 0xd3, 0xfc, 0x97, 0x05, 0x24, 0x36, 0x6b, 0xf3, 0x31, 0xa2, 0x35, 0x79, 0xef, 0xad, 0xf8, 0xae, 0xaf, 0xaf, 0xd8, 0xf2, 0xd8, 0x6d, 0xed, 0x6b, 0xda, 0x7b, 0x18, 0x1b, 0x5d, 0xff, 0x00, 0x52, 0xb1, 0x6d, 0xf0, 0x81, 0x31, 0xca, 0xf4, 0x6e, 0xb1, 0x80, 0xce, 0xb1, 0x84, 0xc0, 0x21, 0xb7, 0xd6, 0x77, 0x31, 0xd1, 0x27, 0xc1, 0xcd, 0xfe, 0xd2, 0xe3, 0xec, 0xe8, 0x1d, 0x45, 0x96, 0xb0, 0x9a, 0xb7, 0x87, 0x3f, 0x68, 0x2d, 0xf7, 0x01, 0x1f, 0xbe, 0xd1, 0xf4, 0x7f, 0xb4, 0xa4, 0x0d, 0x77, 0xbb, 0xfa, 0x8f, 0x80, 0x3a, 0x7f, 0x43, 0xaa, 0xe2, 0xdf, 0xd2, 0x65, 0x7e, 0x95, 0xe4, 0x0f, 0x1f, 0xa1, 0xfe, 0x6b, 0x16, 0x9f, 0x52, 0xfa, 0xc1, 0xd3, 0xba, 0x6d, 0x26, 0xdc, 0xac, 0x86, 0xd4, 0xd9, 0x0d, 0x31, 0x2e, 0x74, 0x9e, 0xdb, 0x59, 0x2e, 0x55, 0xe8, 0xc9, 0xb2, 0x96, 0xd5, 0x4b, 0x9f, 0xb8, 0x6d, 0xda, 0x1c, 0x04, 0x09, 0x03, 0xfe, 0x8a, 0xc6, 0xfa, 0xd3, 0xf5, 0x6a, 0xbe, 0xbb, 0x5b, 0x2e, 0xc6, 0xb5, 0x94, 0xe6, 0xd5, 0x20, 0x97, 0x7d, 0x1b, 0x1b, 0xf9, 0xad, 0x7c, 0x7d, 0x17, 0xb7, 0xf3, 0x1e, 0x92, 0x1b, 0x7f, 0xf8, 0xe0, 0x7d, 0x59, 0xdd, 0xfd, 0x32, 0xd8, 0x8f, 0xa5, 0xe8, 0x3a, 0x12, 0x5c, 0x3f, 0xfc, 0xc4, 0xfa, 0xc3, 0xb3, 0x77, 0xa7, 0x56, 0xed, 0xdb, 0x76, 0x7a, 0x8d, 0xdd, 0x1f, 0xbf, 0xfd, 0x44, 0x92, 0x56, 0x8f, 0xff, 0xd4, 0xf2, 0xe8, 0x86, 0x17, 0x1e, 0xfa, 0x04, 0x56, 0x4b, 0x43, 0x6c, 0x6f, 0x2d, 0xe5, 0x46, 0x01, 0x64, 0x2b, 0x14, 0x32, 0x5b, 0xb4, 0xa0, 0x52, 0x1d, 0xde, 0x9b, 0x94, 0xdb, 0xab, 0x6b, 0x81, 0xf7, 0x05, 0xb0, 0xd7, 0x07, 0xb2, 0x27, 0x55, 0xc6, 0x57, 0x65, 0xd8, 0x76, 0x6e, 0x64, 0xed, 0xee, 0x16, 0xce, 0x27, 0x57, 0x63, 0xda, 0x0c, 0xc2, 0x8e, 0x51, 0x67, 0x84, 0xfa, 0x1d, 0xdd, 0x62, 0xc7, 0x07, 0xe9, 0xf7, 0xa3, 0xd6, 0x6c, 0x02, 0x41, 0x55, 0x31, 0xf3, 0x2b, 0xb3, 0xba, 0x2b, 0x2e, 0x68, 0x24, 0x1d, 0x47, 0x64, 0xca, 0xa6, 0x50, 0x41, 0x65, 0x90, 0x6c, 0xb1, 0xa5, 0xae, 0x33, 0x23, 0x51, 0xe4, 0xab, 0x7d, 0x5d, 0xcb, 0xb6, 0xcc, 0x37, 0xd0, 0x40, 0x73, 0x71, 0xde, 0x58, 0x09, 0xe7, 0x6f, 0x2c, 0x44, 0xc9, 0xc9, 0xae, 0xba, 0x9d, 0x63, 0x88, 0x01, 0xa0, 0x95, 0x9d, 0xf5, 0x3f, 0x2a, 0xe6, 0x67, 0xdb, 0x50, 0x83, 0x55, 0xad, 0x36, 0x3e, 0x78, 0x10, 0x74, 0x77, 0xfd, 0x2d, 0xaa, 0x4c, 0x7d, 0x58, 0x73, 0x91, 0xa0, 0x0f, 0x51, 0x45, 0xb7, 0x33, 0xdd, 0x58, 0x69, 0x1d, 0xd8, 0x0c, 0x9f, 0x96, 0x88, 0x19, 0x99, 0x19, 0xac, 0xcf, 0xa3, 0xd2, 0xad, 0xb5, 0xdb, 0x76, 0x8f, 0xad, 0xc4, 0xea, 0xcf, 0xdf, 0x7e, 0xdf, 0xdd, 0xfc, 0xd5, 0xa3, 0x5e, 0x43, 0x2b, 0x6b, 0xb2, 0xad, 0x3b, 0x6a, 0xa4, 0x13, 0xa7, 0x04, 0xac, 0x7a, 0x6f, 0xb3, 0x23, 0x26, 0xcc, 0xfb, 0xb4, 0x75, 0x8e, 0x01, 0x83, 0xf7, 0x58, 0x3e, 0x8b, 0x53, 0xa7, 0x2a, 0x1a, 0x31, 0x42, 0x36, 0x5d, 0x4c, 0x9a, 0xf2, 0xdc, 0xc6, 0xfe, 0x98, 0xb4, 0x34, 0xcb, 0x48, 0x0a, 0x8f, 0xdb, 0xb2, 0xeb, 0x76, 0xd6, 0x07, 0x5c, 0x59, 0xc9, 0x64, 0x8f, 0x93, 0xa7, 0x73, 0x16, 0x83, 0xaf, 0x0e, 0xa4, 0x33, 0xef, 0x50, 0xc5, 0x0c, 0xda, 0x59, 0x10, 0x06, 0x8a, 0x2e, 0x29, 0x0e, 0xac, 0xc2, 0x31, 0x3d, 0x36, 0x69, 0x7e, 0xd6, 0xcc, 0xf5, 0x3d, 0x6f, 0xb3, 0xeb, 0x1b, 0x76, 0xef, 0x3b, 0xa3, 0xfa, 0xc9, 0x2b, 0x5f, 0x66, 0x6f, 0xa9, 0x1e, 0x73, 0xf2, 0x49, 0x2e, 0x39, 0xf7, 0x4f, 0xb7, 0x8d, 0xff, 0xd5, 0xf3, 0x26, 0xfe, 0x0a, 0xc5, 0x1b, 0xa7, 0xcb, 0xb2, 0xcf, 0x49, 0x03, 0xb2, 0x46, 0xee, 0xd9, 0xd9, 0xb3, 0xf4, 0x9f, 0x25, 0x4a, 0xdf, 0x4b, 0x77, 0xe8, 0x27, 0xd4, 0xef, 0x1c, 0x2a, 0x29, 0x26, 0xc5, 0x7c, 0x9d, 0x6c, 0x7f, 0xb7, 0x6e, 0x1b, 0x26, 0x7f, 0x05, 0xa3, 0xfe, 0x53, 0x8d, 0x62, 0x57, 0x30, 0x92, 0x12, 0xfa, 0x2f, 0x86, 0xdf, 0xa4, 0xec, 0x67, 0xfe, 0xd0, 0xf4, 0xff, 0x00, 0x4d, 0xfc, 0xdf, 0x78, 0xe1, 0x68, 0x7d, 0x54, 0x99, 0xbf, 0x6f, 0xf3, 0xbe, 0xdf, 0x8e, 0xdd, 0x7f, 0xef, 0xeb, 0x97, 0x49, 0x3e, 0x3b, 0x7f, 0x06, 0x2c, 0x9f, 0x37, 0x5f, 0xf0, 0x9f, 0x4c, 0xeb, 0x7b, 0xbf, 0x67, 0x55, 0xe8, 0xff, 0x00, 0x31, 0xbc, 0x7a, 0x9e, 0x31, 0xdb, 0xfe, 0x92, 0xae, 0x37, 0x7a, 0x4d, 0xdb, 0xe2, 0x17, 0x9d, 0xa4, 0xa3, 0xc9, 0xba, 0xfc, 0x7b, 0x7d, 0x5f, 0x52, 0xa7, 0x7e, 0xd1, 0x28, 0xf8, 0xf3, 0xb0, 0xc7, 0x32, 0xbc, 0x99, 0x24, 0xc5, 0xe3, 0xab, 0xeb, 0x1f, 0xa4, 0xf5, 0xfc, 0xe1, 0x25, 0xe4, 0xe9, 0x24, 0x97, 0xff, 0xd9, 0xff, 0xed, 0x2e, 0x1c, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x33, 0x2e, 0x30, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x1c, 0x02, 0x00, 0x00, 0x02, 0x00, 0x02, 0x1c, 0x02, 0x78, 0x00, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfb, 0x09, 0xa6, 0xbd, 0x07, 0x4c, 0x2a, 0x36, 0x9d, 0x8f, 0xe2, 0xcc, 0x57, 0xa9, 0xac, 0x85, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xea, 0x00, 0x00, 0x00, 0x00, 0x1d, 0xb0, 0x3c, 0x3f, 0x78, 0x6d, 0x6c, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x2e, 0x30, 0x22, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3d, 0x22, 0x55, 0x54, 0x46, 0x2d, 0x38, 0x22, 0x3f, 0x3e, 0x0a, 0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x20, 0x22, 0x2d, 0x2f, 0x2f, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x2f, 0x44, 0x54, 0x44, 0x20, 0x50, 0x4c, 0x49, 0x53, 0x54, 0x20, 0x31, 0x2e, 0x30, 0x2f, 0x2f, 0x45, 0x4e, 0x22, 0x20, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x54, 0x44, 0x73, 0x2f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x2d, 0x31, 0x2e, 0x30, 0x2e, 0x64, 0x74, 0x64, 0x22, 0x3e, 0x0a, 0x3c, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x31, 0x2e, 0x30, 0x22, 0x3e, 0x0a, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x48, 0x6f, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x48, 0x6f, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x31, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x31, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x56, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x56, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x32, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x56, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x56, 0x65, 0x72, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x31, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x73, 0x75, 0x62, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x70, 0x61, 0x70, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x30, 0x2e, 0x30, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x30, 0x2e, 0x30, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x33, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x35, 0x37, 0x36, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x70, 0x65, 0x72, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x50, 0x4d, 0x41, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x70, 0x65, 0x72, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x2d, 0x31, 0x38, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x2d, 0x31, 0x38, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x37, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x35, 0x39, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x50, 0x61, 0x70, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x50, 0x61, 0x70, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x6e, 0x61, 0x2d, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x33, 0x2d, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x54, 0x31, 0x37, 0x3a, 0x34, 0x39, 0x3a, 0x33, 0x36, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x31, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x55, 0x6e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x55, 0x6e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x30, 0x2e, 0x30, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x30, 0x2e, 0x30, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x33, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x35, 0x37, 0x36, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x55, 0x6e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x70, 0x65, 0x72, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x50, 0x4d, 0x55, 0x6e, 0x61, 0x64, 0x6a, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x70, 0x65, 0x72, 0x52, 0x65, 0x63, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x2d, 0x31, 0x38, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x2d, 0x31, 0x38, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x37, 0x37, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x35, 0x39, 0x34, 0x3c, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x2d, 0x33, 0x30, 0x54, 0x32, 0x32, 0x3a, 0x30, 0x38, 0x3a, 0x34, 0x31, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x30, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x70, 0x64, 0x2e, 0x50, 0x4d, 0x50, 0x61, 0x70, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x41, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x70, 0x64, 0x2e, 0x50, 0x4d, 0x50, 0x61, 0x70, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x55, 0x53, 0x20, 0x4c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x70, 0x6d, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x44, 0x61, 0x74, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x30, 0x33, 0x2d, 0x30, 0x37, 0x2d, 0x30, 0x31, 0x54, 0x31, 0x37, 0x3a, 0x34, 0x39, 0x3a, 0x33, 0x36, 0x5a, 0x3c, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x31, 0x3c, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x09, 0x3c, 0x2f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x41, 0x50, 0x49, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x30, 0x30, 0x2e, 0x32, 0x30, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2f, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x70, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x41, 0x50, 0x49, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x30, 0x30, 0x2e, 0x32, 0x30, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2f, 0x3e, 0x0a, 0x09, 0x3c, 0x6b, 0x65, 0x79, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x2f, 0x6b, 0x65, 0x79, 0x3e, 0x0a, 0x09, 0x3c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x3c, 0x2f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3e, 0x0a, 0x3c, 0x2f, 0x64, 0x69, 0x63, 0x74, 0x3e, 0x0a, 0x3c, 0x2f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x3e, 0x0a, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x00, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02, 0xde, 0x02, 0x40, 0xff, 0xee, 0xff, 0xee, 0x03, 0x06, 0x02, 0x52, 0x03, 0x67, 0x05, 0x28, 0x03, 0xfc, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd8, 0x02, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x01, 0x7f, 0xff, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x08, 0x00, 0x19, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x2f, 0x66, 0x66, 0x00, 0x01, 0x00, 0x6c, 0x66, 0x66, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2f, 0x66, 0x66, 0x00, 0x01, 0x00, 0xa1, 0x99, 0x9a, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4d, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x03, 0x45, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x08, 0x00, 0x44, 0x00, 0x53, 0x00, 0x43, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x32, 0x00, 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x4f, 0x62, 0x6a, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, 0x74, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x70, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x74, 0x6f, 0x6d, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x68, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x06, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x56, 0x6c, 0x4c, 0x73, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x62, 0x6a, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x07, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x49, 0x44, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x0c, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x6f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x0a, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6d, 0x67, 0x20, 0x00, 0x00, 0x00, 0x06, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x4f, 0x62, 0x6a, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, 0x74, 0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x70, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x74, 0x6f, 0x6d, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x68, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, 0x6c, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6c, 0x6c, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x73, 0x67, 0x65, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x61, 0x6c, 0x74, 0x54, 0x61, 0x67, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x63, 0x65, 0x6c, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x49, 0x73, 0x48, 0x54, 0x4d, 0x4c, 0x62, 0x6f, 0x6f, 0x6c, 0x01, 0x00, 0x00, 0x00, 0x08, 0x63, 0x65, 0x6c, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x68, 0x6f, 0x72, 0x7a, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x0f, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x48, 0x6f, 0x72, 0x7a, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x00, 0x00, 0x00, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x00, 0x09, 0x76, 0x65, 0x72, 0x74, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x0f, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x56, 0x65, 0x72, 0x74, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x00, 0x00, 0x00, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x00, 0x0b, 0x62, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x11, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x42, 0x47, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x09, 0x74, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6c, 0x65, 0x66, 0x74, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x72, 0x69, 0x67, 0x68, 0x74, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x09, 0xf9, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0x09, 0xdd, 0x00, 0x18, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x02, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xed, 0x00, 0x0c, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x5f, 0x43, 0x4d, 0x00, 0x02, 0xff, 0xee, 0x00, 0x0e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0c, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0c, 0x09, 0x09, 0x0c, 0x11, 0x0b, 0x0a, 0x0b, 0x11, 0x15, 0x0f, 0x0c, 0x0c, 0x0f, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, 0x18, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x01, 0x0d, 0x0b, 0x0b, 0x0d, 0x0e, 0x0d, 0x10, 0x0e, 0x0e, 0x10, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0x64, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x07, 0xff, 0xc4, 0x01, 0x3f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x01, 0x04, 0x01, 0x03, 0x02, 0x04, 0x02, 0x05, 0x07, 0x06, 0x08, 0x05, 0x03, 0x0c, 0x33, 0x01, 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x05, 0x41, 0x51, 0x61, 0x13, 0x22, 0x71, 0x81, 0x32, 0x06, 0x14, 0x91, 0xa1, 0xb1, 0x42, 0x23, 0x24, 0x15, 0x52, 0xc1, 0x62, 0x33, 0x34, 0x72, 0x82, 0xd1, 0x43, 0x07, 0x25, 0x92, 0x53, 0xf0, 0xe1, 0xf1, 0x63, 0x73, 0x35, 0x16, 0xa2, 0xb2, 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xc2, 0xa3, 0x74, 0x36, 0x17, 0xd2, 0x55, 0xe2, 0x65, 0xf2, 0xb3, 0x84, 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x27, 0x94, 0xa4, 0x85, 0xb4, 0x95, 0xc4, 0xd4, 0xe4, 0xf4, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x35, 0x01, 0x00, 0x02, 0x11, 0x03, 0x21, 0x31, 0x12, 0x04, 0x41, 0x51, 0x61, 0x71, 0x22, 0x13, 0x05, 0x32, 0x81, 0x91, 0x14, 0xa1, 0xb1, 0x42, 0x23, 0xc1, 0x52, 0xd1, 0xf0, 0x33, 0x24, 0x62, 0xe1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, 0x63, 0x73, 0x34, 0xf1, 0x25, 0x06, 0x16, 0xa2, 0xb2, 0x83, 0x07, 0x26, 0x35, 0xc2, 0xd2, 0x44, 0x93, 0x54, 0xa3, 0x17, 0x64, 0x45, 0x55, 0x36, 0x74, 0x65, 0xe2, 0xf2, 0xb3, 0x84, 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x94, 0xa4, 0x85, 0xb4, 0x95, 0xc4, 0xd4, 0xe4, 0xf4, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf2, 0xed, 0xb2, 0x8d, 0x4d, 0x45, 0xcd, 0x2f, 0x3f, 0x44, 0x68, 0x93, 0xc3, 0x58, 0xc8, 0xf1, 0x1f, 0x8a, 0x33, 0x86, 0xda, 0x58, 0xc1, 0xa0, 0x02, 0x4f, 0xc4, 0xa1, 0x69, 0xa5, 0x9b, 0x5b, 0x4b, 0x84, 0x73, 0xdf, 0xc9, 0x15, 0xf8, 0xe3, 0xd1, 0x0e, 0x07, 0x93, 0xf3, 0xd1, 0x0f, 0x1c, 0x17, 0xef, 0x2e, 0x3b, 0x5b, 0xdc, 0xff, 0x00, 0xdf, 0x42, 0xbf, 0x8f, 0x8e, 0xdc, 0x82, 0xca, 0xd8, 0x37, 0x11, 0xa9, 0x3d, 0x82, 0x69, 0x2b, 0xc4, 0x6d, 0xc9, 0x75, 0x25, 0xbc, 0xf7, 0xec, 0xa1, 0xb5, 0x74, 0x19, 0x5d, 0x2e, 0x8a, 0x9a, 0x4b, 0x89, 0x7d, 0xc4, 0x68, 0xc6, 0xf6, 0xfe, 0xb2, 0xa0, 0x30, 0x1d, 0x60, 0x86, 0x88, 0x8d, 0x49, 0x3e, 0x01, 0x11, 0x20, 0xa3, 0x8c, 0xb9, 0xb1, 0xaa, 0x62, 0xad, 0xbf, 0x18, 0x97, 0x43, 0x47, 0x1d, 0xd2, 0xaf, 0x04, 0xd9, 0xb8, 0xc8, 0x0d, 0x68, 0xe4, 0xf7, 0x3e, 0x48, 0xf1, 0x05, 0xbc, 0x25, 0xaa, 0x07, 0x71, 0xd9, 0x14, 0x78, 0xf6, 0x49, 0xb5, 0x90, 0xfd, 0xa7, 0xc6, 0x14, 0xfd, 0x1b, 0x1c, 0xff, 0x00, 0x4d, 0x8d, 0x2e, 0x73, 0x8c, 0x35, 0xa3, 0x52, 0x4f, 0x92, 0x48, 0xa6, 0x1a, 0x24, 0xb6, 0x2a, 0xfa, 0xa5, 0x9e, 0x60, 0x64, 0x39, 0x94, 0x13, 0xcb, 0x27, 0x73, 0x80, 0xf3, 0x0c, 0xf6, 0xff, 0x00, 0xd2, 0x5a, 0x78, 0xbf, 0x53, 0x71, 0xf6, 0x01, 0x75, 0xb6, 0x97, 0x6a, 0x25, 0xa1, 0xad, 0x1f, 0xf4, 0xb7, 0x23, 0x48, 0xb7, 0x94, 0x84, 0x97, 0x5b, 0xff, 0x00, 0x32, 0xa9, 0xdd, 0xfc, 0xed, 0x9b, 0x7e, 0x0d, 0x9e, 0x52, 0x4a, 0x95, 0x61, 0xff, 0xd0, 0xf3, 0x3b, 0xa7, 0x70, 0xee, 0x01, 0x8f, 0xb9, 0x59, 0xfa, 0x7e, 0xdf, 0xe4, 0xc8, 0xf9, 0x2a, 0xc2, 0x5c, 0x63, 0xc3, 0x54, 0x67, 0x87, 0x6e, 0x10, 0x35, 0x68, 0xd4, 0x79, 0x1e, 0x53, 0x4a, 0xe0, 0xdc, 0xe9, 0xb8, 0x1f, 0x6a, 0xda, 0x6c, 0x25, 0x94, 0x37, 0xb0, 0xd0, 0xb8, 0xad, 0x67, 0xe4, 0x55, 0x8a, 0x5b, 0x8b, 0x82, 0xc0, 0x6f, 0x76, 0x80, 0x34, 0x49, 0x05, 0x2e, 0x9e, 0xc6, 0x1c, 0x66, 0x31, 0xba, 0x10, 0x23, 0xe0, 0xaf, 0xe1, 0x61, 0x53, 0x43, 0x8d, 0x81, 0xb3, 0x67, 0xef, 0x9e, 0x49, 0x2a, 0x12, 0x6c, 0xb6, 0x63, 0x1a, 0x0c, 0x31, 0xba, 0x55, 0xcd, 0xac, 0xfa, 0x8e, 0xdf, 0x91, 0x6e, 0x91, 0xd9, 0xb3, 0xc9, 0x73, 0x90, 0x7a, 0xab, 0x6a, 0xc2, 0xa4, 0x60, 0xe2, 0x8f, 0xd2, 0x38, 0x03, 0x7d, 0x9e, 0x0d, 0xff, 0x00, 0xcc, 0xd6, 0xd3, 0x6b, 0x71, 0x67, 0xd2, 0x3e, 0x64, 0x72, 0xab, 0xdb, 0x8d, 0x54, 0x39, 0xc5, 0x83, 0x6b, 0x3d, 0xee, 0x2e, 0xd4, 0x92, 0x3c, 0x4a, 0x56, 0xba, 0xb4, 0x79, 0x5c, 0xf7, 0xb2, 0x96, 0x6c, 0x8d, 0xaf, 0x80, 0x48, 0x3c, 0xf0, 0xb2, 0x1f, 0x63, 0x9c, 0xe9, 0x3f, 0x24, 0x5c, 0xdb, 0xdd, 0x76, 0x43, 0xde, 0xfd, 0x5c, 0xe3, 0x24, 0xfc, 0x50, 0x00, 0x93, 0x0a, 0x78, 0x8a, 0x0d, 0x49, 0xca, 0xcf, 0x93, 0x63, 0x1b, 0x7d, 0xd7, 0x57, 0x50, 0xd5, 0xef, 0x70, 0x6b, 0x4f, 0xc7, 0x45, 0xdb, 0x74, 0x9e, 0x8d, 0x5e, 0x33, 0x83, 0xd8, 0x37, 0xdd, 0xc3, 0xac, 0x3d, 0xbf, 0x92, 0xc5, 0x5b, 0xea, 0xbf, 0xd5, 0x62, 0xc0, 0xdc, 0xbc, 0xbd, 0x2d, 0x22, 0x5a, 0xcf, 0xdd, 0x69, 0xff, 0x00, 0xd1, 0x8e, 0x5d, 0xa5, 0x38, 0xb5, 0xb0, 0x00, 0xc6, 0xc4, 0x24, 0x4a, 0xd6, 0x8d, 0x18, 0x04, 0x49, 0x88, 0x9e, 0x55, 0xd6, 0x61, 0xb0, 0xc1, 0x70, 0x32, 0xdd, 0x3c, 0x95, 0xda, 0xf1, 0xfe, 0xf5, 0x62, 0xbc, 0x76, 0x8e, 0x75, 0x28, 0x02, 0xa2, 0xe7, 0x7d, 0x92, 0xb9, 0x84, 0x96, 0x96, 0xda, 0xf7, 0x70, 0x12, 0x4e, 0x5a, 0xff, 0x00, 0xff, 0xd1, 0xf3, 0x7a, 0x21, 0xaf, 0xde, 0xef, 0xa2, 0x22, 0x55, 0xfc, 0x5a, 0xbd, 0x42, 0xfb, 0x08, 0xfa, 0x67, 0x4f, 0x82, 0xcd, 0x6d, 0x85, 0xc0, 0x56, 0x3b, 0x90, 0xb7, 0xf0, 0x2a, 0x0e, 0x63, 0x58, 0x3b, 0xf2, 0xa3, 0x9e, 0x8c, 0xb8, 0x86, 0xbe, 0x49, 0xf1, 0x2c, 0x0c, 0x86, 0xb4, 0x4c, 0x69, 0xe4, 0xaf, 0x6e, 0xcc, 0x6b, 0x7d, 0x46, 0xb3, 0x70, 0xec, 0x38, 0x51, 0x7d, 0x02, 0x8a, 0xc7, 0xa6, 0xd9, 0x20, 0x68, 0x0f, 0x8f, 0x8a, 0xcf, 0xc9, 0xc2, 0xea, 0x59, 0x5b, 0x48, 0xb0, 0x91, 0xae, 0xe6, 0xc9, 0x03, 0xc9, 0x30, 0x51, 0x66, 0xd4, 0x0d, 0xad, 0xbd, 0x5f, 0x53, 0xcc, 0x6b, 0xb6, 0x90, 0x5a, 0x3b, 0x83, 0x0b, 0x43, 0x17, 0x31, 0xd6, 0xc3, 0x6e, 0x12, 0x3b, 0x79, 0xac, 0xc1, 0x89, 0x47, 0xd9, 0xe8, 0x63, 0x98, 0x45, 0xed, 0x6c, 0x5a, 0xf1, 0xa0, 0x27, 0xc5, 0x5b, 0xc3, 0x6f, 0xa6, 0xe0, 0x1c, 0x7d, 0xb3, 0xa2, 0x69, 0x34, 0x7b, 0xae, 0x1a, 0x8d, 0x45, 0x17, 0x9d, 0xeb, 0xfd, 0x21, 0xd8, 0xb9, 0xae, 0xb5, 0x80, 0xbb, 0x1e, 0xd2, 0x5c, 0xd7, 0x78, 0x13, 0xf9, 0xae, 0x4b, 0xea, 0xc7, 0x4a, 0x39, 0xbd, 0x55, 0xb3, 0xed, 0x66, 0x38, 0xf5, 0x09, 0x22, 0x41, 0x23, 0xe8, 0x37, 0xfb, 0x4b, 0xa1, 0xeb, 0xd6, 0xfe, 0x88, 0x31, 0xbf, 0x41, 0xc0, 0xee, 0xd2, 0x74, 0x02, 0x78, 0x53, 0xfa, 0x97, 0x43, 0x19, 0x85, 0x65, 0xff, 0x00, 0x9d, 0x71, 0x33, 0xe4, 0x1a, 0x7d, 0x8d, 0x53, 0x42, 0x56, 0x35, 0x6b, 0xe5, 0x80, 0x06, 0xc7, 0x57, 0xa7, 0xc4, 0xa9, 0xdb, 0xb6, 0x81, 0x1f, 0xeb, 0xd9, 0x69, 0x56, 0xc2, 0xd0, 0x00, 0xe5, 0x55, 0xc0, 0x12, 0xc2, 0xd7, 0x4e, 0xa2, 0x5a, 0x7c, 0x0a, 0xd0, 0x63, 0x9a, 0xd1, 0xaf, 0xd2, 0xe2, 0x3c, 0x12, 0x62, 0x66, 0xc6, 0x42, 0x23, 0x5a, 0x49, 0x8f, 0x10, 0xa2, 0xd2, 0x3e, 0x28, 0x9d, 0xc4, 0x88, 0x09, 0x29, 0x16, 0xc3, 0x3c, 0x24, 0x8d, 0xe6, 0x92, 0x72, 0x1f, 0xff, 0xd2, 0xf3, 0xbb, 0xb0, 0xfe, 0xcb, 0x99, 0xe9, 0xce, 0xf6, 0x88, 0x2d, 0x77, 0x91, 0x5b, 0x3d, 0x3d, 0xd0, 0xe6, 0x90, 0xa9, 0x65, 0x57, 0x38, 0x95, 0xdd, 0xcb, 0x9a, 0x7d, 0xce, 0xf2, 0x3f, 0x44, 0x23, 0x60, 0x58, 0x76, 0xe9, 0xca, 0x8c, 0xea, 0x1b, 0x31, 0x02, 0x32, 0x23, 0xea, 0xee, 0xb1, 0xcd, 0xb0, 0xc7, 0x87, 0x74, 0x7a, 0xeb, 0x70, 0x1a, 0x71, 0xe1, 0xfe, 0xe4, 0x1c, 0x1d, 0xae, 0xe5, 0x69, 0xd8, 0xfa, 0x99, 0x50, 0x0d, 0x1a, 0xf7, 0x2a, 0x3a, 0x0c, 0xf4, 0x1a, 0x8e, 0xc7, 0x27, 0x5d, 0xbf, 0x18, 0x41, 0xdc, 0xc2, 0xf0, 0x7f, 0x74, 0xf6, 0x3a, 0x22, 0x66, 0xdb, 0x68, 0xc6, 0x80, 0x48, 0x6b, 0x88, 0x06, 0x39, 0x0d, 0xee, 0xaa, 0x1f, 0xb3, 0xd5, 0x1b, 0x83, 0xd8, 0x3b, 0x38, 0x8f, 0x69, 0xfe, 0xdf, 0xd1, 0x4d, 0x29, 0xa1, 0x4c, 0x7a, 0xf4, 0xbf, 0xa7, 0x92, 0xcf, 0xa5, 0x20, 0x08, 0xf3, 0xf6, 0xff, 0x00, 0x15, 0xbb, 0xd1, 0x31, 0xd9, 0x5e, 0x3d, 0x75, 0x56, 0x36, 0x88, 0x00, 0x81, 0xe0, 0x16, 0x5e, 0x55, 0x74, 0x3f, 0x00, 0x9d, 0xe0, 0xcc, 0x69, 0xe7, 0x3a, 0x2d, 0xbe, 0x90, 0x00, 0xa9, 0xae, 0xef, 0x1f, 0x95, 0x4b, 0x0d, 0x9a, 0xdc, 0xc7, 0x45, 0xfe, 0xb1, 0x7d, 0x60, 0xa7, 0xa1, 0xe0, 0x1f, 0x4e, 0x1d, 0x99, 0x69, 0x02, 0x9a, 0xcf, 0x1f, 0xca, 0x7b, 0xbf, 0x90, 0xc5, 0xc2, 0xb3, 0xeb, 0x57, 0xd6, 0x03, 0x6b, 0xae, 0x39, 0xb6, 0x82, 0xe3, 0x31, 0xa1, 0x68, 0xf2, 0x6b, 0x5c, 0x12, 0xfa, 0xe1, 0x91, 0x66, 0x47, 0x5d, 0xb8, 0x3b, 0x4f, 0x44, 0x36, 0xb6, 0x8f, 0x28, 0xdd, 0xff, 0x00, 0x7e, 0x46, 0xab, 0x12, 0x2b, 0x65, 0x55, 0x32, 0xa7, 0x62, 0xb6, 0xbd, 0xf7, 0x64, 0x10, 0xdb, 0x03, 0x9f, 0x1b, 0x9e, 0xc7, 0xd9, 0xb8, 0x3b, 0x1f, 0x67, 0xf3, 0x6c, 0x52, 0x80, 0xd7, 0x7d, 0x0f, 0xea, 0x7f, 0x5d, 0x1d, 0x67, 0xa6, 0x0b, 0x1e, 0x47, 0xda, 0x69, 0x3b, 0x2e, 0x03, 0xc7, 0xf3, 0x5f, 0x1f, 0xf0, 0x8b, 0xa1, 0x02, 0x46, 0xba, 0x79, 0xaf, 0x32, 0xff, 0x00, 0x16, 0xad, 0xca, 0x1d, 0x57, 0x2a, 0xdc, 0x79, 0x18, 0x41, 0xb0, 0xf6, 0x9e, 0xe4, 0x9f, 0xd0, 0x8f, 0xeb, 0x31, 0xab, 0xd2, 0x83, 0xa4, 0xcb, 0x8c, 0xb8, 0xa0, 0x42, 0x12, 0x7b, 0x67, 0x9f, 0x2f, 0xf5, 0x09, 0x26, 0x96, 0xc4, 0xce, 0xa9, 0x20, 0xa7, 0xff, 0xd3, 0xf3, 0x2f, 0xb4, 0x5d, 0xe9, 0x0a, 0xb7, 0x9f, 0x4c, 0x19, 0xdb, 0x3a, 0x2d, 0x5e, 0x94, 0xfd, 0xc4, 0xb7, 0xc5, 0x62, 0xf9, 0x2b, 0xfd, 0x2e, 0xe3, 0x5d, 0xe0, 0x7c, 0x13, 0x48, 0xd1, 0x92, 0x12, 0xa9, 0x0b, 0x7a, 0xbc, 0x2d, 0xc2, 0x7f, 0x92, 0x60, 0xab, 0x4e, 0x79, 0x2e, 0x00, 0xf0, 0xaa, 0xe1, 0xda, 0x3d, 0x43, 0xfc, 0xad, 0x55, 0xbb, 0x80, 0x79, 0x81, 0xa0, 0xe6, 0x54, 0x32, 0x6d, 0x02, 0xbe, 0xf3, 0x61, 0x81, 0xa8, 0x44, 0x14, 0x03, 0x59, 0x0e, 0x1c, 0xf6, 0x1f, 0xdc, 0xb2, 0xec, 0xa3, 0x23, 0x77, 0xe8, 0x6e, 0x70, 0xf2, 0x25, 0x1f, 0x1f, 0x17, 0xa9, 0x6d, 0x71, 0x36, 0x97, 0x47, 0x00, 0xa4, 0x02, 0xe0, 0x2c, 0x7c, 0xc1, 0xab, 0xd5, 0x31, 0x85, 0x35, 0xd4, 0xe6, 0x13, 0x02, 0xd6, 0x4b, 0x67, 0x48, 0x2b, 0xa9, 0xe9, 0x2e, 0x02, 0xb6, 0x4f, 0x82, 0xe5, 0x7a, 0x95, 0x19, 0xc6, 0x87, 0x3d, 0xfb, 0xa2, 0xb8, 0x79, 0x1e, 0x4d, 0x3b, 0x96, 0xcf, 0x4f, 0xbd, 0xcd, 0xa2, 0xa2, 0x1f, 0xa0, 0x82, 0xd3, 0xfc, 0x97, 0x05, 0x24, 0x36, 0x6b, 0xf3, 0x31, 0xa2, 0x35, 0x79, 0xef, 0xad, 0xf8, 0xae, 0xaf, 0xaf, 0xd8, 0xf2, 0xd8, 0x6d, 0xed, 0x6b, 0xda, 0x7b, 0x18, 0x1b, 0x5d, 0xff, 0x00, 0x52, 0xb1, 0x6d, 0xf0, 0x81, 0x31, 0xca, 0xf4, 0x6e, 0xb1, 0x80, 0xce, 0xb1, 0x84, 0xc0, 0x21, 0xb7, 0xd6, 0x77, 0x31, 0xd1, 0x27, 0xc1, 0xcd, 0xfe, 0xd2, 0xe3, 0xec, 0xe8, 0x1d, 0x45, 0x96, 0xb0, 0x9a, 0xb7, 0x87, 0x3f, 0x68, 0x2d, 0xf7, 0x01, 0x1f, 0xbe, 0xd1, 0xf4, 0x7f, 0xb4, 0xa4, 0x0d, 0x77, 0xbb, 0xfa, 0x8f, 0x80, 0x3a, 0x7f, 0x43, 0xaa, 0xe2, 0xdf, 0xd2, 0x65, 0x7e, 0x95, 0xe4, 0x0f, 0x1f, 0xa1, 0xfe, 0x6b, 0x16, 0x9f, 0x52, 0xfa, 0xc1, 0xd3, 0xba, 0x6d, 0x26, 0xdc, 0xac, 0x86, 0xd4, 0xd9, 0x0d, 0x31, 0x2e, 0x74, 0x9e, 0xdb, 0x59, 0x2e, 0x55, 0xe8, 0xc9, 0xb2, 0x96, 0xd5, 0x4b, 0x9f, 0xb8, 0x6d, 0xda, 0x1c, 0x04, 0x09, 0x03, 0xfe, 0x8a, 0xc6, 0xfa, 0xd3, 0xf5, 0x6a, 0xbe, 0xbb, 0x5b, 0x2e, 0xc6, 0xb5, 0x94, 0xe6, 0xd5, 0x20, 0x97, 0x7d, 0x1b, 0x1b, 0xf9, 0xad, 0x7c, 0x7d, 0x17, 0xb7, 0xf3, 0x1e, 0x92, 0x1b, 0x7f, 0xf8, 0xe0, 0x7d, 0x59, 0xdd, 0xfd, 0x32, 0xd8, 0x8f, 0xa5, 0xe8, 0x3a, 0x12, 0x5c, 0x3f, 0xfc, 0xc4, 0xfa, 0xc3, 0xb3, 0x77, 0xa7, 0x56, 0xed, 0xdb, 0x76, 0x7a, 0x8d, 0xdd, 0x1f, 0xbf, 0xfd, 0x44, 0x92, 0x56, 0x8f, 0xff, 0xd4, 0xf2, 0xe8, 0x86, 0x17, 0x1e, 0xfa, 0x04, 0x56, 0x4b, 0x43, 0x6c, 0x6f, 0x2d, 0xe5, 0x46, 0x01, 0x64, 0x2b, 0x14, 0x32, 0x5b, 0xb4, 0xa0, 0x52, 0x1d, 0xde, 0x9b, 0x94, 0xdb, 0xab, 0x6b, 0x81, 0xf7, 0x05, 0xb0, 0xd7, 0x07, 0xb2, 0x27, 0x55, 0xc6, 0x57, 0x65, 0xd8, 0x76, 0x6e, 0x64, 0xed, 0xee, 0x16, 0xce, 0x27, 0x57, 0x63, 0xda, 0x0c, 0xc2, 0x8e, 0x51, 0x67, 0x84, 0xfa, 0x1d, 0xdd, 0x62, 0xc7, 0x07, 0xe9, 0xf7, 0xa3, 0xd6, 0x6c, 0x02, 0x41, 0x55, 0x31, 0xf3, 0x2b, 0xb3, 0xba, 0x2b, 0x2e, 0x68, 0x24, 0x1d, 0x47, 0x64, 0xca, 0xa6, 0x50, 0x41, 0x65, 0x90, 0x6c, 0xb1, 0xa5, 0xae, 0x33, 0x23, 0x51, 0xe4, 0xab, 0x7d, 0x5d, 0xcb, 0xb6, 0xcc, 0x37, 0xd0, 0x40, 0x73, 0x71, 0xde, 0x58, 0x09, 0xe7, 0x6f, 0x2c, 0x44, 0xc9, 0xc9, 0xae, 0xba, 0x9d, 0x63, 0x88, 0x01, 0xa0, 0x95, 0x9d, 0xf5, 0x3f, 0x2a, 0xe6, 0x67, 0xdb, 0x50, 0x83, 0x55, 0xad, 0x36, 0x3e, 0x78, 0x10, 0x74, 0x77, 0xfd, 0x2d, 0xaa, 0x4c, 0x7d, 0x58, 0x73, 0x91, 0xa0, 0x0f, 0x51, 0x45, 0xb7, 0x33, 0xdd, 0x58, 0x69, 0x1d, 0xd8, 0x0c, 0x9f, 0x96, 0x88, 0x19, 0x99, 0x19, 0xac, 0xcf, 0xa3, 0xd2, 0xad, 0xb5, 0xdb, 0x76, 0x8f, 0xad, 0xc4, 0xea, 0xcf, 0xdf, 0x7e, 0xdf, 0xdd, 0xfc, 0xd5, 0xa3, 0x5e, 0x43, 0x2b, 0x6b, 0xb2, 0xad, 0x3b, 0x6a, 0xa4, 0x13, 0xa7, 0x04, 0xac, 0x7a, 0x6f, 0xb3, 0x23, 0x26, 0xcc, 0xfb, 0xb4, 0x75, 0x8e, 0x01, 0x83, 0xf7, 0x58, 0x3e, 0x8b, 0x53, 0xa7, 0x2a, 0x1a, 0x31, 0x42, 0x36, 0x5d, 0x4c, 0x9a, 0xf2, 0xdc, 0xc6, 0xfe, 0x98, 0xb4, 0x34, 0xcb, 0x48, 0x0a, 0x8f, 0xdb, 0xb2, 0xeb, 0x76, 0xd6, 0x07, 0x5c, 0x59, 0xc9, 0x64, 0x8f, 0x93, 0xa7, 0x73, 0x16, 0x83, 0xaf, 0x0e, 0xa4, 0x33, 0xef, 0x50, 0xc5, 0x0c, 0xda, 0x59, 0x10, 0x06, 0x8a, 0x2e, 0x29, 0x0e, 0xac, 0xc2, 0x31, 0x3d, 0x36, 0x69, 0x7e, 0xd6, 0xcc, 0xf5, 0x3d, 0x6f, 0xb3, 0xeb, 0x1b, 0x76, 0xef, 0x3b, 0xa3, 0xfa, 0xc9, 0x2b, 0x5f, 0x66, 0x6f, 0xa9, 0x1e, 0x73, 0xf2, 0x49, 0x2e, 0x39, 0xf7, 0x4f, 0xb7, 0x8d, 0xff, 0xd5, 0xf3, 0x26, 0xfe, 0x0a, 0xc5, 0x1b, 0xa7, 0xcb, 0xb2, 0xcf, 0x49, 0x03, 0xb2, 0x46, 0xee, 0xd9, 0xd9, 0xb3, 0xf4, 0x9f, 0x25, 0x4a, 0xdf, 0x4b, 0x77, 0xe8, 0x27, 0xd4, 0xef, 0x1c, 0x2a, 0x29, 0x26, 0xc5, 0x7c, 0x9d, 0x6c, 0x7f, 0xb7, 0x6e, 0x1b, 0x26, 0x7f, 0x05, 0xa3, 0xfe, 0x53, 0x8d, 0x62, 0x57, 0x30, 0x92, 0x12, 0xfa, 0x2f, 0x86, 0xdf, 0xa4, 0xec, 0x67, 0xfe, 0xd0, 0xf4, 0xff, 0x00, 0x4d, 0xfc, 0xdf, 0x78, 0xe1, 0x68, 0x7d, 0x54, 0x99, 0xbf, 0x6f, 0xf3, 0xbe, 0xdf, 0x8e, 0xdd, 0x7f, 0xef, 0xeb, 0x97, 0x49, 0x3e, 0x3b, 0x7f, 0x06, 0x2c, 0x9f, 0x37, 0x5f, 0xf0, 0x9f, 0x4c, 0xeb, 0x7b, 0xbf, 0x67, 0x55, 0xe8, 0xff, 0x00, 0x31, 0xbc, 0x7a, 0x9e, 0x31, 0xdb, 0xfe, 0x92, 0xae, 0x37, 0x7a, 0x4d, 0xdb, 0xe2, 0x17, 0x9d, 0xa4, 0xa3, 0xc9, 0xba, 0xfc, 0x7b, 0x7d, 0x5f, 0x52, 0xa7, 0x7e, 0xd1, 0x28, 0xf8, 0xf3, 0xb0, 0xc7, 0x32, 0xbc, 0x99, 0x24, 0xc5, 0xe3, 0xab, 0xeb, 0x1f, 0xa4, 0xf5, 0xfc, 0xe1, 0x25, 0xe4, 0xe9, 0x24, 0x97, 0xff, 0xd9, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x00, 0x00, 0x13, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x20, 0x00, 0x37, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0xff, 0xe1, 0x15, 0x67, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x00, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x27, 0xef, 0xbb, 0xbf, 0x27, 0x20, 0x69, 0x64, 0x3d, 0x27, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x27, 0x3f, 0x3e, 0x0a, 0x3c, 0x3f, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x78, 0x61, 0x70, 0x2d, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x20, 0x65, 0x73, 0x63, 0x3d, 0x22, 0x43, 0x52, 0x22, 0x3f, 0x3e, 0x0a, 0x3c, 0x78, 0x3a, 0x78, 0x61, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x27, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x27, 0x20, 0x78, 0x3a, 0x78, 0x61, 0x70, 0x74, 0x6b, 0x3d, 0x27, 0x58, 0x4d, 0x50, 0x20, 0x74, 0x6f, 0x6f, 0x6c, 0x6b, 0x69, 0x74, 0x20, 0x32, 0x2e, 0x38, 0x2e, 0x32, 0x2d, 0x33, 0x33, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x31, 0x2e, 0x35, 0x27, 0x3e, 0x0a, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x27, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x69, 0x58, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x58, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x27, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x70, 0x64, 0x66, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x64, 0x66, 0x2f, 0x31, 0x2e, 0x33, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x21, 0x2d, 0x2d, 0x20, 0x70, 0x64, 0x66, 0x3a, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x2d, 0x2d, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x21, 0x2d, 0x2d, 0x20, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x43, 0x61, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x2d, 0x2d, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x61, 0x70, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x21, 0x2d, 0x2d, 0x20, 0x78, 0x61, 0x70, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x65, 0x64, 0x20, 0x2d, 0x2d, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x61, 0x70, 0x4d, 0x4d, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x78, 0x61, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, 0x64, 0x6f, 0x63, 0x69, 0x64, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x36, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x3c, 0x2f, 0x78, 0x61, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x27, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x32, 0x32, 0x64, 0x30, 0x32, 0x62, 0x30, 0x61, 0x2d, 0x62, 0x32, 0x34, 0x39, 0x2d, 0x31, 0x31, 0x64, 0x62, 0x2d, 0x38, 0x61, 0x66, 0x38, 0x2d, 0x39, 0x31, 0x64, 0x35, 0x34, 0x30, 0x33, 0x66, 0x39, 0x32, 0x66, 0x39, 0x27, 0x0a, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x27, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, 0x27, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x20, 0x78, 0x6d, 0x6c, 0x3a, 0x6c, 0x61, 0x6e, 0x67, 0x3d, 0x27, 0x78, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x27, 0x3e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x41, 0x6c, 0x74, 0x3e, 0x0a, 0x20, 0x20, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x0a, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x61, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x27, 0x77, 0x27, 0x3f, 0x3e, 0xff, 0xee, 0x00, 0x0e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x00, 0x64, 0x40, 0x00, 0x00, 0x00, 0x01, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x06, 0x04, 0x03, 0x04, 0x06, 0x07, 0x05, 0x04, 0x04, 0x05, 0x07, 0x08, 0x06, 0x06, 0x07, 0x06, 0x06, 0x08, 0x0a, 0x08, 0x09, 0x09, 0x09, 0x09, 0x08, 0x0a, 0x0a, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0a, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x01, 0x04, 0x05, 0x05, 0x08, 0x07, 0x08, 0x0f, 0x0a, 0x0a, 0x0f, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14, 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0x64, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xdd, 0x00, 0x04, 0x00, 0x0d, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x00, 0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05, 0x03, 0x02, 0x06, 0x01, 0x00, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x02, 0x02, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x02, 0x06, 0x07, 0x03, 0x04, 0x02, 0x06, 0x02, 0x73, 0x01, 0x02, 0x03, 0x11, 0x04, 0x00, 0x05, 0x21, 0x12, 0x31, 0x41, 0x51, 0x06, 0x13, 0x61, 0x22, 0x71, 0x81, 0x14, 0x32, 0x91, 0xa1, 0x07, 0x15, 0xb1, 0x42, 0x23, 0xc1, 0x52, 0xd1, 0xe1, 0x33, 0x16, 0x62, 0xf0, 0x24, 0x72, 0x82, 0xf1, 0x25, 0x43, 0x34, 0x53, 0x92, 0xa2, 0xb2, 0x63, 0x73, 0xc2, 0x35, 0x44, 0x27, 0x93, 0xa3, 0xb3, 0x36, 0x17, 0x54, 0x64, 0x74, 0xc3, 0xd2, 0xe2, 0x08, 0x26, 0x83, 0x09, 0x0a, 0x18, 0x19, 0x84, 0x94, 0x45, 0x46, 0xa4, 0xb4, 0x56, 0xd3, 0x55, 0x28, 0x1a, 0xf2, 0xe3, 0xf3, 0xc4, 0xd4, 0xe4, 0xf4, 0x65, 0x75, 0x85, 0x95, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, 0xb8, 0xc8, 0xd8, 0xe8, 0xf8, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, 0xb9, 0xc9, 0xd9, 0xe9, 0xf9, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa, 0x11, 0x00, 0x02, 0x02, 0x01, 0x02, 0x03, 0x05, 0x05, 0x04, 0x05, 0x06, 0x04, 0x08, 0x03, 0x03, 0x6d, 0x01, 0x00, 0x02, 0x11, 0x03, 0x04, 0x21, 0x12, 0x31, 0x41, 0x05, 0x51, 0x13, 0x61, 0x22, 0x06, 0x71, 0x81, 0x91, 0x32, 0xa1, 0xb1, 0xf0, 0x14, 0xc1, 0xd1, 0xe1, 0x23, 0x42, 0x15, 0x52, 0x62, 0x72, 0xf1, 0x33, 0x24, 0x34, 0x43, 0x82, 0x16, 0x92, 0x53, 0x25, 0xa2, 0x63, 0xb2, 0xc2, 0x07, 0x73, 0xd2, 0x35, 0xe2, 0x44, 0x83, 0x17, 0x54, 0x93, 0x08, 0x09, 0x0a, 0x18, 0x19, 0x26, 0x36, 0x45, 0x1a, 0x27, 0x64, 0x74, 0x55, 0x37, 0xf2, 0xa3, 0xb3, 0xc3, 0x28, 0x29, 0xd3, 0xe3, 0xf3, 0x84, 0x94, 0xa4, 0xb4, 0xc4, 0xd4, 0xe4, 0xf4, 0x65, 0x75, 0x85, 0x95, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, 0xb8, 0xc8, 0xd8, 0xe8, 0xf8, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, 0xb9, 0xc9, 0xd9, 0xe9, 0xf9, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf0, 0x67, 0xa6, 0x5c, 0x0f, 0x01, 0xd4, 0x7e, 0x18, 0x12, 0x98, 0xe9, 0xd6, 0x2d, 0x34, 0x6d, 0x70, 0xdf, 0xdc, 0xa1, 0xe3, 0xec, 0x5b, 0xfb, 0x32, 0x24, 0xb2, 0x01, 0x1f, 0x15, 0xa4, 0x52, 0x4a, 0x82, 0x31, 0xf1, 0xfe, 0xd1, 0x3d, 0x14, 0x64, 0x49, 0x64, 0x22, 0x98, 0xcf, 0xa5, 0x46, 0x6c, 0x16, 0x55, 0x71, 0x56, 0x62, 0x28, 0x07, 0xc5, 0x45, 0x15, 0xa0, 0xc8, 0x89, 0x33, 0xe1, 0x63, 0xd2, 0xd8, 0x34, 0x44, 0x17, 0xa0, 0x2c, 0x4d, 0x16, 0xbb, 0xed, 0xdc, 0xf8, 0x64, 0xc1, 0x6b, 0x31, 0x42, 0x18, 0x8e, 0xc7, 0xb5, 0x2a, 0x7d, 0xb2, 0x56, 0xc5, 0x61, 0x8c, 0xf2, 0xa0, 0x1b, 0x1e, 0x83, 0x0d, 0xa1, 0x63, 0x50, 0x1f, 0x97, 0x7c, 0x2a, 0xa9, 0x1a, 0x9a, 0x86, 0x4f, 0xb4, 0xb4, 0x38, 0x0a, 0xa6, 0x0b, 0xb8, 0x0c, 0x05, 0x14, 0xf8, 0x76, 0x3e, 0x19, 0x14, 0xb6, 0x78, 0xf8, 0x8c, 0x2a, 0xd5, 0x01, 0xdc, 0x6f, 0x8a, 0x1a, 0xe3, 0x8d, 0xab, 0xff, 0xd0, 0xf0, 0xec, 0xe9, 0x15, 0xb5, 0xb9, 0x5a, 0x7c, 0x4c, 0xa2, 0x9e, 0x24, 0xf5, 0xca, 0xc6, 0xe5, 0x99, 0xd9, 0x34, 0x99, 0x04, 0x3a, 0x7d, 0xb5, 0xba, 0xd5, 0x51, 0x63, 0x0e, 0xc7, 0xc5, 0x9b, 0x73, 0xf8, 0xe4, 0x6f, 0x76, 0xca, 0xd9, 0xda, 0x54, 0x6d, 0x72, 0x2e, 0x1a, 0x57, 0x11, 0x44, 0x40, 0x0d, 0x27, 0x7a, 0x0f, 0xd9, 0x5f, 0x12, 0x69, 0x4c, 0x84, 0xcd, 0x36, 0xe3, 0x85, 0xb2, 0xcd, 0x2f, 0x4a, 0x8b, 0x58, 0x36, 0xf6, 0x76, 0xa8, 0x64, 0x64, 0x3c, 0xa4, 0x93, 0xaa, 0x25, 0x3c, 0x49, 0xda, 0xa4, 0xe5, 0x26, 0x54, 0xe4, 0x8c, 0x7c, 0x5c, 0x93, 0x4d, 0x67, 0xc9, 0x3a, 0x6e, 0x9f, 0x13, 0xb4, 0xce, 0xf7, 0x3a, 0x9b, 0xad, 0x52, 0xd6, 0x2a, 0xd1, 0x49, 0xee, 0xc7, 0xf8, 0x64, 0x46, 0x42, 0x4e, 0xcd, 0x92, 0xc2, 0x00, 0xdd, 0x8a, 0x47, 0xe5, 0x69, 0x6e, 0xd4, 0xa4, 0x08, 0x16, 0x83, 0x9c, 0x8c, 0xdd, 0x95, 0x6b, 0xb9, 0xf6, 0xef, 0x97, 0x78, 0x94, 0xe3, 0x78, 0x04, 0xa4, 0xf3, 0xe8, 0xee, 0x64, 0xe1, 0x12, 0x10, 0x05, 0x6a, 0xc7, 0xc0, 0x6f, 0x53, 0xf3, 0xc9, 0x89, 0xb4, 0x9c, 0x4e, 0xb4, 0xf2, 0xd3, 0xde, 0x7a, 0xd2, 0x19, 0x16, 0x38, 0x61, 0x5d, 0xd9, 0x88, 0x05, 0x9c, 0xf4, 0x0a, 0x0f, 0x5f, 0x73, 0x84, 0xe4, 0xa4, 0xc7, 0x0d, 0xa5, 0xf1, 0x59, 0xba, 0x5c, 0x08, 0x98, 0x6f, 0xc8, 0x20, 0xfa, 0x4e, 0x4e, 0xf6, 0x69, 0xe1, 0xa2, 0x89, 0xfd, 0x1f, 0x77, 0x2c, 0xe6, 0xce, 0xd6, 0x17, 0x9a, 0x69, 0xdb, 0xd3, 0x86, 0x18, 0xc1, 0x67, 0x77, 0x26, 0x80, 0x28, 0x1b, 0x93, 0x88, 0x41, 0x0f, 0x40, 0xb0, 0xfc, 0x87, 0xf3, 0x43, 0x98, 0xd7, 0x58, 0x96, 0xdb, 0x4d, 0x91, 0x88, 0xe5, 0x6c, 0x58, 0xdc, 0x5c, 0x2a, 0xf7, 0x2c, 0xb1, 0xfc, 0x20, 0x8f, 0x02, 0xd9, 0x65, 0x06, 0xbe, 0x26, 0x6f, 0xa2, 0x7f, 0xce, 0x3d, 0x69, 0x26, 0xdd, 0x13, 0x52, 0xbf, 0xbd, 0x92, 0x62, 0x59, 0x4c, 0x90, 0xac, 0x50, 0x45, 0x5e, 0xbb, 0x09, 0x03, 0x12, 0x29, 0x84, 0x00, 0xc4, 0xc9, 0x11, 0xff, 0x00, 0x42, 0xe7, 0xa7, 0x7a, 0xd4, 0xfd, 0x21, 0x79, 0xe9, 0x78, 0x71, 0x8b, 0x95, 0x39, 0x75, 0xaf, 0x4e, 0x98, 0x78, 0x42, 0x38, 0xdf, 0xff, 0xd1, 0xf0, 0xe6, 0xa0, 0x58, 0xc8, 0x84, 0x9a, 0xaa, 0x30, 0x55, 0xf9, 0x0a, 0x6f, 0x90, 0x0c, 0xca, 0x72, 0x48, 0xb8, 0x1e, 0x89, 0xa7, 0x23, 0x17, 0x24, 0xff, 0x00, 0x61, 0xb6, 0x54, 0x76, 0x6e, 0x1b, 0xa7, 0xbe, 0x50, 0xf2, 0xc1, 0xd7, 0x4c, 0x52, 0x5e, 0x33, 0x5b, 0xe9, 0x10, 0xf4, 0x54, 0x3c, 0x5e, 0x77, 0xee, 0x49, 0xec, 0x2b, 0xb6, 0x63, 0xe4, 0xc9, 0xc3, 0xef, 0x73, 0xf0, 0xe1, 0x32, 0x1b, 0xf2, 0x7a, 0x05, 0xce, 0xad, 0x65, 0xa1, 0x98, 0xb4, 0x0f, 0x2a, 0x5b, 0x23, 0xeb, 0x12, 0x00, 0x88, 0xb0, 0xa8, 0x66, 0x46, 0x3d, 0xea, 0x7b, 0xfb, 0x9e, 0x99, 0x89, 0xbc, 0x8d, 0x97, 0x3a, 0x34, 0x05, 0x32, 0x5d, 0x1f, 0xc9, 0x1a, 0x8c, 0x36, 0x8c, 0x6f, 0x66, 0xfa, 0xc6, 0xb7, 0x7d, 0xf0, 0x94, 0x04, 0xf0, 0x88, 0xc9, 0xd5, 0x9d, 0x8d, 0x4b, 0x11, 0xd4, 0x9f, 0xbb, 0x25, 0xc5, 0xdc, 0xa2, 0x03, 0x99, 0x4b, 0xbc, 0xf3, 0x0d, 0x97, 0x96, 0x74, 0xe5, 0xf2, 0xb6, 0x80, 0x95, 0xbd, 0x99, 0x15, 0xf5, 0x4b, 0xd2, 0x37, 0x58, 0x46, 0xd4, 0x27, 0xc5, 0xce, 0xc1, 0x7c, 0x30, 0x8e, 0x68, 0x94, 0x7b, 0x9e, 0x6d, 0xe6, 0x7b, 0x9b, 0x5d, 0x3a, 0xd8, 0xdb, 0x32, 0xfa, 0x77, 0x65, 0x15, 0xe4, 0x57, 0xa7, 0x21, 0x55, 0x04, 0x57, 0xef, 0xd8, 0x66, 0x56, 0x38, 0x19, 0x1b, 0xe8, 0xe0, 0x67, 0x98, 0xc7, 0x1a, 0x1c, 0xde, 0x71, 0x71, 0x79, 0x2c, 0xf2, 0xfa, 0x8c, 0x48, 0xec, 0xb5, 0x24, 0x9a, 0x0c, 0xce, 0x75, 0x29, 0xae, 0x8c, 0x67, 0xd4, 0xb5, 0x0b, 0x4b, 0x04, 0x05, 0xef, 0x2e, 0x66, 0x8e, 0x18, 0x08, 0x15, 0xdd, 0x8f, 0x11, 0xb0, 0xeb, 0x4c, 0x04, 0x5b, 0x21, 0x2a, 0x7d, 0x41, 0xe4, 0x4f, 0xcb, 0xcb, 0x5d, 0x12, 0x45, 0xb8, 0xb7, 0x53, 0x71, 0xaa, 0x9f, 0x86, 0x5b, 0xd6, 0x50, 0x4a, 0xed, 0xba, 0x46, 0x77, 0x00, 0x13, 0xd4, 0x8c, 0x85, 0xd3, 0x12, 0x6d, 0xeb, 0x1a, 0x67, 0x95, 0xd9, 0x39, 0x39, 0x50, 0xac, 0xff, 0x00, 0x6f, 0xc4, 0xff, 0x00, 0x1c, 0x81, 0x92, 0xb2, 0x6b, 0x6d, 0x02, 0xdd, 0xbd, 0x36, 0x92, 0x36, 0x2d, 0x1f, 0xc0, 0x2a, 0x0b, 0x28, 0x1b, 0x91, 0x41, 0xf4, 0x9c, 0xb6, 0x25, 0x81, 0x46, 0xfe, 0x81, 0xb5, 0xad, 0x3d, 0xba, 0x57, 0xb7, 0xf9, 0xf6, 0xc9, 0xb0, 0x7f, 0xff, 0xd2, 0xf0, 0xe2, 0x86, 0x95, 0xc4, 0x67, 0x7e, 0x3f, 0x11, 0xf7, 0xa8, 0x19, 0x06, 0x69, 0x8d, 0xca, 0xca, 0x24, 0x8f, 0xd3, 0x52, 0x24, 0x89, 0x47, 0x25, 0x1f, 0xcb, 0x20, 0xf8, 0xb2, 0xb2, 0x76, 0x6e, 0x88, 0x36, 0xf6, 0x6f, 0x2a, 0xc1, 0x6e, 0xfa, 0x45, 0xad, 0xbc, 0x3f, 0x0b, 0x46, 0x81, 0x4d, 0x46, 0xea, 0x7a, 0x9a, 0x83, 0x9a, 0xa9, 0xdd, 0xbb, 0xec, 0x7b, 0x06, 0x5b, 0xe5, 0xcf, 0x2e, 0x69, 0xfa, 0x5c, 0xcd, 0x7b, 0x14, 0x5e, 0xa5, 0xee, 0xf5, 0xb8, 0x7d, 0xdd, 0x99, 0xba, 0xef, 0x91, 0x16, 0x5b, 0x36, 0xb6, 0x65, 0x0d, 0xac, 0xb2, 0x5b, 0xed, 0x34, 0x81, 0x7a, 0xbb, 0x46, 0x40, 0x6a, 0x9e, 0xb4, 0x39, 0x31, 0x13, 0x49, 0xda, 0xd2, 0x9b, 0xed, 0x1e, 0xc4, 0x24, 0xb3, 0x35, 0xb2, 0x88, 0x60, 0x06, 0xe6, 0x56, 0x98, 0x96, 0x79, 0x1e, 0x31, 0x51, 0xc9, 0x8f, 0xcb, 0x00, 0xe6, 0xb3, 0xe4, 0xf9, 0x2b, 0xcc, 0x7a, 0x94, 0xda, 0x96, 0xa9, 0x71, 0x77, 0x70, 0x79, 0xcd, 0x33, 0x97, 0x76, 0x3f, 0xcc, 0xc6, 0xa6, 0x9f, 0x2e, 0x99, 0xb9, 0xc6, 0x2a, 0x21, 0xe6, 0x73, 0xca, 0xe6, 0x4a, 0x51, 0x1a, 0x99, 0x1c, 0x28, 0x04, 0x93, 0xd0, 0x0e, 0xa4, 0xe4, 0xda, 0x5f, 0x50, 0xfe, 0x4a, 0xfe, 0x48, 0xb5, 0xb2, 0xc1, 0xe6, 0x1f, 0x31, 0x7e, 0xef, 0x52, 0x91, 0x43, 0xc3, 0x6e, 0x77, 0xf4, 0x22, 0x6d, 0xbf, 0xe4, 0x63, 0x0e, 0xbf, 0xca, 0x36, 0xeb, 0x5c, 0x84, 0xa5, 0x48, 0x7d, 0x3b, 0x61, 0xa1, 0xdb, 0x5b, 0x2c, 0x71, 0xda, 0x45, 0xc4, 0x28, 0x00, 0x81, 0xdb, 0x31, 0xc9, 0xb4, 0xb2, 0x3b, 0x5d, 0x27, 0xa5, 0x05, 0x1b, 0xc7, 0xdb, 0x10, 0xa9, 0xbd, 0xa6, 0x93, 0x0c, 0x75, 0xe4, 0x39, 0x35, 0x41, 0x3d, 0xc5, 0x06, 0xdb, 0x8e, 0xfd, 0x46, 0x5b, 0x1d, 0x98, 0x95, 0x4f, 0x46, 0xdb, 0xd5, 0xfb, 0x29, 0x5e, 0x9d, 0x0d, 0x32, 0xeb, 0x61, 0x4f, 0xff, 0xd3, 0xf1, 0x46, 0x9a, 0x16, 0x1b, 0x91, 0x71, 0x28, 0xac, 0x4a, 0x14, 0x30, 0x3e, 0x19, 0x54, 0xb9, 0x36, 0xc7, 0x9b, 0x2d, 0xd1, 0x6c, 0x45, 0xe3, 0xdc, 0xde, 0xc8, 0x95, 0x5b, 0x87, 0xf8, 0x41, 0x1d, 0x10, 0x54, 0x01, 0x98, 0x79, 0x25, 0xd1, 0xda, 0xe9, 0xe1, 0xb5, 0x9e, 0xac, 0xeb, 0x42, 0xba, 0x8e, 0xdf, 0x8c, 0x31, 0x21, 0x70, 0xb4, 0x5d, 0xbe, 0xc5, 0x7c, 0x2b, 0xed, 0xe1, 0x94, 0x18, 0xb9, 0x51, 0x3d, 0x03, 0x2c, 0x13, 0x6b, 0xf1, 0x42, 0x6e, 0xe2, 0xb7, 0x12, 0xa0, 0xdd, 0x50, 0x9f, 0x4f, 0x6f, 0xa7, 0x6f, 0xc7, 0x03, 0x61, 0xa0, 0x83, 0xb5, 0xf3, 0x97, 0x98, 0x20, 0x9c, 0x44, 0xea, 0xd0, 0xad, 0x48, 0x64, 0x90, 0x21, 0xd8, 0x9f, 0xa7, 0xa6, 0x44, 0xca, 0x99, 0xc6, 0x36, 0xcb, 0x74, 0x5d, 0x7e, 0x5b, 0xfe, 0x31, 0x6a, 0x31, 0xf3, 0x8c, 0xd0, 0xad, 0x40, 0xa3, 0x1f, 0x7c, 0x44, 0xd6, 0x51, 0xd9, 0xe0, 0x5f, 0x9a, 0x7e, 0x41, 0x9f, 0x40, 0xf3, 0x14, 0xba, 0x85, 0xba, 0x34, 0xba, 0x2d, 0xfb, 0x34, 0xd0, 0xcf, 0x4f, 0xb0, 0xce, 0x6a, 0x51, 0xe9, 0xb0, 0x20, 0xf4, 0xf1, 0x19, 0xb2, 0xc3, 0x90, 0x11, 0x4e, 0x97, 0x55, 0x80, 0x83, 0xc4, 0x17, 0x7e, 0x4c, 0x79, 0x19, 0xfc, 0xd1, 0xe7, 0x78, 0x4b, 0x91, 0x1d, 0xae, 0x92, 0xa6, 0xf6, 0x46, 0x75, 0xe4, 0xad, 0x22, 0x1f, 0xdd, 0xa1, 0x07, 0xb3, 0x1e, 0xfe, 0xd9, 0x92, 0xeb, 0x4b, 0xed, 0xfd, 0x0a, 0xc2, 0x63, 0x27, 0xa4, 0x88, 0x17, 0x60, 0x49, 0x35, 0xdc, 0x8e, 0xa5, 0x7d, 0xab, 0xd3, 0x28, 0x90, 0x50, 0xcd, 0xed, 0x2d, 0xda, 0x15, 0x55, 0x51, 0xf1, 0x1a, 0x0a, 0xf7, 0x39, 0x5d, 0xaa, 0x77, 0x6f, 0x01, 0x8e, 0xa7, 0x7d, 0xfa, 0xff, 0x00, 0x66, 0x10, 0xa8, 0xb8, 0x63, 0x76, 0x90, 0xa8, 0x20, 0x06, 0x56, 0xdb, 0x61, 0xda, 0xbd, 0x4f, 0xcb, 0x24, 0x15, 0x0f, 0xf5, 0x66, 0xe5, 0x5f, 0x4c, 0x53, 0xc3, 0xb7, 0xce, 0x99, 0x6b, 0x17, 0xff, 0xd4, 0xf0, 0xec, 0x57, 0x6f, 0x32, 0xa5, 0xa4, 0x43, 0x76, 0x75, 0xa9, 0xf1, 0x03, 0xfa, 0x64, 0x08, 0x6c, 0x8e, 0xfb, 0x3d, 0x7f, 0xcb, 0x16, 0x2b, 0x3d, 0xbc, 0x16, 0xa3, 0x66, 0x6d, 0x98, 0xfb, 0x1e, 0xb9, 0xac, 0xc8, 0x77, 0xb7, 0x7d, 0x01, 0xb3, 0x37, 0xb8, 0xd3, 0x46, 0x95, 0x68, 0x86, 0xd2, 0x2e, 0x4e, 0xab, 0xf0, 0x23, 0x11, 0x4e, 0x5f, 0xcd, 0x98, 0xe7, 0x25, 0x96, 0x71, 0x83, 0x0f, 0xd6, 0x3c, 0xb9, 0xe7, 0x0d, 0x7c, 0x41, 0x22, 0x5e, 0xb3, 0x20, 0x0c, 0x65, 0x80, 0xc8, 0x63, 0x8e, 0xbb, 0x95, 0xa5, 0x07, 0xeb, 0xcc, 0xac, 0x73, 0x83, 0x4e, 0x5c, 0x59, 0x09, 0xd8, 0xec, 0xc8, 0x57, 0x41, 0xd3, 0x4e, 0x95, 0xa5, 0x5b, 0x4b, 0x6a, 0xcb, 0xab, 0x43, 0x10, 0x4b, 0xeb, 0x85, 0xa2, 0x2c, 0x8e, 0x3f, 0x68, 0x54, 0xf5, 0x00, 0xd3, 0x97, 0x7a, 0x65, 0x79, 0xa6, 0x24, 0x76, 0x6f, 0xd3, 0x62, 0x96, 0x30, 0x78, 0xcb, 0x21, 0xf2, 0xf4, 0x22, 0xce, 0x54, 0x8e, 0x46, 0x26, 0x10, 0x7e, 0x0a, 0xf5, 0xd8, 0xf5, 0x1f, 0x31, 0x98, 0x83, 0x73, 0xb3, 0x91, 0xcd, 0x67, 0xe6, 0x7d, 0xe8, 0x16, 0x69, 0x6f, 0x10, 0x1f, 0x54, 0x9a, 0x37, 0xf5, 0x41, 0x5e, 0x7f, 0x0a, 0x29, 0x62, 0x02, 0xf8, 0x9c, 0xc8, 0x8c, 0x77, 0x6a, 0x99, 0xa0, 0x89, 0xff, 0x00, 0x9c, 0x74, 0xd2, 0xed, 0xed, 0xfc, 0xbb, 0x7b, 0xaa, 0x9a, 0x7d, 0x62, 0xfe, 0x46, 0x2d, 0xfe, 0x4c, 0x51, 0x31, 0x11, 0xa9, 0xf6, 0xef, 0x9b, 0x30, 0x5e, 0x7b, 0x38, 0xdd, 0xf4, 0x7f, 0x95, 0x94, 0xbc, 0x12, 0x43, 0x30, 0x6a, 0xb2, 0xf3, 0x86, 0x40, 0x3e, 0xcb, 0xd7, 0x6a, 0xd7, 0xb1, 0xe9, 0x8f, 0x37, 0x19, 0x97, 0x41, 0x2c, 0x71, 0x20, 0xf5, 0x36, 0x9c, 0x55, 0x78, 0x1d, 0x8a, 0x91, 0xd7, 0x11, 0x14, 0x5a, 0x3e, 0x19, 0x03, 0x10, 0x6b, 0xca, 0xbd, 0x86, 0xf8, 0x9d, 0x95, 0x18, 0x36, 0x65, 0x2e, 0xbc, 0x54, 0x1f, 0xa2, 0x99, 0x00, 0x59, 0x2a, 0x6f, 0x5e, 0x55, 0x15, 0xe9, 0x5f, 0xc3, 0x2f, 0xb6, 0x14, 0xff, 0x00, 0xff, 0xd5, 0xf1, 0x95, 0xfe, 0x80, 0x74, 0x0d, 0x7c, 0xd9, 0x89, 0x3d, 0x78, 0x57, 0x8b, 0xc5, 0x28, 0xe8, 0x55, 0xf7, 0x1f, 0x48, 0xca, 0x38, 0xb8, 0x83, 0x9f, 0x93, 0x07, 0x85, 0x3a, 0x7a, 0x6f, 0x95, 0x66, 0x2b, 0x2c, 0x4c, 0x0d, 0x14, 0x00, 0x3e, 0x9c, 0xc3, 0x98, 0x76, 0xb8, 0x45, 0xbd, 0x02, 0xde, 0x48, 0xee, 0xdc, 0xa0, 0x15, 0xe2, 0x2b, 0xc8, 0x8a, 0x8a, 0xfd, 0x3b, 0x66, 0x3f, 0x00, 0x73, 0x84, 0x2d, 0x36, 0xb5, 0xb5, 0x9e, 0x35, 0x1c, 0x29, 0xc4, 0xfe, 0xc8, 0x04, 0x7f, 0xc4, 0x69, 0x91, 0xe1, 0x67, 0x2c, 0x4a, 0xd2, 0xe9, 0x4e, 0xe3, 0xd4, 0xf4, 0x81, 0x5a, 0x12, 0xc5, 0x41, 0x3f, 0x79, 0x38, 0x9b, 0x60, 0x20, 0x07, 0x34, 0xb0, 0xc9, 0x03, 0x5c, 0x23, 0x03, 0x53, 0x13, 0x56, 0x88, 0xdf, 0x09, 0xda, 0x9b, 0xd3, 0xb6, 0x52, 0x0e, 0xec, 0xe4, 0x29, 0x24, 0xfc, 0xd0, 0xe7, 0x75, 0xe5, 0x57, 0x6b, 0x61, 0xfb, 0xf0, 0xca, 0xaa, 0x57, 0xa8, 0xe6, 0x78, 0x1a, 0x7d, 0xf9, 0x95, 0x8a, 0x5e, 0xa0, 0xe3, 0x67, 0x8f, 0xa0, 0xbd, 0x5b, 0xf2, 0xdf, 0x4a, 0x82, 0xcb, 0x4a, 0xb3, 0xb0, 0xb4, 0x41, 0x0a, 0x70, 0x48, 0xd9, 0x57, 0x60, 0x51, 0x3a, 0x8f, 0xbc, 0xe6, 0x7b, 0xcb, 0xe4, 0x3b, 0xa7, 0x3f, 0x9b, 0x9f, 0x9a, 0xba, 0x77, 0xe5, 0x5f, 0x95, 0x9c, 0x59, 0x94, 0x9f, 0xcd, 0x37, 0x8c, 0xa9, 0xa6, 0xd9, 0x39, 0xaa, 0xd0, 0x7d, 0xa9, 0x1c, 0x03, 0x5e, 0x09, 0xff, 0x00, 0x0c, 0x76, 0xcb, 0x62, 0x2d, 0xa5, 0xf2, 0x85, 0xbf, 0xe7, 0x87, 0xe6, 0xa3, 0x5e, 0x4d, 0xa8, 0xc9, 0xe6, 0x8b, 0xd5, 0x69, 0x5c, 0xb0, 0x4a, 0xab, 0xc4, 0xb5, 0x35, 0x0a, 0xaa, 0xea, 0x40, 0x03, 0xa0, 0xf6, 0xcb, 0x40, 0x4d, 0x3e, 0xdb, 0xff, 0x00, 0x9c, 0x7f, 0xfc, 0xce, 0x4f, 0xcc, 0xbf, 0x26, 0x25, 0xe5, 0xd3, 0x2f, 0xe9, 0xdd, 0x3d, 0xfe, 0xab, 0xa9, 0xaa, 0xd2, 0xa6, 0x40, 0x2a, 0xb2, 0x71, 0x00, 0x01, 0xea, 0x0d, 0xe8, 0x3a, 0x64, 0x25, 0x16, 0x1c, 0x8b, 0xd9, 0x51, 0x39, 0x28, 0x12, 0x51, 0x41, 0xfd, 0xa3, 0xd2, 0xb9, 0x4f, 0x0d, 0x33, 0xb5, 0xf4, 0x87, 0x9d, 0x79, 0x0e, 0xb4, 0xaf, 0x6a, 0xf8, 0xf1, 0xf0, 0xc9, 0xda, 0xbf, 0xff, 0xd6, 0xf2, 0xc6, 0xb5, 0x68, 0x64, 0xd0, 0x6d, 0x35, 0x20, 0x39, 0xcd, 0x13, 0x0f, 0x5e, 0x61, 0xfc, 0x8f, 0x40, 0x8b, 0x5e, 0xe0, 0x66, 0x1c, 0x4f, 0xaa, 0x9d, 0xe6, 0xa6, 0x1e, 0x91, 0x2e, 0xa9, 0x87, 0x95, 0xee, 0x9c, 0xc5, 0x55, 0x34, 0x60, 0x40, 0xae, 0x57, 0x30, 0xd9, 0xa7, 0x95, 0xbd, 0x6f, 0xcb, 0x26, 0x39, 0x40, 0x0d, 0x4e, 0xc0, 0x9f, 0x9e, 0x50, 0x5d, 0xac, 0x79, 0x33, 0x8b, 0xbb, 0x9b, 0x3b, 0x6b, 0x35, 0x48, 0x54, 0x09, 0x29, 0x56, 0x7f, 0xe1, 0x86, 0x72, 0x00, 0x2c, 0x6e, 0xf7, 0x63, 0x3e, 0x63, 0xbd, 0xbd, 0x5d, 0x20, 0x2a, 0xb3, 0xa4, 0x33, 0x48, 0xab, 0x21, 0x43, 0xf1, 0x2c, 0x47, 0xed, 0x1d, 0xbc, 0x73, 0x18, 0x9b, 0x64, 0x28, 0x96, 0x3a, 0xc7, 0x49, 0xb0, 0xf4, 0xcc, 0xe9, 0x73, 0x6c, 0xb4, 0xf8, 0x67, 0x92, 0x32, 0x21, 0x70, 0x7b, 0x89, 0x05, 0x57, 0xef, 0x38, 0x28, 0x94, 0x4a, 0x7d, 0x13, 0x7d, 0x6a, 0xd3, 0x4c, 0xb8, 0xf2, 0xc3, 0xc8, 0x2e, 0x03, 0xf3, 0xe2, 0x7d, 0x33, 0xb7, 0xc5, 0xcc, 0x71, 0x03, 0xc6, 0xb9, 0x64, 0x06, 0xe2, 0x9a, 0xf2, 0x4f, 0xd2, 0x6d, 0xe9, 0xfe, 0x41, 0x45, 0x5b, 0x18, 0x66, 0xa5, 0x64, 0x09, 0xf4, 0xd5, 0xb7, 0xcd, 0x93, 0xc7, 0xcf, 0x9b, 0xe5, 0x6f, 0xf9, 0xc8, 0x0d, 0x56, 0xeb, 0x59, 0xfc, 0xce, 0xd5, 0x12, 0x61, 0xc4, 0x69, 0xe9, 0x0d, 0xa4, 0x4b, 0xfe, 0x48, 0x40, 0xd5, 0x3e, 0xe4, 0xb6, 0x64, 0x8e, 0x4c, 0x02, 0x61, 0x65, 0xa0, 0x14, 0xb4, 0xb6, 0xb0, 0xb1, 0xb6, 0xb2, 0x97, 0xcb, 0xf1, 0x5a, 0x2d, 0xc6, 0xa5, 0xac, 0xb4, 0x70, 0x5d, 0xc7, 0x3d, 0xc1, 0x51, 0x24, 0x91, 0xc9, 0x31, 0x75, 0x6b, 0x70, 0x9f, 0x14, 0x68, 0x01, 0x46, 0xe4, 0xb5, 0xa3, 0x17, 0xcb, 0x40, 0x61, 0x6f, 0x47, 0xff, 0x00, 0x9c, 0x3a, 0x8f, 0x5b, 0x4f, 0x3c, 0x6b, 0xb7, 0xfa, 0x30, 0x91, 0x3c, 0xa4, 0xb1, 0x95, 0xb9, 0x82, 0x42, 0x0a, 0xbc, 0x8e, 0xe4, 0xdb, 0xa9, 0xef, 0xc9, 0x17, 0x91, 0x24, 0x7c, 0xb2, 0x05, 0x64, 0xfb, 0x75, 0x64, 0x32, 0x39, 0x69, 0x5b, 0x9c, 0xad, 0xb9, 0xdb, 0xa7, 0xb5, 0x3b, 0x53, 0x2a, 0x21, 0x41, 0x44, 0xf3, 0x8b, 0x8f, 0x2e, 0x43, 0x9d, 0x2b, 0xd4, 0x57, 0x23, 0x41, 0x36, 0xff, 0x00, 0xff, 0xd7, 0xf0, 0xc0, 0xd5, 0xb5, 0x11, 0x64, 0xb6, 0x3f, 0x59, 0x90, 0xd9, 0xab, 0x06, 0xf4, 0x79, 0x7c, 0x3b, 0x74, 0xc8, 0x08, 0x8b, 0xb6, 0xe3, 0x96, 0x55, 0x57, 0xb3, 0x3e, 0xf2, 0x35, 0xc7, 0xd6, 0x0b, 0x45, 0x5d, 0xdc, 0x8a, 0x7d, 0xd9, 0x8d, 0x94, 0x3b, 0x3d, 0x1c, 0x9e, 0xc3, 0xe5, 0xc3, 0x2c, 0x7c, 0xc5, 0x0f, 0xee, 0xdb, 0x8b, 0x0c, 0xc4, 0x26, 0x9d, 0xa0, 0x9a, 0x7d, 0x2c, 0xe5, 0xe4, 0x55, 0x7f, 0xee, 0xc1, 0x15, 0x04, 0xd0, 0x12, 0x3c, 0x72, 0x89, 0x1b, 0x2c, 0xcc, 0xa8, 0x2a, 0x8b, 0x87, 0xbb, 0x63, 0x1a, 0x28, 0x65, 0xf0, 0xed, 0xf2, 0xc3, 0xc2, 0x0a, 0x06, 0x4a, 0x46, 0xc7, 0xa5, 0xa3, 0x59, 0xc8, 0xb2, 0xc7, 0x45, 0x22, 0x9c, 0x14, 0x54, 0x10, 0x46, 0xf5, 0x1d, 0x32, 0x5c, 0x14, 0x14, 0xe4, 0x32, 0x2f, 0x3a, 0xf3, 0xb6, 0x90, 0x9a, 0x6d, 0xae, 0x9f, 0x3d, 0xab, 0xb8, 0x8a, 0x3b, 0xf8, 0x39, 0x44, 0x58, 0xf0, 0x08, 0xd5, 0x14, 0xa5, 0x7b, 0x65, 0x98, 0x8e, 0xfb, 0xb5, 0x67, 0x87, 0xa5, 0xef, 0x5e, 0x44, 0x96, 0x35, 0xb5, 0xb6, 0x59, 0x36, 0xfd, 0xd8, 0xa0, 0xf1, 0x20, 0x53, 0x33, 0xc0, 0x79, 0x59, 0x73, 0x7c, 0xd7, 0xf9, 0xfb, 0xa2, 0xcd, 0x67, 0xf9, 0xa7, 0x7b, 0x72, 0xf1, 0x71, 0x83, 0x53, 0x86, 0x0b, 0x98, 0x24, 0x22, 0x8a, 0xcc, 0x88, 0x23, 0x7f, 0xb8, 0xae, 0xf9, 0x7c, 0x50, 0x1e, 0x5f, 0x7c, 0x48, 0x21, 0x44, 0x6b, 0xce, 0x9b, 0xb0, 0x1b, 0x9e, 0xf5, 0xaf, 0x8e, 0x4d, 0x5f, 0x7a, 0x7f, 0xce, 0x34, 0xf9, 0x5d, 0x3c, 0xa3, 0xf9, 0x69, 0x63, 0xa9, 0x3c, 0x27, 0xeb, 0xda, 0xe1, 0x37, 0xd7, 0x2e, 0xaa, 0xdb, 0x06, 0xda, 0x30, 0x49, 0xfe, 0x54, 0x03, 0x03, 0x49, 0xdc, 0xb3, 0xaf, 0x38, 0xfe, 0x6a, 0xf9, 0x47, 0xc9, 0x3a, 0x74, 0x97, 0xfa, 0xf6, 0xaf, 0x15, 0x85, 0xb8, 0x75, 0x89, 0xb8, 0x87, 0x9a, 0x72, 0xee, 0x2a, 0x14, 0x24, 0x60, 0xb1, 0xa8, 0xdf, 0x07, 0x0b, 0x2d, 0xcb, 0xcf, 0x7f, 0xe8, 0x6a, 0xff, 0x00, 0x26, 0xbd, 0x6a, 0x7f, 0x89, 0x2f, 0xf8, 0x52, 0x9e, 0xb7, 0xe8, 0xb9, 0xb8, 0x57, 0xc2, 0x95, 0xe9, 0x8f, 0x08, 0x5a, 0x2f, 0xff, 0xd0, 0xf0, 0x4d, 0x40, 0xaa, 0xd7, 0x00, 0x64, 0xcb, 0x3c, 0x97, 0xa8, 0xb5, 0x9e, 0xa3, 0x1a, 0xd6, 0x84, 0x95, 0x3f, 0x45, 0x72, 0x9c, 0xa2, 0xc3, 0x99, 0xa5, 0x9d, 0x49, 0xf4, 0x17, 0x97, 0xaf, 0x63, 0x17, 0x52, 0x6f, 0xf0, 0xc8, 0x43, 0x6f, 0x9a, 0xe9, 0x07, 0x70, 0x0e, 0xec, 0x83, 0x51, 0x44, 0xb8, 0x61, 0x1a, 0x9e, 0x11, 0xd3, 0x91, 0x60, 0x68, 0x6b, 0xd3, 0x31, 0x4f, 0x36, 0xd3, 0x4c, 0x52, 0xef, 0x4c, 0xd5, 0x0c, 0xc4, 0x69, 0xda, 0x94, 0xc8, 0x3a, 0xf0, 0x66, 0x07, 0x73, 0xe0, 0x40, 0xfd, 0x79, 0x93, 0x12, 0x1c, 0x9c, 0x32, 0xc7, 0xfc, 0x41, 0x33, 0xd2, 0xb4, 0x6f, 0x38, 0x98, 0x65, 0x76, 0xbf, 0x69, 0x42, 0xd0, 0xaa, 0xc9, 0xde, 0x95, 0xad, 0x28, 0x46, 0x4e, 0xac, 0x39, 0x77, 0x80, 0x11, 0xbf, 0xd8, 0xc7, 0x7c, 0xe1, 0xa5, 0xf9, 0x92, 0x4d, 0x32, 0x5b, 0x8b, 0x93, 0x27, 0xa7, 0x68, 0x56, 0xe2, 0x45, 0xda, 0x85, 0x61, 0x6e, 0x67, 0xad, 0x6b, 0xb0, 0x38, 0xc2, 0x81, 0xe4, 0xc7, 0x52, 0x31, 0x1c, 0x67, 0x86, 0x5b, 0xbd, 0x37, 0xca, 0x7a, 0x94, 0xb1, 0x69, 0xb6, 0x2e, 0xb7, 0x15, 0x48, 0xc2, 0xb4, 0x52, 0x53, 0xac, 0x32, 0xaf, 0xb1, 0xed, 0x9b, 0x10, 0x36, 0x78, 0x5c, 0x9f, 0x51, 0x64, 0x1f, 0x98, 0x3e, 0x58, 0xb6, 0xfc, 0xc8, 0xf2, 0xe5, 0xbc, 0x68, 0x52, 0x2d, 0x5a, 0xd1, 0x84, 0xb6, 0xf3, 0x95, 0x0e, 0xc0, 0x85, 0xe2, 0xcb, 0xd8, 0xd1, 0xbb, 0xe4, 0xc1, 0xa6, 0x97, 0xce, 0x17, 0x5f, 0x95, 0xde, 0x6d, 0xb6, 0xbe, 0xb7, 0x69, 0x34, 0xf3, 0x3c, 0x72, 0xcf, 0xe8, 0xa3, 0x45, 0x49, 0x95, 0x4a, 0x90, 0x3e, 0x35, 0x5a, 0x95, 0x1d, 0xfe, 0x21, 0x93, 0x4d, 0xbe, 0xd2, 0xd2, 0xf5, 0x8b, 0xbd, 0x32, 0x2d, 0x3f, 0x4c, 0x9a, 0xe4, 0xca, 0x9e, 0x90, 0x85, 0x65, 0x55, 0x08, 0x85, 0x91, 0x01, 0x3b, 0x0a, 0x05, 0xe9, 0xb0, 0xc0, 0x5a, 0xc3, 0xcd, 0x3f, 0x3b, 0x7f, 0x26, 0xec, 0xff, 0x00, 0x35, 0x6d, 0x6d, 0xb5, 0x3d, 0x16, 0xfe, 0x0d, 0x3b, 0xcd, 0x96, 0x01, 0x92, 0x46, 0x9e, 0xa2, 0x0b, 0xc8, 0xb7, 0x28, 0x92, 0x71, 0xfb, 0x2e, 0xa7, 0xec, 0x3d, 0x0f, 0xc2, 0x68, 0x71, 0x05, 0x95, 0xd3, 0xe7, 0x9f, 0xfa, 0x16, 0x2f, 0xcd, 0x7f, 0x43, 0xd6, 0xfa, 0xa5, 0x97, 0xab, 0xeb, 0x7a, 0x5f, 0x55, 0xfa, 0xec, 0x5e, 0xaf, 0x0f, 0xf7, 0xed, 0x2b, 0x4e, 0x15, 0xff, 0x00, 0x65, 0xdf, 0x8e, 0x14, 0xf1, 0xbf, 0xff, 0xd1, 0xf0, 0x5a, 0xa7, 0x18, 0x5e, 0x56, 0x1f, 0x68, 0x71, 0x5f, 0xa7, 0xbe, 0x2a, 0x98, 0xdb, 0xfa, 0x90, 0x24, 0x37, 0xb0, 0xfd, 0xb8, 0xa8, 0x58, 0x78, 0xae, 0x43, 0xc9, 0xb4, 0x6d, 0xbb, 0xda, 0x3c, 0xa1, 0xad, 0x43, 0xa8, 0xda, 0xc5, 0x2a, 0x3d, 0x26, 0x5a, 0x02, 0x2b, 0xbe, 0x60, 0x64, 0x8d, 0x17, 0x6f, 0x8b, 0x20, 0x90, 0x7a, 0x3c, 0x32, 0x8b, 0xa8, 0x02, 0xf3, 0xfd, 0xe0, 0x1b, 0x11, 0x98, 0x66, 0x3b, 0xb9, 0x62, 0x54, 0x83, 0x36, 0xf2, 0xa4, 0xe4, 0x29, 0x34, 0xeb, 0xc8, 0x74, 0xae, 0x0d, 0xc3, 0x65, 0x82, 0x13, 0x6b, 0x57, 0xba, 0x54, 0xe4, 0x8c, 0x41, 0x1b, 0x75, 0xa7, 0xe0, 0x72, 0x5c, 0x4c, 0x84, 0x50, 0x5a, 0xb3, 0xdd, 0xdd, 0xc3, 0x24, 0x33, 0xb1, 0x60, 0xe0, 0x86, 0x52, 0x45, 0x38, 0xd2, 0x87, 0x24, 0x26, 0x6d, 0x8c, 0xe1, 0x41, 0x25, 0xfc, 0xa3, 0xd7, 0x2f, 0x6f, 0x3c, 0xbf, 0x73, 0xa5, 0xb2, 0x2c, 0xd1, 0x69, 0x17, 0x2f, 0x6b, 0x14, 0x8c, 0x0f, 0x21, 0x0d, 0x79, 0x46, 0x09, 0x15, 0xed, 0xb7, 0x4e, 0xd9, 0xb9, 0x8b, 0xcb, 0xe4, 0xa2, 0x5e, 0xa3, 0xa6, 0xdf, 0x6a, 0x36, 0xe4, 0xcd, 0x69, 0x1c, 0x4e, 0x84, 0x7c, 0x76, 0xab, 0x21, 0x67, 0xa8, 0xa7, 0xd9, 0xf8, 0x4d, 0x2b, 0xf3, 0xc3, 0x4d, 0x49, 0x57, 0x98, 0x75, 0x6f, 0x31, 0xda, 0xf9, 0xa3, 0x4b, 0xfd, 0x1f, 0x69, 0x1d, 0xae, 0xa1, 0xa9, 0x7e, 0xee, 0xe6, 0xd2, 0x79, 0x18, 0xf3, 0xb5, 0x1f, 0xee, 0xd9, 0x0a, 0x01, 0x4e, 0x3f, 0xb3, 0x4d, 0xf2, 0x9c, 0xb9, 0x04, 0x05, 0xb7, 0xe2, 0x87, 0x1e, 0xdd, 0x19, 0x3e, 0xaf, 0x6b, 0xae, 0xcb, 0x6d, 0x13, 0x0d, 0x45, 0xa2, 0x8e, 0x06, 0xe5, 0x13, 0x2a, 0x02, 0x01, 0x5e, 0x82, 0xb5, 0x04, 0xe6, 0x11, 0xd4, 0xcd, 0xda, 0x43, 0x49, 0x8e, 0xb7, 0xdc, 0xb1, 0x51, 0xe6, 0x4d, 0x76, 0xd2, 0x61, 0x15, 0xaa, 0x4b, 0xa8, 0xc9, 0x6e, 0x49, 0x79, 0x20, 0xe6, 0x8c, 0x49, 0xad, 0x43, 0x16, 0xe4, 0xa7, 0xaf, 0x43, 0xd3, 0x26, 0x35, 0x75, 0xcd, 0xa8, 0xe8, 0x87, 0x46, 0xbf, 0xc7, 0x9a, 0xff, 0x00, 0xd6, 0xbf, 0x48, 0xfe, 0x88, 0xfd, 0xe7, 0x0f, 0xab, 0xfa, 0x3f, 0x58, 0x7f, 0x5f, 0x8d, 0x3f, 0x9f, 0xa7, 0x5e, 0xd4, 0xc3, 0xf9, 0xd1, 0x7c, 0xb6, 0x47, 0xe4, 0x3a, 0x5b, 0xff, 0xd2, 0xf0, 0xb7, 0xa6, 0x1e, 0xdf, 0xd3, 0xf6, 0xa5, 0x71, 0x54, 0xdb, 0x4b, 0x80, 0x3c, 0x42, 0x26, 0xee, 0x29, 0xbe, 0x51, 0x23, 0x4e, 0x44, 0x05, 0x84, 0x45, 0xa5, 0xd5, 0xf7, 0x97, 0x2e, 0xfd, 0x6b, 0x6a, 0x98, 0x09, 0xab, 0xc7, 0xfc, 0x46, 0x3b, 0x4c, 0x26, 0x32, 0x30, 0x3e, 0x4f, 0x49, 0xd0, 0xfc, 0xfb, 0x05, 0xd4, 0x4a, 0x7d, 0x40, 0xac, 0x3a, 0x8e, 0x84, 0x1c, 0xc5, 0x96, 0x2a, 0x73, 0xe1, 0x9c, 0x16, 0x6d, 0xa5, 0x79, 0x86, 0xd6, 0xec, 0x80, 0x5a, 0xa0, 0xf5, 0xca, 0xcc, 0x5c, 0xa1, 0x2b, 0x1b, 0x26, 0x30, 0x6a, 0x31, 0x46, 0xcf, 0x1c, 0x87, 0x94, 0x64, 0x9e, 0x3d, 0xb6, 0xf0, 0xca, 0xa8, 0x39, 0x51, 0x99, 0x42, 0x6b, 0x1a, 0xc5, 0xa5, 0xa5, 0x94, 0xf7, 0x92, 0xc8, 0xaa, 0xb1, 0x23, 0x30, 0x04, 0xf8, 0x0e, 0x9f, 0x4e, 0x4a, 0x11, 0xb2, 0xd5, 0x9b, 0x25, 0x06, 0x1b, 0xff, 0x00, 0x38, 0xfd, 0xad, 0xdf, 0xda, 0xf9, 0xa2, 0xfe, 0xc5, 0x42, 0xbe, 0x9b, 0x7f, 0x0b, 0xdd, 0xdd, 0x07, 0xaf, 0x14, 0x68, 0xd8, 0x71, 0x6d, 0xbb, 0x90, 0xfc, 0x73, 0x6e, 0xf2, 0xf2, 0xdd, 0xf4, 0xad, 0xa6, 0xab, 0x6d, 0x69, 0x14, 0xfa, 0xee, 0xa0, 0xe2, 0x0b, 0x0d, 0x39, 0x19, 0xfe, 0x11, 0xc5, 0x1a, 0x4a, 0x1d, 0x8f, 0x73, 0x4f, 0xf8, 0x96, 0x0b, 0x40, 0x8d, 0xec, 0xf3, 0x6d, 0x3f, 0x52, 0xba, 0xd6, 0x35, 0x8b, 0xbf, 0x36, 0x6a, 0x5f, 0x0d, 0xc5, 0xdc, 0xa8, 0xb6, 0xa8, 0x7a, 0xc5, 0x6c, 0x9b, 0x22, 0x0f, 0xa3, 0x73, 0x9a, 0xbc, 0xb3, 0xe2, 0x36, 0xed, 0xb1, 0x43, 0x80, 0x53, 0xd0, 0xa7, 0xd4, 0x44, 0xfa, 0x7a, 0xda, 0x83, 0xbd, 0x3e, 0x2f, 0xa7, 0x2b, 0xad, 0x9b, 0xb8, 0x8d, 0xa8, 0xe8, 0x91, 0xdb, 0xfa, 0x2d, 0x6f, 0xc3, 0x8a, 0x2d, 0x56, 0xa3, 0xad, 0x4f, 0x5c, 0xa4, 0x0d, 0xdc, 0xa3, 0xca, 0xd0, 0xbf, 0xa1, 0xe3, 0xfa, 0xe7, 0x0f, 0xf2, 0xb9, 0x57, 0xbf, 0x1a, 0xe4, 0xb8, 0x57, 0xc5, 0xdd, 0xff, 0xd3, 0xf0, 0xcc, 0x5d, 0x7b, 0x70, 0xc5, 0x53, 0x6d, 0x2f, 0xd5, 0xe4, 0x69, 0xfd, 0xdf, 0xec, 0xd7, 0xad, 0x7d, 0xb2, 0x8c, 0x8d, 0xd8, 0xed, 0x91, 0x9f, 0x43, 0xea, 0xe7, 0xeb, 0x94, 0xad, 0x3e, 0x1e, 0x95, 0xfc, 0x72, 0x81, 0x7d, 0x1c, 0x9d, 0xba, 0xb1, 0x7b, 0xdf, 0xa9, 0x7a, 0xdf, 0xee, 0x2f, 0xd4, 0xfa, 0xe7, 0xed, 0x7a, 0x7f, 0xdd, 0xff, 0x00, 0xb2, 0xae, 0x64, 0x0b, 0xea, 0xe3, 0x9a, 0xbf, 0x4a, 0x6f, 0xa4, 0xff, 0x00, 0x89, 0xbd, 0x45, 0xfa, 0xb5, 0x79, 0xf7, 0xeb, 0xc7, 0xe9, 0xae, 0x57, 0x2e, 0x17, 0x23, 0x1f, 0x89, 0xd1, 0x99, 0x8f, 0xf1, 0xa7, 0x11, 0xcf, 0xd3, 0xf5, 0x29, 0xb5, 0x6b, 0xd3, 0xe8, 0xcc, 0x7f, 0x45, 0xb9, 0xa3, 0xc5, 0x62, 0xbe, 0x68, 0xff, 0x00, 0x15, 0xfd, 0x4c, 0xfe, 0x90, 0xaf, 0xd4, 0xab, 0xf1, 0x7a, 0x7f, 0x62, 0x9d, 0xab, 0xdf, 0x32, 0xb1, 0x70, 0x5e, 0xdc, 0xdc, 0x2d, 0x47, 0x8b, 0x5e, 0xae, 0x4c, 0xbf, 0xf2, 0x37, 0x9f, 0x3d, 0x5b, 0xd2, 0xff, 0x00, 0x8e, 0x87, 0xee, 0x29, 0x5a, 0xf2, 0xf4, 0xaa, 0xd4, 0xa5, 0x36, 0xa7, 0x3a, 0x57, 0xfd, 0x8e, 0x64, 0x3a, 0xf2, 0xf6, 0xbf, 0xcc, 0x7f, 0x5b, 0xfc, 0x23, 0xa7, 0xfe, 0x8e, 0xff, 0x00, 0x8e, 0x37, 0xd6, 0x63, 0xfa, 0xe5, 0x2b, 0xcb, 0x87, 0xec, 0xd6, 0xbd, 0xb9, 0x7d, 0xac, 0xc7, 0xcd, 0x7c, 0x2d, 0xf8, 0x2b, 0x89, 0x26, 0x8f, 0xd4, 0xfa, 0x94, 0x3e, 0x85, 0x29, 0xc9, 0x69, 0xfc, 0x33, 0x58, 0x5d, 0x9c, 0x79, 0xb2, 0xbb, 0x0f, 0xac, 0x7a, 0x2b, 0xea, 0x75, 0xef, 0x92, 0x0c, 0x53, 0x3d, 0x2f, 0xd4, 0xfa, 0xbb, 0xfa, 0x74, 0xf5, 0x39, 0x9a, 0xd7, 0xe7, 0x80, 0x53, 0x79, 0xba, 0x5b, 0xfe, 0x97, 0xfa, 0x4b, 0xfc, 0xba, 0x7f, 0xb1, 0xc7, 0xab, 0x1e, 0x8f, 0xff, 0xd9
+};
diff --git a/base/testclient.cc b/base/testclient.cc
new file mode 100644
index 0000000..b6fd692
--- /dev/null
+++ b/base/testclient.cc
@@ -0,0 +1,171 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/testclient.h"
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/ptr_util.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+
+// DESIGN: Each packet received is put it into a list of packets.
+//         Callers can retrieve received packets from any thread by calling
+//         NextPacket.
+
+TestClient::TestClient(std::unique_ptr<AsyncPacketSocket> socket)
+    : TestClient(std::move(socket), nullptr) {}
+
+TestClient::TestClient(std::unique_ptr<AsyncPacketSocket> socket,
+                       FakeClock* fake_clock)
+    : fake_clock_(fake_clock),
+      socket_(std::move(socket)),
+      prev_packet_timestamp_(-1) {
+  socket_->SignalReadPacket.connect(this, &TestClient::OnPacket);
+  socket_->SignalReadyToSend.connect(this, &TestClient::OnReadyToSend);
+}
+
+TestClient::~TestClient() {}
+
+bool TestClient::CheckConnState(AsyncPacketSocket::State state) {
+  // Wait for our timeout value until the socket reaches the desired state.
+  int64_t end = TimeAfter(kTimeoutMs);
+  while (socket_->GetState() != state && TimeUntil(end) > 0) {
+    AdvanceTime(1);
+  }
+  return (socket_->GetState() == state);
+}
+
+int TestClient::Send(const char* buf, size_t size) {
+  rtc::PacketOptions options;
+  return socket_->Send(buf, size, options);
+}
+
+int TestClient::SendTo(const char* buf, size_t size,
+                       const SocketAddress& dest) {
+  rtc::PacketOptions options;
+  return socket_->SendTo(buf, size, dest, options);
+}
+
+std::unique_ptr<TestClient::Packet> TestClient::NextPacket(int timeout_ms) {
+  // If no packets are currently available, we go into a get/dispatch loop for
+  // at most timeout_ms.  If, during the loop, a packet arrives, then we can
+  // stop early and return it.
+
+  // Note that the case where no packet arrives is important.  We often want to
+  // test that a packet does not arrive.
+
+  // Note also that we only try to pump our current thread's message queue.
+  // Pumping another thread's queue could lead to messages being dispatched from
+  // the wrong thread to non-thread-safe objects.
+
+  int64_t end = TimeAfter(timeout_ms);
+  while (TimeUntil(end) > 0) {
+    {
+      CritScope cs(&crit_);
+      if (packets_.size() != 0) {
+        break;
+      }
+    }
+    AdvanceTime(1);
+  }
+
+  // Return the first packet placed in the queue.
+  std::unique_ptr<Packet> packet;
+  CritScope cs(&crit_);
+  if (packets_.size() > 0) {
+    packet = std::move(packets_.front());
+    packets_.erase(packets_.begin());
+  }
+
+  return packet;
+}
+
+bool TestClient::CheckNextPacket(const char* buf, size_t size,
+                                 SocketAddress* addr) {
+  bool res = false;
+  std::unique_ptr<Packet> packet = NextPacket(kTimeoutMs);
+  if (packet) {
+    res = (packet->size == size && memcmp(packet->buf, buf, size) == 0 &&
+           CheckTimestamp(packet->packet_time.timestamp));
+    if (addr)
+      *addr = packet->addr;
+  }
+  return res;
+}
+
+bool TestClient::CheckTimestamp(int64_t packet_timestamp) {
+  bool res = true;
+  if (packet_timestamp == -1) {
+    res = false;
+  }
+  if (prev_packet_timestamp_ != -1) {
+    if (packet_timestamp < prev_packet_timestamp_) {
+      res = false;
+    }
+  }
+  prev_packet_timestamp_ = packet_timestamp;
+  return res;
+}
+
+void TestClient::AdvanceTime(int ms) {
+  // If the test is using a fake clock, we must advance the fake clock to
+  // advance time. Otherwise, ProcessMessages will work.
+  if (fake_clock_) {
+    SIMULATED_WAIT(false, ms, *fake_clock_);
+  } else {
+    Thread::Current()->ProcessMessages(1);
+  }
+}
+
+bool TestClient::CheckNoPacket() {
+  return NextPacket(kNoPacketTimeoutMs) == nullptr;
+}
+
+int TestClient::GetError() {
+  return socket_->GetError();
+}
+
+int TestClient::SetOption(Socket::Option opt, int value) {
+  return socket_->SetOption(opt, value);
+}
+
+void TestClient::OnPacket(AsyncPacketSocket* socket, const char* buf,
+                          size_t size, const SocketAddress& remote_addr,
+                          const PacketTime& packet_time) {
+  CritScope cs(&crit_);
+  packets_.push_back(MakeUnique<Packet>(remote_addr, buf, size, packet_time));
+}
+
+void TestClient::OnReadyToSend(AsyncPacketSocket* socket) {
+  ++ready_to_send_count_;
+}
+
+TestClient::Packet::Packet(const SocketAddress& a,
+                           const char* b,
+                           size_t s,
+                           const PacketTime& packet_time)
+    : addr(a), buf(0), size(s), packet_time(packet_time) {
+  buf = new char[size];
+  memcpy(buf, b, size);
+}
+
+TestClient::Packet::Packet(const Packet& p)
+    : addr(p.addr), buf(0), size(p.size), packet_time(p.packet_time) {
+  buf = new char[size];
+  memcpy(buf, p.buf, size);
+}
+
+TestClient::Packet::~Packet() {
+  delete[] buf;
+}
+
+}  // namespace rtc
diff --git a/base/testclient.h b/base/testclient.h
index 378e2b8..bd78d83 100644
--- a/base/testclient.h
+++ b/base/testclient.h
@@ -11,9 +11,104 @@
 #ifndef WEBRTC_BASE_TESTCLIENT_H_
 #define WEBRTC_BASE_TESTCLIENT_H_
 
+#include <memory>
+#include <vector>
+#include "webrtc/base/asyncudpsocket.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/fakeclock.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/testclient.h"
+namespace rtc {
+
+// A simple client that can send TCP or UDP data and check that it receives
+// what it expects to receive. Useful for testing server functionality.
+class TestClient : public sigslot::has_slots<> {
+ public:
+  // Records the contents of a packet that was received.
+  struct Packet {
+    Packet(const SocketAddress& a,
+           const char* b,
+           size_t s,
+           const PacketTime& packet_time);
+    Packet(const Packet& p);
+    virtual ~Packet();
+
+    SocketAddress addr;
+    char*  buf;
+    size_t size;
+    PacketTime packet_time;
+  };
+
+  // Default timeout for NextPacket reads.
+  static const int kTimeoutMs = 5000;
+
+  // Creates a client that will send and receive with the given socket and
+  // will post itself messages with the given thread.
+  explicit TestClient(std::unique_ptr<AsyncPacketSocket> socket);
+  // Create a test client that will use a fake clock. NextPacket needs to wait
+  // for a packet to be received, and thus it needs to advance the fake clock
+  // if the test is using one, rather than just sleeping.
+  TestClient(std::unique_ptr<AsyncPacketSocket> socket, FakeClock* fake_clock);
+  ~TestClient() override;
+
+  SocketAddress address() const { return socket_->GetLocalAddress(); }
+  SocketAddress remote_address() const { return socket_->GetRemoteAddress(); }
+
+  // Checks that the socket moves to the specified connect state.
+  bool CheckConnState(AsyncPacketSocket::State state);
+
+  // Checks that the socket is connected to the remote side.
+  bool CheckConnected() {
+    return CheckConnState(AsyncPacketSocket::STATE_CONNECTED);
+  }
+
+  // Sends using the clients socket.
+  int Send(const char* buf, size_t size);
+
+  // Sends using the clients socket to the given destination.
+  int SendTo(const char* buf, size_t size, const SocketAddress& dest);
+
+  // Returns the next packet received by the client or null if none is received
+  // within the specified timeout.
+  std::unique_ptr<Packet> NextPacket(int timeout_ms);
+
+  // Checks that the next packet has the given contents. Returns the remote
+  // address that the packet was sent from.
+  bool CheckNextPacket(const char* buf, size_t len, SocketAddress* addr);
+
+  // Checks that no packets have arrived or will arrive in the next second.
+  bool CheckNoPacket();
+
+  int GetError();
+  int SetOption(Socket::Option opt, int value);
+
+  bool ready_to_send() const { return ready_to_send_count() > 0; }
+
+  // How many times SignalReadyToSend has been fired.
+  int ready_to_send_count() const { return ready_to_send_count_; }
+
+ private:
+  // Timeout for reads when no packet is expected.
+  static const int kNoPacketTimeoutMs = 1000;
+  // Workaround for the fact that AsyncPacketSocket::GetConnState doesn't exist.
+  Socket::ConnState GetState();
+  // Slot for packets read on the socket.
+  void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t len,
+                const SocketAddress& remote_addr,
+                const PacketTime& packet_time);
+  void OnReadyToSend(AsyncPacketSocket* socket);
+  bool CheckTimestamp(int64_t packet_timestamp);
+  void AdvanceTime(int ms);
+
+  FakeClock* fake_clock_ = nullptr;
+  CriticalSection crit_;
+  std::unique_ptr<AsyncPacketSocket> socket_;
+  std::vector<std::unique_ptr<Packet>> packets_;
+  int ready_to_send_count_ = 0;
+  int64_t prev_packet_timestamp_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(TestClient);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_TESTCLIENT_H_
diff --git a/base/testclient_unittest.cc b/base/testclient_unittest.cc
new file mode 100644
index 0000000..8392abf
--- /dev/null
+++ b/base/testclient_unittest.cc
@@ -0,0 +1,96 @@
+/*
+ *  Copyright 2006 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/testclient.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/nethelpers.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/ptr_util.h"
+#include "webrtc/base/testechoserver.h"
+#include "webrtc/base/thread.h"
+
+using namespace rtc;
+
+#define MAYBE_SKIP_IPV4                    \
+  if (!HasIPv4Enabled()) {                 \
+    LOG(LS_INFO) << "No IPv4... skipping"; \
+    return;                                \
+  }
+
+#define MAYBE_SKIP_IPV6                    \
+  if (!HasIPv6Enabled()) {                 \
+    LOG(LS_INFO) << "No IPv6... skipping"; \
+    return;                                \
+  }
+
+void TestUdpInternal(const SocketAddress& loopback) {
+  Thread *main = Thread::Current();
+  AsyncSocket* socket = main->socketserver()
+      ->CreateAsyncSocket(loopback.family(), SOCK_DGRAM);
+  socket->Bind(loopback);
+
+  TestClient client(MakeUnique<AsyncUDPSocket>(socket));
+  SocketAddress addr = client.address(), from;
+  EXPECT_EQ(3, client.SendTo("foo", 3, addr));
+  EXPECT_TRUE(client.CheckNextPacket("foo", 3, &from));
+  EXPECT_EQ(from, addr);
+  EXPECT_TRUE(client.CheckNoPacket());
+}
+
+void TestTcpInternal(const SocketAddress& loopback) {
+  Thread *main = Thread::Current();
+  TestEchoServer server(main, loopback);
+
+  AsyncSocket* socket = main->socketserver()
+      ->CreateAsyncSocket(loopback.family(), SOCK_STREAM);
+  std::unique_ptr<AsyncTCPSocket> tcp_socket =
+      WrapUnique(AsyncTCPSocket::Create(socket, loopback, server.address()));
+  ASSERT_TRUE(tcp_socket != nullptr);
+
+  TestClient client(std::move(tcp_socket));
+  SocketAddress addr = client.address(), from;
+  EXPECT_TRUE(client.CheckConnected());
+  EXPECT_EQ(3, client.Send("foo", 3));
+  EXPECT_TRUE(client.CheckNextPacket("foo", 3, &from));
+  EXPECT_EQ(from, server.address());
+  EXPECT_TRUE(client.CheckNoPacket());
+}
+
+// Tests whether the TestClient can send UDP to itself.
+TEST(TestClientTest, TestUdpIPv4) {
+  MAYBE_SKIP_IPV4;
+  TestUdpInternal(SocketAddress("127.0.0.1", 0));
+}
+
+#if defined(WEBRTC_LINUX)
+#define MAYBE_TestUdpIPv6 DISABLED_TestUdpIPv6
+#else
+#define MAYBE_TestUdpIPv6 TestUdpIPv6
+#endif
+TEST(TestClientTest, MAYBE_TestUdpIPv6) {
+  MAYBE_SKIP_IPV6;
+  TestUdpInternal(SocketAddress("::1", 0));
+}
+
+// Tests whether the TestClient can connect to a server and exchange data.
+TEST(TestClientTest, TestTcpIPv4) {
+  MAYBE_SKIP_IPV4;
+  TestTcpInternal(SocketAddress("127.0.0.1", 0));
+}
+
+#if defined(WEBRTC_LINUX)
+#define MAYBE_TestTcpIPv6 DISABLED_TestTcpIPv6
+#else
+#define MAYBE_TestTcpIPv6 TestTcpIPv6
+#endif
+TEST(TestClientTest, MAYBE_TestTcpIPv6) {
+  MAYBE_SKIP_IPV6;
+  TestTcpInternal(SocketAddress("::1", 0));
+}
diff --git a/base/testechoserver.h b/base/testechoserver.h
index 21365e2..97606c1 100644
--- a/base/testechoserver.h
+++ b/base/testechoserver.h
@@ -11,9 +11,65 @@
 #ifndef WEBRTC_BASE_TESTECHOSERVER_H_
 #define WEBRTC_BASE_TESTECHOSERVER_H_
 
+#include <list>
+#include <memory>
+#include "webrtc/base/asynctcpsocket.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/thread.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/testechoserver.h"
+namespace rtc {
+
+// A test echo server, echoes back any packets sent to it.
+// Useful for unit tests.
+class TestEchoServer : public sigslot::has_slots<> {
+ public:
+  TestEchoServer(Thread* thread, const SocketAddress& addr)
+      : server_socket_(thread->socketserver()->CreateAsyncSocket(addr.family(),
+                                                                 SOCK_STREAM)) {
+    server_socket_->Bind(addr);
+    server_socket_->Listen(5);
+    server_socket_->SignalReadEvent.connect(this, &TestEchoServer::OnAccept);
+  }
+  ~TestEchoServer() {
+    for (ClientList::iterator it = client_sockets_.begin();
+         it != client_sockets_.end(); ++it) {
+      delete *it;
+    }
+  }
+
+  SocketAddress address() const { return server_socket_->GetLocalAddress(); }
+
+ private:
+  void OnAccept(AsyncSocket* socket) {
+    AsyncSocket* raw_socket = socket->Accept(nullptr);
+    if (raw_socket) {
+      AsyncTCPSocket* packet_socket = new AsyncTCPSocket(raw_socket, false);
+      packet_socket->SignalReadPacket.connect(this, &TestEchoServer::OnPacket);
+      packet_socket->SignalClose.connect(this, &TestEchoServer::OnClose);
+      client_sockets_.push_back(packet_socket);
+    }
+  }
+  void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t size,
+                const SocketAddress& remote_addr,
+                const PacketTime& packet_time) {
+    rtc::PacketOptions options;
+    socket->Send(buf, size, options);
+  }
+  void OnClose(AsyncPacketSocket* socket, int err) {
+    ClientList::iterator it =
+        std::find(client_sockets_.begin(), client_sockets_.end(), socket);
+    client_sockets_.erase(it);
+    Thread::Current()->Dispose(socket);
+  }
+
+  typedef std::list<AsyncTCPSocket*> ClientList;
+  std::unique_ptr<AsyncSocket> server_socket_;
+  ClientList client_sockets_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(TestEchoServer);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_TESTECHOSERVER_H_
diff --git a/base/testutils.h b/base/testutils.h
index 74f2160..cda7a84 100644
--- a/base/testutils.h
+++ b/base/testutils.h
@@ -8,12 +8,559 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_TESTUTILS_H_
-#define WEBRTC_BASE_TESTUTILS_H_
+#ifndef WEBRTC_BASE_TESTUTILS_H__
+#define WEBRTC_BASE_TESTUTILS_H__
 
+// Utilities for testing rtc infrastructure in unittests
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/testutils.h"
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
 
-#endif  // WEBRTC_BASE_TESTUTILS_H_
+// X defines a few macros that stomp on types that gunit.h uses.
+#undef None
+#undef Bool
+#endif
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <vector>
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/asyncsocket.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/nethelpers.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringencode.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread.h"
+
+namespace webrtc {
+namespace testing {
+
+using namespace rtc;
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamSink - Monitor asynchronously signalled events from StreamInterface
+// or AsyncSocket (which should probably be a StreamInterface.
+///////////////////////////////////////////////////////////////////////////////
+
+// Note: Any event that is an error is treaded as SSE_ERROR instead of that
+// event.
+
+enum StreamSinkEvent {
+  SSE_OPEN  = SE_OPEN,
+  SSE_READ  = SE_READ,
+  SSE_WRITE = SE_WRITE,
+  SSE_CLOSE = SE_CLOSE,
+  SSE_ERROR = 16
+};
+
+class StreamSink : public sigslot::has_slots<> {
+ public:
+  void Monitor(StreamInterface* stream) {
+   stream->SignalEvent.connect(this, &StreamSink::OnEvent);
+   events_.erase(stream);
+  }
+  void Unmonitor(StreamInterface* stream) {
+   stream->SignalEvent.disconnect(this);
+   // In case you forgot to unmonitor a previous object with this address
+   events_.erase(stream);
+  }
+  bool Check(StreamInterface* stream, StreamSinkEvent event, bool reset = true) {
+    return DoCheck(stream, event, reset);
+  }
+  int Events(StreamInterface* stream, bool reset = true) {
+    return DoEvents(stream, reset);
+  }
+
+  void Monitor(AsyncSocket* socket) {
+   socket->SignalConnectEvent.connect(this, &StreamSink::OnConnectEvent);
+   socket->SignalReadEvent.connect(this, &StreamSink::OnReadEvent);
+   socket->SignalWriteEvent.connect(this, &StreamSink::OnWriteEvent);
+   socket->SignalCloseEvent.connect(this, &StreamSink::OnCloseEvent);
+   // In case you forgot to unmonitor a previous object with this address
+   events_.erase(socket);
+  }
+  void Unmonitor(AsyncSocket* socket) {
+   socket->SignalConnectEvent.disconnect(this);
+   socket->SignalReadEvent.disconnect(this);
+   socket->SignalWriteEvent.disconnect(this);
+   socket->SignalCloseEvent.disconnect(this);
+   events_.erase(socket);
+  }
+  bool Check(AsyncSocket* socket, StreamSinkEvent event, bool reset = true) {
+    return DoCheck(socket, event, reset);
+  }
+  int Events(AsyncSocket* socket, bool reset = true) {
+    return DoEvents(socket, reset);
+  }
+
+ private:
+  typedef std::map<void*,int> EventMap;
+
+  void OnEvent(StreamInterface* stream, int events, int error) {
+    if (error) {
+      events = SSE_ERROR;
+    }
+    AddEvents(stream, events);
+  }
+  void OnConnectEvent(AsyncSocket* socket) {
+    AddEvents(socket, SSE_OPEN);
+  }
+  void OnReadEvent(AsyncSocket* socket) {
+    AddEvents(socket, SSE_READ);
+  }
+  void OnWriteEvent(AsyncSocket* socket) {
+    AddEvents(socket, SSE_WRITE);
+  }
+  void OnCloseEvent(AsyncSocket* socket, int error) {
+    AddEvents(socket, (0 == error) ? SSE_CLOSE : SSE_ERROR);
+  }
+
+  void AddEvents(void* obj, int events) {
+    EventMap::iterator it = events_.find(obj);
+    if (events_.end() == it) {
+      events_.insert(EventMap::value_type(obj, events));
+    } else {
+      it->second |= events;
+    }
+  }
+  bool DoCheck(void* obj, StreamSinkEvent event, bool reset) {
+    EventMap::iterator it = events_.find(obj);
+    if ((events_.end() == it) || (0 == (it->second & event))) {
+      return false;
+    }
+    if (reset) {
+      it->second &= ~event;
+    }
+    return true;
+  }
+  int DoEvents(void* obj, bool reset) {
+    EventMap::iterator it = events_.find(obj);
+    if (events_.end() == it)
+      return 0;
+    int events = it->second;
+    if (reset) {
+      it->second = 0;
+    }
+    return events;
+  }
+
+  EventMap events_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamSource - Implements stream interface and simulates asynchronous
+// events on the stream, without a network.  Also buffers written data.
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamSource : public StreamInterface {
+public:
+  StreamSource() {
+    Clear();
+  }
+
+  void Clear() {
+    readable_data_.clear();
+    written_data_.clear();
+    state_ = SS_CLOSED;
+    read_block_ = 0;
+    write_block_ = SIZE_UNKNOWN;
+  }
+  void QueueString(const char* data) {
+    QueueData(data, strlen(data));
+  }
+  void QueueStringF(const char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    char buffer[1024];
+    size_t len = vsprintfn(buffer, sizeof(buffer), format, args);
+    RTC_CHECK(len < sizeof(buffer) - 1);
+    va_end(args);
+    QueueData(buffer, len);
+  }
+  void QueueData(const char* data, size_t len) {
+    readable_data_.insert(readable_data_.end(), data, data + len);
+    if ((SS_OPEN == state_) && (readable_data_.size() == len)) {
+      SignalEvent(this, SE_READ, 0);
+    }
+  }
+  std::string ReadData() {
+    std::string data;
+    // avoid accessing written_data_[0] if it is undefined
+    if (written_data_.size() > 0) {
+      data.insert(0, &written_data_[0], written_data_.size());
+    }
+    written_data_.clear();
+    return data;
+  }
+  void SetState(StreamState state) {
+    int events = 0;
+    if ((SS_OPENING == state_) && (SS_OPEN == state)) {
+      events |= SE_OPEN;
+      if (!readable_data_.empty()) {
+        events |= SE_READ;
+      }
+    } else if ((SS_CLOSED != state_) && (SS_CLOSED == state)) {
+      events |= SE_CLOSE;
+    }
+    state_ = state;
+    if (events) {
+      SignalEvent(this, events, 0);
+    }
+  }
+  // Will cause Read to block when there are pos bytes in the read queue.
+  void SetReadBlock(size_t pos) { read_block_ = pos; }
+  // Will cause Write to block when there are pos bytes in the write queue.
+  void SetWriteBlock(size_t pos) { write_block_ = pos; }
+
+  virtual StreamState GetState() const { return state_; }
+  virtual StreamResult Read(void* buffer, size_t buffer_len,
+                            size_t* read, int* error) {
+    if (SS_CLOSED == state_) {
+      if (error) *error = -1;
+      return SR_ERROR;
+    }
+    if ((SS_OPENING == state_) || (readable_data_.size() <= read_block_)) {
+      return SR_BLOCK;
+    }
+    size_t count = std::min(buffer_len, readable_data_.size() - read_block_);
+    memcpy(buffer, &readable_data_[0], count);
+    size_t new_size = readable_data_.size() - count;
+    // Avoid undefined access beyond the last element of the vector.
+    // This only happens when new_size is 0.
+    if (count < readable_data_.size()) {
+      memmove(&readable_data_[0], &readable_data_[count], new_size);
+    }
+    readable_data_.resize(new_size);
+    if (read) *read = count;
+    return SR_SUCCESS;
+  }
+  virtual StreamResult Write(const void* data, size_t data_len,
+                             size_t* written, int* error) {
+    if (SS_CLOSED == state_) {
+      if (error) *error = -1;
+      return SR_ERROR;
+    }
+    if (SS_OPENING == state_) {
+      return SR_BLOCK;
+    }
+    if (SIZE_UNKNOWN != write_block_) {
+      if (written_data_.size() >= write_block_) {
+        return SR_BLOCK;
+      }
+      if (data_len > (write_block_ - written_data_.size())) {
+        data_len = write_block_ - written_data_.size();
+      }
+    }
+    if (written) *written = data_len;
+    const char* cdata = static_cast<const char*>(data);
+    written_data_.insert(written_data_.end(), cdata, cdata + data_len);
+    return SR_SUCCESS;
+  }
+  virtual void Close() { state_ = SS_CLOSED; }
+
+private:
+  typedef std::vector<char> Buffer;
+  Buffer readable_data_, written_data_;
+  StreamState state_;
+  size_t read_block_, write_block_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SocketTestClient
+// Creates a simulated client for testing.  Works on real and virtual networks.
+///////////////////////////////////////////////////////////////////////////////
+
+class SocketTestClient : public sigslot::has_slots<> {
+public:
+ SocketTestClient() { Init(nullptr, AF_INET); }
+ SocketTestClient(AsyncSocket* socket) {
+   Init(socket, socket->GetLocalAddress().family());
+  }
+  SocketTestClient(const SocketAddress& address) {
+    Init(nullptr, address.family());
+    socket_->Connect(address);
+  }
+
+  AsyncSocket* socket() { return socket_.get(); }
+
+  void QueueString(const char* data) {
+    QueueData(data, strlen(data));
+  }
+  void QueueStringF(const char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    char buffer[1024];
+    size_t len = vsprintfn(buffer, sizeof(buffer), format, args);
+    RTC_CHECK(len < sizeof(buffer) - 1);
+    va_end(args);
+    QueueData(buffer, len);
+  }
+  void QueueData(const char* data, size_t len) {
+    send_buffer_.insert(send_buffer_.end(), data, data + len);
+    if (Socket::CS_CONNECTED == socket_->GetState()) {
+      Flush();
+    }
+  }
+  std::string ReadData() {
+    std::string data(&recv_buffer_[0], recv_buffer_.size());
+    recv_buffer_.clear();
+    return data;
+  }
+
+  bool IsConnected() const {
+    return (Socket::CS_CONNECTED == socket_->GetState());
+  }
+  bool IsClosed() const {
+    return (Socket::CS_CLOSED == socket_->GetState());
+  }
+
+private:
+  typedef std::vector<char> Buffer;
+
+  void Init(AsyncSocket* socket, int family) {
+    if (!socket) {
+      socket = Thread::Current()->socketserver()
+          ->CreateAsyncSocket(family, SOCK_STREAM);
+    }
+    socket_.reset(socket);
+    socket_->SignalConnectEvent.connect(this,
+      &SocketTestClient::OnConnectEvent);
+    socket_->SignalReadEvent.connect(this, &SocketTestClient::OnReadEvent);
+    socket_->SignalWriteEvent.connect(this, &SocketTestClient::OnWriteEvent);
+    socket_->SignalCloseEvent.connect(this, &SocketTestClient::OnCloseEvent);
+  }
+
+  void Flush() {
+    size_t sent = 0;
+    while (sent < send_buffer_.size()) {
+      int result = socket_->Send(&send_buffer_[sent],
+                                 send_buffer_.size() - sent);
+      if (result > 0) {
+        sent += result;
+      } else {
+        break;
+      }
+    }
+    size_t new_size = send_buffer_.size() - sent;
+    memmove(&send_buffer_[0], &send_buffer_[sent], new_size);
+    send_buffer_.resize(new_size);
+  }
+
+  void OnConnectEvent(AsyncSocket* socket) {
+    if (!send_buffer_.empty()) {
+      Flush();
+    }
+  }
+  void OnReadEvent(AsyncSocket* socket) {
+    char data[64 * 1024];
+    int result = socket_->Recv(data, arraysize(data), nullptr);
+    if (result > 0) {
+      recv_buffer_.insert(recv_buffer_.end(), data, data + result);
+    }
+  }
+  void OnWriteEvent(AsyncSocket* socket) {
+    if (!send_buffer_.empty()) {
+      Flush();
+    }
+  }
+  void OnCloseEvent(AsyncSocket* socket, int error) {
+  }
+
+  std::unique_ptr<AsyncSocket> socket_;
+  Buffer send_buffer_, recv_buffer_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SocketTestServer
+// Creates a simulated server for testing.  Works on real and virtual networks.
+///////////////////////////////////////////////////////////////////////////////
+
+class SocketTestServer : public sigslot::has_slots<> {
+ public:
+  SocketTestServer(const SocketAddress& address)
+      : socket_(Thread::Current()->socketserver()
+                ->CreateAsyncSocket(address.family(), SOCK_STREAM))
+  {
+    socket_->SignalReadEvent.connect(this, &SocketTestServer::OnReadEvent);
+    socket_->Bind(address);
+    socket_->Listen(5);
+  }
+  virtual ~SocketTestServer() {
+    clear();
+  }
+
+  size_t size() const { return clients_.size(); }
+  SocketTestClient* client(size_t index) const { return clients_[index]; }
+  SocketTestClient* operator[](size_t index) const { return client(index); }
+
+  void clear() {
+    for (size_t i=0; i<clients_.size(); ++i) {
+      delete clients_[i];
+    }
+    clients_.clear();
+  }
+
+ private:
+  void OnReadEvent(AsyncSocket* socket) {
+    AsyncSocket* accepted = static_cast<AsyncSocket*>(socket_->Accept(nullptr));
+    if (!accepted)
+      return;
+    clients_.push_back(new SocketTestClient(accepted));
+  }
+
+  std::unique_ptr<AsyncSocket> socket_;
+  std::vector<SocketTestClient*> clients_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Unittest predicates which are similar to STREQ, but for raw memory
+///////////////////////////////////////////////////////////////////////////////
+
+inline ::testing::AssertionResult CmpHelperMemEq(
+    const char* expected_expression,
+    const char* expected_length_expression,
+    const char* actual_expression,
+    const char* actual_length_expression,
+    const void* expected,
+    size_t expected_length,
+    const void* actual,
+    size_t actual_length) {
+  if ((expected_length == actual_length)
+      && (0 == memcmp(expected, actual, expected_length))) {
+    return ::testing::AssertionSuccess();
+  }
+
+  ::testing::Message msg;
+  msg << "Value of: " << actual_expression
+      << " [" << actual_length_expression << "]";
+  if (true) {  //!actual_value.Equals(actual_expression)) {
+    size_t buffer_size = actual_length * 2 + 1;
+    char* buffer = STACK_ARRAY(char, buffer_size);
+    hex_encode(buffer, buffer_size,
+               reinterpret_cast<const char*>(actual), actual_length);
+    msg << "\n  Actual: " << buffer << " [" << actual_length << "]";
+  }
+
+  msg << "\nExpected: " << expected_expression
+      << " [" << expected_length_expression << "]";
+  if (true) {  //!expected_value.Equals(expected_expression)) {
+    size_t buffer_size = expected_length * 2 + 1;
+    char* buffer = STACK_ARRAY(char, buffer_size);
+    hex_encode(buffer, buffer_size,
+               reinterpret_cast<const char*>(expected), expected_length);
+    msg << "\nWhich is: " << buffer << " [" << expected_length << "]";
+  }
+
+  return AssertionFailure(msg);
+}
+
+#define EXPECT_MEMEQ(expected, expected_length, actual, actual_length) \
+  EXPECT_PRED_FORMAT4(::testing::CmpHelperMemEq, expected, expected_length, \
+                      actual, actual_length)
+
+#define ASSERT_MEMEQ(expected, expected_length, actual, actual_length) \
+  ASSERT_PRED_FORMAT4(::testing::CmpHelperMemEq, expected, expected_length, \
+                      actual, actual_length)
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for initializing constant memory with integers in a particular byte
+// order
+///////////////////////////////////////////////////////////////////////////////
+
+#define BYTE_CAST(x) static_cast<uint8_t>((x)&0xFF)
+
+// Declare a N-bit integer as a little-endian sequence of bytes
+#define LE16(x) BYTE_CAST(((uint16_t)x) >> 0), BYTE_CAST(((uint16_t)x) >> 8)
+
+#define LE32(x) \
+  BYTE_CAST(((uint32_t)x) >> 0), BYTE_CAST(((uint32_t)x) >> 8), \
+      BYTE_CAST(((uint32_t)x) >> 16), BYTE_CAST(((uint32_t)x) >> 24)
+
+#define LE64(x) \
+  BYTE_CAST(((uint64_t)x) >> 0), BYTE_CAST(((uint64_t)x) >> 8),       \
+      BYTE_CAST(((uint64_t)x) >> 16), BYTE_CAST(((uint64_t)x) >> 24), \
+      BYTE_CAST(((uint64_t)x) >> 32), BYTE_CAST(((uint64_t)x) >> 40), \
+      BYTE_CAST(((uint64_t)x) >> 48), BYTE_CAST(((uint64_t)x) >> 56)
+
+// Declare a N-bit integer as a big-endian (Internet) sequence of bytes
+#define BE16(x) BYTE_CAST(((uint16_t)x) >> 8), BYTE_CAST(((uint16_t)x) >> 0)
+
+#define BE32(x) \
+  BYTE_CAST(((uint32_t)x) >> 24), BYTE_CAST(((uint32_t)x) >> 16), \
+      BYTE_CAST(((uint32_t)x) >> 8), BYTE_CAST(((uint32_t)x) >> 0)
+
+#define BE64(x) \
+  BYTE_CAST(((uint64_t)x) >> 56), BYTE_CAST(((uint64_t)x) >> 48),     \
+      BYTE_CAST(((uint64_t)x) >> 40), BYTE_CAST(((uint64_t)x) >> 32), \
+      BYTE_CAST(((uint64_t)x) >> 24), BYTE_CAST(((uint64_t)x) >> 16), \
+      BYTE_CAST(((uint64_t)x) >> 8), BYTE_CAST(((uint64_t)x) >> 0)
+
+// Declare a N-bit integer as a this-endian (local machine) sequence of bytes
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 1
+#endif  // BIG_ENDIAN
+
+#if BIG_ENDIAN
+#define TE16 BE16
+#define TE32 BE32
+#define TE64 BE64
+#else  // !BIG_ENDIAN
+#define TE16 LE16
+#define TE32 LE32
+#define TE64 LE64
+#endif  // !BIG_ENDIAN
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Helpers for determining if X/screencasting is available (on linux).
+
+#define MAYBE_SKIP_SCREENCAST_TEST() \
+  if (!testing::IsScreencastingAvailable()) { \
+    LOG(LS_WARNING) << "Skipping test, since it doesn't have the requisite " \
+                    << "X environment for screen capture."; \
+    return; \
+  } \
+
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+struct XDisplay {
+  XDisplay() : display_(XOpenDisplay(nullptr)) {}
+  ~XDisplay() { if (display_) XCloseDisplay(display_); }
+  bool IsValid() const { return display_ != nullptr; }
+  operator Display*() { return display_; }
+ private:
+  Display* display_;
+};
+#endif
+
+// Returns true if screencasting is available. When false, anything that uses
+// screencasting features may fail.
+inline bool IsScreencastingAvailable() {
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+  XDisplay display;
+  if (!display.IsValid()) {
+    LOG(LS_WARNING) << "No X Display available.";
+    return false;
+  }
+  int ignored_int, major_version, minor_version;
+  if (!XRRQueryExtension(display, &ignored_int, &ignored_int) ||
+      !XRRQueryVersion(display, &major_version, &minor_version) ||
+      major_version < 1 ||
+      (major_version < 2 && minor_version < 3)) {
+    LOG(LS_WARNING) << "XRandr version: " << major_version << "."
+                    << minor_version;
+    LOG(LS_WARNING) << "XRandr is not supported or is too old (pre 1.3).";
+    return false;
+  }
+#endif
+  return true;
+}
+
+}  // namespace testing
+}  // namespace webrtc
+
+#endif  // WEBRTC_BASE_TESTUTILS_H__
diff --git a/base/thread.cc b/base/thread.cc
new file mode 100644
index 0000000..9174cd1
--- /dev/null
+++ b/base/thread.cc
@@ -0,0 +1,558 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/thread.h"
+
+#if defined(WEBRTC_WIN)
+#include <comdef.h>
+#elif defined(WEBRTC_POSIX)
+#include <time.h>
+#endif
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/nullsocketserver.h"
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/base/trace_event.h"
+
+namespace rtc {
+
+ThreadManager* ThreadManager::Instance() {
+  RTC_DEFINE_STATIC_LOCAL(ThreadManager, thread_manager, ());
+  return &thread_manager;
+}
+
+ThreadManager::~ThreadManager() {
+  // By above RTC_DEFINE_STATIC_LOCAL.
+  RTC_NOTREACHED() << "ThreadManager should never be destructed.";
+}
+
+// static
+Thread* Thread::Current() {
+  ThreadManager* manager = ThreadManager::Instance();
+  Thread* thread = manager->CurrentThread();
+
+#ifndef NO_MAIN_THREAD_WRAPPING
+  // Only autowrap the thread which instantiated the ThreadManager.
+  if (!thread && manager->IsMainThread()) {
+    thread = new Thread();
+    thread->WrapCurrentWithThreadManager(manager, true);
+  }
+#endif
+
+  return thread;
+}
+
+#if defined(WEBRTC_POSIX)
+#if !defined(WEBRTC_MAC)
+ThreadManager::ThreadManager() {
+  main_thread_ref_ = CurrentThreadRef();
+  pthread_key_create(&key_, nullptr);
+}
+#endif
+
+Thread *ThreadManager::CurrentThread() {
+  return static_cast<Thread *>(pthread_getspecific(key_));
+}
+
+void ThreadManager::SetCurrentThread(Thread *thread) {
+  pthread_setspecific(key_, thread);
+}
+#endif
+
+#if defined(WEBRTC_WIN)
+ThreadManager::ThreadManager() {
+  main_thread_ref_ = CurrentThreadRef();
+  key_ = TlsAlloc();
+}
+
+Thread *ThreadManager::CurrentThread() {
+  return static_cast<Thread *>(TlsGetValue(key_));
+}
+
+void ThreadManager::SetCurrentThread(Thread *thread) {
+  TlsSetValue(key_, thread);
+}
+#endif
+
+Thread *ThreadManager::WrapCurrentThread() {
+  Thread* result = CurrentThread();
+  if (nullptr == result) {
+    result = new Thread();
+    result->WrapCurrentWithThreadManager(this, true);
+  }
+  return result;
+}
+
+void ThreadManager::UnwrapCurrentThread() {
+  Thread* t = CurrentThread();
+  if (t && !(t->IsOwned())) {
+    t->UnwrapCurrent();
+    delete t;
+  }
+}
+
+bool ThreadManager::IsMainThread() {
+  return IsThreadRefEqual(CurrentThreadRef(), main_thread_ref_);
+}
+
+Thread::ScopedDisallowBlockingCalls::ScopedDisallowBlockingCalls()
+  : thread_(Thread::Current()),
+    previous_state_(thread_->SetAllowBlockingCalls(false)) {
+}
+
+Thread::ScopedDisallowBlockingCalls::~ScopedDisallowBlockingCalls() {
+  RTC_DCHECK(thread_->IsCurrent());
+  thread_->SetAllowBlockingCalls(previous_state_);
+}
+
+Thread::Thread() : Thread(SocketServer::CreateDefault()) {}
+
+Thread::Thread(SocketServer* ss)
+    : MessageQueue(ss, false),
+      running_(true, false),
+#if defined(WEBRTC_WIN)
+      thread_(nullptr),
+      thread_id_(0),
+#endif
+      owned_(true),
+      blocking_calls_allowed_(true) {
+  SetName("Thread", this);  // default name
+  DoInit();
+}
+
+Thread::Thread(std::unique_ptr<SocketServer> ss)
+    : MessageQueue(std::move(ss), false),
+      running_(true, false),
+#if defined(WEBRTC_WIN)
+      thread_(nullptr),
+      thread_id_(0),
+#endif
+      owned_(true),
+      blocking_calls_allowed_(true) {
+  SetName("Thread", this);  // default name
+  DoInit();
+}
+
+Thread::~Thread() {
+  Stop();
+  DoDestroy();
+}
+
+bool Thread::IsCurrent() const {
+  return ThreadManager::Instance()->CurrentThread() == this;
+}
+
+std::unique_ptr<Thread> Thread::CreateWithSocketServer() {
+  return std::unique_ptr<Thread>(new Thread(SocketServer::CreateDefault()));
+}
+
+std::unique_ptr<Thread> Thread::Create() {
+  return std::unique_ptr<Thread>(
+      new Thread(std::unique_ptr<SocketServer>(new NullSocketServer())));
+}
+
+bool Thread::SleepMs(int milliseconds) {
+  AssertBlockingIsAllowedOnCurrentThread();
+
+#if defined(WEBRTC_WIN)
+  ::Sleep(milliseconds);
+  return true;
+#else
+  // POSIX has both a usleep() and a nanosleep(), but the former is deprecated,
+  // so we use nanosleep() even though it has greater precision than necessary.
+  struct timespec ts;
+  ts.tv_sec = milliseconds / 1000;
+  ts.tv_nsec = (milliseconds % 1000) * 1000000;
+  int ret = nanosleep(&ts, nullptr);
+  if (ret != 0) {
+    LOG_ERR(LS_WARNING) << "nanosleep() returning early";
+    return false;
+  }
+  return true;
+#endif
+}
+
+bool Thread::SetName(const std::string& name, const void* obj) {
+  if (running()) return false;
+  name_ = name;
+  if (obj) {
+    char buf[16];
+    sprintfn(buf, sizeof(buf), " 0x%p", obj);
+    name_ += buf;
+  }
+  return true;
+}
+
+bool Thread::Start(Runnable* runnable) {
+  RTC_DCHECK(owned_);
+  if (!owned_) return false;
+  RTC_DCHECK(!running());
+  if (running()) return false;
+
+  Restart();  // reset IsQuitting() if the thread is being restarted
+
+  // Make sure that ThreadManager is created on the main thread before
+  // we start a new thread.
+  ThreadManager::Instance();
+
+  ThreadInit* init = new ThreadInit;
+  init->thread = this;
+  init->runnable = runnable;
+#if defined(WEBRTC_WIN)
+  thread_ = CreateThread(nullptr, 0, PreRun, init, 0, &thread_id_);
+  if (thread_) {
+    running_.Set();
+  } else {
+    return false;
+  }
+#elif defined(WEBRTC_POSIX)
+  pthread_attr_t attr;
+  pthread_attr_init(&attr);
+
+  int error_code = pthread_create(&thread_, &attr, PreRun, init);
+  if (0 != error_code) {
+    LOG(LS_ERROR) << "Unable to create pthread, error " << error_code;
+    return false;
+  }
+  running_.Set();
+#endif
+  return true;
+}
+
+bool Thread::WrapCurrent() {
+  return WrapCurrentWithThreadManager(ThreadManager::Instance(), true);
+}
+
+void Thread::UnwrapCurrent() {
+  // Clears the platform-specific thread-specific storage.
+  ThreadManager::Instance()->SetCurrentThread(nullptr);
+#if defined(WEBRTC_WIN)
+  if (thread_ != nullptr) {
+    if (!CloseHandle(thread_)) {
+      LOG_GLE(LS_ERROR) << "When unwrapping thread, failed to close handle.";
+    }
+    thread_ = nullptr;
+  }
+#endif
+  running_.Reset();
+}
+
+void Thread::SafeWrapCurrent() {
+  WrapCurrentWithThreadManager(ThreadManager::Instance(), false);
+}
+
+void Thread::Join() {
+  if (running()) {
+    RTC_DCHECK(!IsCurrent());
+    if (Current() && !Current()->blocking_calls_allowed_) {
+      LOG(LS_WARNING) << "Waiting for the thread to join, "
+                      << "but blocking calls have been disallowed";
+    }
+
+#if defined(WEBRTC_WIN)
+    RTC_DCHECK(thread_ != nullptr);
+    WaitForSingleObject(thread_, INFINITE);
+    CloseHandle(thread_);
+    thread_ = nullptr;
+    thread_id_ = 0;
+#elif defined(WEBRTC_POSIX)
+    void *pv;
+    pthread_join(thread_, &pv);
+#endif
+    running_.Reset();
+  }
+}
+
+bool Thread::SetAllowBlockingCalls(bool allow) {
+  RTC_DCHECK(IsCurrent());
+  bool previous = blocking_calls_allowed_;
+  blocking_calls_allowed_ = allow;
+  return previous;
+}
+
+// static
+void Thread::AssertBlockingIsAllowedOnCurrentThread() {
+#if !defined(NDEBUG)
+  Thread* current = Thread::Current();
+  RTC_DCHECK(!current || current->blocking_calls_allowed_);
+#endif
+}
+
+// static
+#if !defined(WEBRTC_MAC)
+#if defined(WEBRTC_WIN)
+DWORD WINAPI Thread::PreRun(LPVOID pv) {
+#else
+void* Thread::PreRun(void* pv) {
+#endif
+  ThreadInit* init = static_cast<ThreadInit*>(pv);
+  ThreadManager::Instance()->SetCurrentThread(init->thread);
+  rtc::SetCurrentThreadName(init->thread->name_.c_str());
+  if (init->runnable) {
+    init->runnable->Run(init->thread);
+  } else {
+    init->thread->Run();
+  }
+  delete init;
+#ifdef WEBRTC_WIN
+  return 0;
+#else
+  return nullptr;
+#endif
+}
+#endif
+
+void Thread::Run() {
+  ProcessMessages(kForever);
+}
+
+bool Thread::IsOwned() {
+  return owned_;
+}
+
+void Thread::Stop() {
+  MessageQueue::Quit();
+  Join();
+}
+
+void Thread::Send(const Location& posted_from,
+                  MessageHandler* phandler,
+                  uint32_t id,
+                  MessageData* pdata) {
+  if (IsQuitting())
+    return;
+
+  // Sent messages are sent to the MessageHandler directly, in the context
+  // of "thread", like Win32 SendMessage. If in the right context,
+  // call the handler directly.
+  Message msg;
+  msg.posted_from = posted_from;
+  msg.phandler = phandler;
+  msg.message_id = id;
+  msg.pdata = pdata;
+  if (IsCurrent()) {
+    phandler->OnMessage(&msg);
+    return;
+  }
+
+  AssertBlockingIsAllowedOnCurrentThread();
+
+  AutoThread thread;
+  Thread *current_thread = Thread::Current();
+  RTC_DCHECK(current_thread != nullptr);  // AutoThread ensures this
+
+  bool ready = false;
+  {
+    CritScope cs(&crit_);
+    _SendMessage smsg;
+    smsg.thread = current_thread;
+    smsg.msg = msg;
+    smsg.ready = &ready;
+    sendlist_.push_back(smsg);
+  }
+
+  // Wait for a reply
+  WakeUpSocketServer();
+
+  bool waited = false;
+  crit_.Enter();
+  while (!ready) {
+    crit_.Leave();
+    // We need to limit "ReceiveSends" to |this| thread to avoid an arbitrary
+    // thread invoking calls on the current thread.
+    current_thread->ReceiveSendsFromThread(this);
+    current_thread->socketserver()->Wait(kForever, false);
+    waited = true;
+    crit_.Enter();
+  }
+  crit_.Leave();
+
+  // Our Wait loop above may have consumed some WakeUp events for this
+  // MessageQueue, that weren't relevant to this Send.  Losing these WakeUps can
+  // cause problems for some SocketServers.
+  //
+  // Concrete example:
+  // Win32SocketServer on thread A calls Send on thread B.  While processing the
+  // message, thread B Posts a message to A.  We consume the wakeup for that
+  // Post while waiting for the Send to complete, which means that when we exit
+  // this loop, we need to issue another WakeUp, or else the Posted message
+  // won't be processed in a timely manner.
+
+  if (waited) {
+    current_thread->socketserver()->WakeUp();
+  }
+}
+
+void Thread::ReceiveSends() {
+  ReceiveSendsFromThread(nullptr);
+}
+
+void Thread::ReceiveSendsFromThread(const Thread* source) {
+  // Receive a sent message. Cleanup scenarios:
+  // - thread sending exits: We don't allow this, since thread can exit
+  //   only via Join, so Send must complete.
+  // - thread receiving exits: Wakeup/set ready in Thread::Clear()
+  // - object target cleared: Wakeup/set ready in Thread::Clear()
+  _SendMessage smsg;
+
+  crit_.Enter();
+  while (PopSendMessageFromThread(source, &smsg)) {
+    crit_.Leave();
+
+    smsg.msg.phandler->OnMessage(&smsg.msg);
+
+    crit_.Enter();
+    *smsg.ready = true;
+    smsg.thread->socketserver()->WakeUp();
+  }
+  crit_.Leave();
+}
+
+bool Thread::PopSendMessageFromThread(const Thread* source, _SendMessage* msg) {
+  for (std::list<_SendMessage>::iterator it = sendlist_.begin();
+       it != sendlist_.end(); ++it) {
+    if (it->thread == source || source == nullptr) {
+      *msg = *it;
+      sendlist_.erase(it);
+      return true;
+    }
+  }
+  return false;
+}
+
+void Thread::InvokeInternal(const Location& posted_from,
+                            MessageHandler* handler) {
+  TRACE_EVENT2("webrtc", "Thread::Invoke", "src_file_and_line",
+               posted_from.file_and_line(), "src_func",
+               posted_from.function_name());
+  Send(posted_from, handler);
+}
+
+void Thread::Clear(MessageHandler* phandler,
+                   uint32_t id,
+                   MessageList* removed) {
+  CritScope cs(&crit_);
+
+  // Remove messages on sendlist_ with phandler
+  // Object target cleared: remove from send list, wakeup/set ready
+  // if sender not null.
+
+  std::list<_SendMessage>::iterator iter = sendlist_.begin();
+  while (iter != sendlist_.end()) {
+    _SendMessage smsg = *iter;
+    if (smsg.msg.Match(phandler, id)) {
+      if (removed) {
+        removed->push_back(smsg.msg);
+      } else {
+        delete smsg.msg.pdata;
+      }
+      iter = sendlist_.erase(iter);
+      *smsg.ready = true;
+      smsg.thread->socketserver()->WakeUp();
+      continue;
+    }
+    ++iter;
+  }
+
+  MessageQueue::Clear(phandler, id, removed);
+}
+
+#if !defined(WEBRTC_MAC)
+// Note that these methods have a separate implementation for mac and ios
+// defined in webrtc/base/thread_darwin.mm.
+bool Thread::ProcessMessages(int cmsLoop) {
+  // Using ProcessMessages with a custom clock for testing and a time greater
+  // than 0 doesn't work, since it's not guaranteed to advance the custom
+  // clock's time, and may get stuck in an infinite loop.
+  RTC_DCHECK(GetClockForTesting() == nullptr || cmsLoop == 0 ||
+             cmsLoop == kForever);
+  int64_t msEnd = (kForever == cmsLoop) ? 0 : TimeAfter(cmsLoop);
+  int cmsNext = cmsLoop;
+
+  while (true) {
+    Message msg;
+    if (!Get(&msg, cmsNext))
+      return !IsQuitting();
+    Dispatch(&msg);
+
+    if (cmsLoop != kForever) {
+      cmsNext = static_cast<int>(TimeUntil(msEnd));
+      if (cmsNext < 0)
+        return true;
+    }
+  }
+}
+#endif
+
+bool Thread::WrapCurrentWithThreadManager(ThreadManager* thread_manager,
+                                          bool need_synchronize_access) {
+  if (running())
+    return false;
+
+#if defined(WEBRTC_WIN)
+  if (need_synchronize_access) {
+    // We explicitly ask for no rights other than synchronization.
+    // This gives us the best chance of succeeding.
+    thread_ = OpenThread(SYNCHRONIZE, FALSE, GetCurrentThreadId());
+    if (!thread_) {
+      LOG_GLE(LS_ERROR) << "Unable to get handle to thread.";
+      return false;
+    }
+    thread_id_ = GetCurrentThreadId();
+  }
+#elif defined(WEBRTC_POSIX)
+  thread_ = pthread_self();
+#endif
+
+  owned_ = false;
+  running_.Set();
+  thread_manager->SetCurrentThread(this);
+  return true;
+}
+
+AutoThread::AutoThread() {
+  if (!ThreadManager::Instance()->CurrentThread()) {
+    ThreadManager::Instance()->SetCurrentThread(this);
+  }
+}
+
+AutoThread::~AutoThread() {
+  Stop();
+  if (ThreadManager::Instance()->CurrentThread() == this) {
+    ThreadManager::Instance()->SetCurrentThread(nullptr);
+  }
+}
+
+AutoSocketServerThread::AutoSocketServerThread(SocketServer* ss)
+    : Thread(ss) {
+  old_thread_ = ThreadManager::Instance()->CurrentThread();
+  rtc::ThreadManager::Instance()->SetCurrentThread(this);
+  if (old_thread_) {
+    MessageQueueManager::Remove(old_thread_);
+  }
+}
+
+AutoSocketServerThread::~AutoSocketServerThread() {
+  RTC_DCHECK(ThreadManager::Instance()->CurrentThread() == this);
+  // Some tests post destroy messages to this thread. To avoid memory
+  // leaks, we have to process those messages. In particular
+  // P2PTransportChannelPingTest, relying on the message posted in
+  // cricket::Connection::Destroy.
+  ProcessMessages(0);
+  rtc::ThreadManager::Instance()->SetCurrentThread(old_thread_);
+  if (old_thread_) {
+    MessageQueueManager::Add(old_thread_);
+  }
+}
+
+}  // namespace rtc
diff --git a/base/thread.h b/base/thread.h
index 6a6887a..6e5da61 100644
--- a/base/thread.h
+++ b/base/thread.h
@@ -11,9 +11,322 @@
 #ifndef WEBRTC_BASE_THREAD_H_
 #define WEBRTC_BASE_THREAD_H_
 
+#include <algorithm>
+#include <list>
+#include <memory>
+#include <string>
+#include <vector>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/thread.h"
+#if defined(WEBRTC_POSIX)
+#include <pthread.h>
+#endif
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/messagequeue.h"
+#include "webrtc/base/platform_thread_types.h"
+
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/win32.h"
+#endif
+
+namespace rtc {
+
+class Thread;
+
+class ThreadManager {
+ public:
+  static const int kForever = -1;
+
+  // Singleton, constructor and destructor are private.
+  static ThreadManager* Instance();
+
+  Thread* CurrentThread();
+  void SetCurrentThread(Thread* thread);
+
+  // Returns a thread object with its thread_ ivar set
+  // to whatever the OS uses to represent the thread.
+  // If there already *is* a Thread object corresponding to this thread,
+  // this method will return that.  Otherwise it creates a new Thread
+  // object whose wrapped() method will return true, and whose
+  // handle will, on Win32, be opened with only synchronization privileges -
+  // if you need more privilegs, rather than changing this method, please
+  // write additional code to adjust the privileges, or call a different
+  // factory method of your own devising, because this one gets used in
+  // unexpected contexts (like inside browser plugins) and it would be a
+  // shame to break it.  It is also conceivable on Win32 that we won't even
+  // be able to get synchronization privileges, in which case the result
+  // will have a null handle.
+  Thread *WrapCurrentThread();
+  void UnwrapCurrentThread();
+
+  bool IsMainThread();
+
+ private:
+  ThreadManager();
+  ~ThreadManager();
+
+#if defined(WEBRTC_POSIX)
+  pthread_key_t key_;
+#endif
+
+#if defined(WEBRTC_WIN)
+  DWORD key_;
+#endif
+
+  // The thread to potentially autowrap.
+  PlatformThreadRef main_thread_ref_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(ThreadManager);
+};
+
+struct _SendMessage {
+  _SendMessage() {}
+  Thread *thread;
+  Message msg;
+  bool *ready;
+};
+
+class Runnable {
+ public:
+  virtual ~Runnable() {}
+  virtual void Run(Thread* thread) = 0;
+
+ protected:
+  Runnable() {}
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(Runnable);
+};
+
+// WARNING! SUBCLASSES MUST CALL Stop() IN THEIR DESTRUCTORS!  See ~Thread().
+
+class LOCKABLE Thread : public MessageQueue {
+ public:
+  // Create a new Thread and optionally assign it to the passed SocketServer.
+  Thread();
+  explicit Thread(SocketServer* ss);
+  explicit Thread(std::unique_ptr<SocketServer> ss);
+
+  // NOTE: ALL SUBCLASSES OF Thread MUST CALL Stop() IN THEIR DESTRUCTORS (or
+  // guarantee Stop() is explicitly called before the subclass is destroyed).
+  // This is required to avoid a data race between the destructor modifying the
+  // vtable, and the Thread::PreRun calling the virtual method Run().
+  ~Thread() override;
+
+  static std::unique_ptr<Thread> CreateWithSocketServer();
+  static std::unique_ptr<Thread> Create();
+  static Thread* Current();
+
+  // Used to catch performance regressions. Use this to disallow blocking calls
+  // (Invoke) for a given scope.  If a synchronous call is made while this is in
+  // effect, an assert will be triggered.
+  // Note that this is a single threaded class.
+  class ScopedDisallowBlockingCalls {
+   public:
+    ScopedDisallowBlockingCalls();
+    ~ScopedDisallowBlockingCalls();
+   private:
+    Thread* const thread_;
+    const bool previous_state_;
+  };
+
+  bool IsCurrent() const;
+
+  // Sleeps the calling thread for the specified number of milliseconds, during
+  // which time no processing is performed. Returns false if sleeping was
+  // interrupted by a signal (POSIX only).
+  static bool SleepMs(int millis);
+
+  // Sets the thread's name, for debugging. Must be called before Start().
+  // If |obj| is non-null, its value is appended to |name|.
+  const std::string& name() const { return name_; }
+  bool SetName(const std::string& name, const void* obj);
+
+  // Starts the execution of the thread.
+  bool Start(Runnable* runnable = nullptr);
+
+  // Tells the thread to stop and waits until it is joined.
+  // Never call Stop on the current thread.  Instead use the inherited Quit
+  // function which will exit the base MessageQueue without terminating the
+  // underlying OS thread.
+  virtual void Stop();
+
+  // By default, Thread::Run() calls ProcessMessages(kForever).  To do other
+  // work, override Run().  To receive and dispatch messages, call
+  // ProcessMessages occasionally.
+  virtual void Run();
+
+  virtual void Send(const Location& posted_from,
+                    MessageHandler* phandler,
+                    uint32_t id = 0,
+                    MessageData* pdata = nullptr);
+
+  // Convenience method to invoke a functor on another thread.  Caller must
+  // provide the |ReturnT| template argument, which cannot (easily) be deduced.
+  // Uses Send() internally, which blocks the current thread until execution
+  // is complete.
+  // Ex: bool result = thread.Invoke<bool>(RTC_FROM_HERE,
+  // &MyFunctionReturningBool);
+  // NOTE: This function can only be called when synchronous calls are allowed.
+  // See ScopedDisallowBlockingCalls for details.
+  template <class ReturnT, class FunctorT>
+  ReturnT Invoke(const Location& posted_from, const FunctorT& functor) {
+    FunctorMessageHandler<ReturnT, FunctorT> handler(functor);
+    InvokeInternal(posted_from, &handler);
+    return handler.MoveResult();
+  }
+
+  // From MessageQueue
+  void Clear(MessageHandler* phandler,
+             uint32_t id = MQID_ANY,
+             MessageList* removed = nullptr) override;
+  void ReceiveSends() override;
+
+  // ProcessMessages will process I/O and dispatch messages until:
+  //  1) cms milliseconds have elapsed (returns true)
+  //  2) Stop() is called (returns false)
+  bool ProcessMessages(int cms);
+
+  // Returns true if this is a thread that we created using the standard
+  // constructor, false if it was created by a call to
+  // ThreadManager::WrapCurrentThread().  The main thread of an application
+  // is generally not owned, since the OS representation of the thread
+  // obviously exists before we can get to it.
+  // You cannot call Start on non-owned threads.
+  bool IsOwned();
+
+#if defined(WEBRTC_WIN)
+  HANDLE GetHandle() const {
+    return thread_;
+  }
+  DWORD GetId() const {
+    return thread_id_;
+  }
+#elif defined(WEBRTC_POSIX)
+  pthread_t GetPThread() {
+    return thread_;
+  }
+#endif
+
+  // Expose private method running() for tests.
+  //
+  // DANGER: this is a terrible public API.  Most callers that might want to
+  // call this likely do not have enough control/knowledge of the Thread in
+  // question to guarantee that the returned value remains true for the duration
+  // of whatever code is conditionally executing because of the return value!
+  bool RunningForTest() { return running(); }
+
+  // Sets the per-thread allow-blocking-calls flag and returns the previous
+  // value. Must be called on this thread.
+  bool SetAllowBlockingCalls(bool allow);
+
+  // These functions are public to avoid injecting test hooks. Don't call them
+  // outside of tests.
+  // This method should be called when thread is created using non standard
+  // method, like derived implementation of rtc::Thread and it can not be
+  // started by calling Start(). This will set started flag to true and
+  // owned to false. This must be called from the current thread.
+  bool WrapCurrent();
+  void UnwrapCurrent();
+
+ protected:
+  // Same as WrapCurrent except that it never fails as it does not try to
+  // acquire the synchronization access of the thread. The caller should never
+  // call Stop() or Join() on this thread.
+  void SafeWrapCurrent();
+
+  // Blocks the calling thread until this thread has terminated.
+  void Join();
+
+  static void AssertBlockingIsAllowedOnCurrentThread();
+
+  friend class ScopedDisallowBlockingCalls;
+
+ private:
+  struct ThreadInit {
+    Thread* thread;
+    Runnable* runnable;
+  };
+
+#if defined(WEBRTC_WIN)
+  static DWORD WINAPI PreRun(LPVOID context);
+#else
+  static void *PreRun(void *pv);
+#endif
+
+  // ThreadManager calls this instead WrapCurrent() because
+  // ThreadManager::Instance() cannot be used while ThreadManager is
+  // being created.
+  // The method tries to get synchronization rights of the thread on Windows if
+  // |need_synchronize_access| is true.
+  bool WrapCurrentWithThreadManager(ThreadManager* thread_manager,
+                                    bool need_synchronize_access);
+
+  // Return true if the thread was started and hasn't yet stopped.
+  bool running() { return running_.Wait(0); }
+
+  // Processes received "Send" requests. If |source| is not null, only requests
+  // from |source| are processed, otherwise, all requests are processed.
+  void ReceiveSendsFromThread(const Thread* source);
+
+  // If |source| is not null, pops the first "Send" message from |source| in
+  // |sendlist_|, otherwise, pops the first "Send" message of |sendlist_|.
+  // The caller must lock |crit_| before calling.
+  // Returns true if there is such a message.
+  bool PopSendMessageFromThread(const Thread* source, _SendMessage* msg);
+
+  void InvokeInternal(const Location& posted_from, MessageHandler* handler);
+
+  std::list<_SendMessage> sendlist_;
+  std::string name_;
+  Event running_;  // Signalled means running.
+
+#if defined(WEBRTC_POSIX)
+  pthread_t thread_;
+#endif
+
+#if defined(WEBRTC_WIN)
+  HANDLE thread_;
+  DWORD thread_id_;
+#endif
+
+  bool owned_;
+  bool blocking_calls_allowed_;  // By default set to |true|.
+
+  friend class ThreadManager;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(Thread);
+};
+
+// AutoThread automatically installs itself at construction
+// uninstalls at destruction, if a Thread object is
+// _not already_ associated with the current OS thread.
+
+class AutoThread : public Thread {
+ public:
+  AutoThread();
+  ~AutoThread() override;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(AutoThread);
+};
+
+// AutoSocketServerThread automatically installs itself at
+// construction and uninstalls at destruction. If a Thread object is
+// already associated with the current OS thread, it is temporarily
+// disassociated and restored by the destructor.
+
+class AutoSocketServerThread : public Thread {
+ public:
+  explicit AutoSocketServerThread(SocketServer* ss);
+  ~AutoSocketServerThread() override;
+
+ private:
+  rtc::Thread* old_thread_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(AutoSocketServerThread);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_THREAD_H_
diff --git a/base/thread_annotations.h b/base/thread_annotations.h
index 5b94ffe..8d5abbd 100644
--- a/base/thread_annotations.h
+++ b/base/thread_annotations.h
@@ -19,9 +19,82 @@
 #ifndef WEBRTC_BASE_THREAD_ANNOTATIONS_H_
 #define WEBRTC_BASE_THREAD_ANNOTATIONS_H_
 
+#if defined(__clang__) && (!defined(SWIG))
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x)  // no-op
+#endif
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/thread_annotations.h"
+// Document if a shared variable/field needs to be protected by a lock.
+// GUARDED_BY allows the user to specify a particular lock that should be
+// held when accessing the annotated variable, while GUARDED_VAR only
+// indicates a shared variable should be guarded (by any lock). GUARDED_VAR
+// is primarily used when the client cannot express the name of the lock.
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded_var)
+
+// Document if the memory location pointed to by a pointer should be guarded
+// by a lock when dereferencing the pointer. Similar to GUARDED_VAR,
+// PT_GUARDED_VAR is primarily used when the client cannot express the name
+// of the lock. Note that a pointer variable to a shared memory location
+// could itself be a shared variable. For example, if a shared global pointer
+// q, which is guarded by mu1, points to a shared memory location that is
+// guarded by mu2, q should be annotated as follows:
+//     int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+#define PT_GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_var)
+
+// Document the acquisition order between locks that can be held
+// simultaneously by a thread. For any two locks that need to be annotated
+// to establish an acquisition order, only one of them needs the annotation.
+// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
+// and ACQUIRED_BEFORE.)
+#define ACQUIRED_AFTER(x) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x))
+#define ACQUIRED_BEFORE(x) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x))
+
+// The following three annotations document the lock requirements for
+// functions/methods.
+
+// Document if a function expects certain locks to be held before it is called
+#define EXCLUSIVE_LOCKS_REQUIRED(...) \
+  THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
+
+#define SHARED_LOCKS_REQUIRED(...) \
+  THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
+
+// Document the locks acquired in the body of the function. These locks
+// cannot be held when calling this function (as google3's Mutex locks are
+// non-reentrant).
+#define LOCKS_EXCLUDED(...) \
+  THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+// Document the lock the annotated function returns without acquiring it.
+#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+// Document if a class/type is a lockable type (such as the Mutex class).
+#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable)
+
+// Document if a class is a scoped lockable type (such as the MutexLock class).
+#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+// The following annotations specify lock and unlock primitives.
+#define EXCLUSIVE_LOCK_FUNCTION(...) \
+  THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
+
+#define SHARED_LOCK_FUNCTION(...) \
+  THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
+
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
+  THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
+
+#define SHARED_TRYLOCK_FUNCTION(...) \
+  THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
+
+#define UNLOCK_FUNCTION(...) \
+  THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
+
+// An escape hatch for thread safety analysis to ignore the annotated function.
+#define NO_THREAD_SAFETY_ANALYSIS \
+  THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
 
 #endif  // WEBRTC_BASE_THREAD_ANNOTATIONS_H_
diff --git a/base/thread_annotations_unittest.cc b/base/thread_annotations_unittest.cc
new file mode 100644
index 0000000..69938ef
--- /dev/null
+++ b/base/thread_annotations_unittest.cc
@@ -0,0 +1,151 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/thread_annotations.h"
+#include "webrtc/test/gtest.h"
+
+namespace {
+
+class LOCKABLE Lock {
+ public:
+  void EnterWrite() const EXCLUSIVE_LOCK_FUNCTION() {}
+  void EnterRead() const SHARED_LOCK_FUNCTION() {}
+  bool TryEnterWrite() const EXCLUSIVE_TRYLOCK_FUNCTION(true) { return true; }
+  bool TryEnterRead() const SHARED_TRYLOCK_FUNCTION(true) { return true; }
+  void Leave() const UNLOCK_FUNCTION() {}
+};
+
+class SCOPED_LOCKABLE ScopeLock {
+ public:
+  explicit ScopeLock(const Lock& lock) EXCLUSIVE_LOCK_FUNCTION(lock) {}
+  ~ScopeLock() UNLOCK_FUNCTION() {}
+};
+
+class ThreadSafe {
+ public:
+  ThreadSafe() {
+    pt_protected_by_lock_ = new int;
+    pt_protected_by_anything_ = new int;
+  }
+
+  ~ThreadSafe() {
+    delete pt_protected_by_lock_;
+    delete pt_protected_by_anything_;
+  }
+
+  void LockInOrder() {
+    anylock_.EnterWrite();
+    lock_.EnterWrite();
+    pt_lock_.EnterWrite();
+
+    pt_lock_.Leave();
+    lock_.Leave();
+    anylock_.Leave();
+  }
+
+  void UnprotectedFunction() LOCKS_EXCLUDED(anylock_, lock_, pt_lock_) {
+    // Can access unprotected Value.
+    unprotected_ = 15;
+    // Can access pointers themself, but not data they point to.
+    int* tmp = pt_protected_by_lock_;
+    pt_protected_by_lock_ = pt_protected_by_anything_;
+    pt_protected_by_anything_ = tmp;
+  }
+
+  void ReadProtected() {
+    lock_.EnterRead();
+    unprotected_ = protected_by_anything_;
+    unprotected_ = protected_by_lock_;
+    lock_.Leave();
+
+    if (pt_lock_.TryEnterRead()) {
+      unprotected_ = *pt_protected_by_anything_;
+      unprotected_ = *pt_protected_by_lock_;
+      pt_lock_.Leave();
+    }
+
+    anylock_.EnterRead();
+    unprotected_ = protected_by_anything_;
+    unprotected_ = *pt_protected_by_anything_;
+    anylock_.Leave();
+  }
+
+  void WriteProtected() {
+    lock_.EnterWrite();
+    protected_by_anything_ = unprotected_;
+    protected_by_lock_ = unprotected_;
+    lock_.Leave();
+
+    if (pt_lock_.TryEnterWrite()) {
+      *pt_protected_by_anything_ = unprotected_;
+      *pt_protected_by_lock_ = unprotected_;
+      pt_lock_.Leave();
+    }
+
+    anylock_.EnterWrite();
+    protected_by_anything_ = unprotected_;
+    *pt_protected_by_anything_ = unprotected_;
+    anylock_.Leave();
+  }
+
+  void CallReadProtectedFunction() {
+    lock_.EnterRead();
+    pt_lock_.EnterRead();
+    ReadProtectedFunction();
+    pt_lock_.Leave();
+    lock_.Leave();
+  }
+
+  void CallWriteProtectedFunction() {
+    ScopeLock scope_lock(GetLock());
+    ScopeLock pt_scope_lock(pt_lock_);
+    WriteProtectedFunction();
+  }
+
+ private:
+  void ReadProtectedFunction() SHARED_LOCKS_REQUIRED(lock_, pt_lock_) {
+    unprotected_ = protected_by_lock_;
+    unprotected_ = *pt_protected_by_lock_;
+  }
+
+  void WriteProtectedFunction() EXCLUSIVE_LOCKS_REQUIRED(lock_, pt_lock_) {
+    int x = protected_by_lock_;
+    *pt_protected_by_lock_ = x;
+    protected_by_lock_ = unprotected_;
+  }
+
+  const Lock& GetLock() LOCK_RETURNED(lock_) { return lock_; }
+
+  Lock anylock_ ACQUIRED_BEFORE(lock_);
+  Lock lock_;
+  Lock pt_lock_ ACQUIRED_AFTER(lock_);
+
+  int unprotected_ = 0;
+
+  int protected_by_lock_ GUARDED_BY(lock_) = 0;
+  int protected_by_anything_ GUARDED_VAR = 0;
+
+  int* pt_protected_by_lock_ PT_GUARDED_BY(pt_lock_);
+  int* pt_protected_by_anything_ PT_GUARDED_VAR;
+};
+
+}  // namespace
+
+TEST(ThreadAnnotationsTest, Test) {
+  // This test ensure thread annotations doesn't break compilation.
+  // Thus no run-time expectations.
+  ThreadSafe t;
+  t.LockInOrder();
+  t.UnprotectedFunction();
+  t.ReadProtected();
+  t.WriteProtected();
+  t.CallReadProtectedFunction();
+  t.CallWriteProtectedFunction();
+}
diff --git a/base/thread_checker.h b/base/thread_checker.h
index ade5256..5914282 100644
--- a/base/thread_checker.h
+++ b/base/thread_checker.h
@@ -13,9 +13,166 @@
 #ifndef WEBRTC_BASE_THREAD_CHECKER_H_
 #define WEBRTC_BASE_THREAD_CHECKER_H_
 
+// Apart from debug builds, we also enable the thread checker in
+// builds with RTC_DCHECK_IS_ON so that trybots and waterfall bots
+// with this define will get the same level of thread checking as
+// debug bots.
+#define ENABLE_THREAD_CHECKER RTC_DCHECK_IS_ON
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/thread_checker.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/thread_annotations.h"
+#include "webrtc/base/thread_checker_impl.h"
+
+namespace rtc {
+
+// Do nothing implementation, for use in release mode.
+//
+// Note: You should almost always use the ThreadChecker class to get the
+// right version for your build configuration.
+class ThreadCheckerDoNothing {
+ public:
+  bool CalledOnValidThread() const {
+    return true;
+  }
+
+  void DetachFromThread() {}
+};
+
+// ThreadChecker is a helper class used to help verify that some methods of a
+// class are called from the same thread. It provides identical functionality to
+// base::NonThreadSafe, but it is meant to be held as a member variable, rather
+// than inherited from base::NonThreadSafe.
+//
+// While inheriting from base::NonThreadSafe may give a clear indication about
+// the thread-safety of a class, it may also lead to violations of the style
+// guide with regard to multiple inheritance. The choice between having a
+// ThreadChecker member and inheriting from base::NonThreadSafe should be based
+// on whether:
+//  - Derived classes need to know the thread they belong to, as opposed to
+//    having that functionality fully encapsulated in the base class.
+//  - Derived classes should be able to reassign the base class to another
+//    thread, via DetachFromThread.
+//
+// If neither of these are true, then having a ThreadChecker member and calling
+// CalledOnValidThread is the preferable solution.
+//
+// Example:
+// class MyClass {
+//  public:
+//   void Foo() {
+//     RTC_DCHECK(thread_checker_.CalledOnValidThread());
+//     ... (do stuff) ...
+//   }
+//
+//  private:
+//   ThreadChecker thread_checker_;
+// }
+//
+// In Release mode, CalledOnValidThread will always return true.
+#if ENABLE_THREAD_CHECKER
+class LOCKABLE ThreadChecker : public ThreadCheckerImpl {
+};
+#else
+class LOCKABLE ThreadChecker : public ThreadCheckerDoNothing {
+};
+#endif  // ENABLE_THREAD_CHECKER
+
+#undef ENABLE_THREAD_CHECKER
+
+namespace internal {
+class SCOPED_LOCKABLE AnnounceOnThread {
+ public:
+  template<typename ThreadLikeObject>
+  explicit AnnounceOnThread(const ThreadLikeObject* thread_like_object)
+      EXCLUSIVE_LOCK_FUNCTION(thread_like_object) {}
+  ~AnnounceOnThread() UNLOCK_FUNCTION() {}
+
+  template<typename ThreadLikeObject>
+  static bool IsCurrent(const ThreadLikeObject* thread_like_object) {
+    return thread_like_object->IsCurrent();
+  }
+  static bool IsCurrent(const rtc::ThreadChecker* checker) {
+    return checker->CalledOnValidThread();
+  }
+
+ private:
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(AnnounceOnThread);
+};
+
+}  // namespace internal
+}  // namespace rtc
+
+// RUN_ON/ACCESS_ON/RTC_DCHECK_RUN_ON macros allows to annotate variables are
+// accessed from same thread/task queue.
+// Using tools designed to check mutexes, it checks at compile time everywhere
+// variable is access, there is a run-time dcheck thread/task queue is correct.
+//
+// class ExampleThread {
+//  public:
+//   void NeedVar1() {
+//     RTC_DCHECK_RUN_ON(network_thread_);
+//     transport_->Send();
+//   }
+//
+//  private:
+//   rtc::Thread* network_thread_;
+//   int transport_ ACCESS_ON(network_thread_);
+// };
+//
+// class ExampleThreadChecker {
+//  public:
+//   int CalledFromPacer() RUN_ON(pacer_thread_checker_) {
+//     return var2_;
+//   }
+//
+//   void CallMeFromPacer() {
+//     RTC_DCHECK_RUN_ON(&pacer_thread_checker_)
+//        << "Should be called from pacer";
+//     CalledFromPacer();
+//   }
+//
+//  private:
+//   int pacer_var_ ACCESS_ON(pacer_thread_checker_);
+//   rtc::ThreadChecker pacer_thread_checker_;
+// };
+//
+// class TaskQueueExample {
+//  public:
+//   class Encoder {
+//    public:
+//     rtc::TaskQueue* Queue() { return encoder_queue_; }
+//     void Encode() {
+//       RTC_DCHECK_RUN_ON(encoder_queue_);
+//       DoSomething(var_);
+//     }
+//
+//    private:
+//     rtc::TaskQueue* const encoder_queue_;
+//     Frame var_ ACCESS_ON(encoder_queue_);
+//   };
+//
+//   void Encode() {
+//     // Will fail at runtime when DCHECK is enabled:
+//     // encoder_->Encode();
+//     // Will work:
+//     rtc::scoped_ref_ptr<Encoder> encoder = encoder_;
+//     encoder_->Queue()->PostTask([encoder] { encoder->Encode(); });
+//   }
+//
+//  private:
+//   rtc::scoped_ref_ptr<Encoder> encoder_;
+// }
+
+// Document if a variable/field is not shared and should be accessed from
+// same thread/task queue.
+#define ACCESS_ON(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+// Document if a function expected to be called from same thread/task queue.
+#define RUN_ON(x) THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(x))
+
+#define RTC_DCHECK_RUN_ON(thread_like_object) \
+  rtc::internal::AnnounceOnThread thread_announcer(thread_like_object); \
+  RTC_DCHECK(rtc::internal::AnnounceOnThread::IsCurrent(thread_like_object))
 
 #endif  // WEBRTC_BASE_THREAD_CHECKER_H_
diff --git a/base/thread_checker_impl.cc b/base/thread_checker_impl.cc
new file mode 100644
index 0000000..79be606
--- /dev/null
+++ b/base/thread_checker_impl.cc
@@ -0,0 +1,38 @@
+/*
+ *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Borrowed from Chromium's src/base/threading/thread_checker_impl.cc.
+
+#include "webrtc/base/thread_checker_impl.h"
+
+#include "webrtc/base/platform_thread.h"
+
+namespace rtc {
+
+ThreadCheckerImpl::ThreadCheckerImpl() : valid_thread_(CurrentThreadRef()) {
+}
+
+ThreadCheckerImpl::~ThreadCheckerImpl() {
+}
+
+bool ThreadCheckerImpl::CalledOnValidThread() const {
+  const PlatformThreadRef current_thread = CurrentThreadRef();
+  CritScope scoped_lock(&lock_);
+  if (!valid_thread_)  // Set if previously detached.
+    valid_thread_ = current_thread;
+  return IsThreadRefEqual(valid_thread_, current_thread);
+}
+
+void ThreadCheckerImpl::DetachFromThread() {
+  CritScope scoped_lock(&lock_);
+  valid_thread_ = 0;
+}
+
+}  // namespace rtc
diff --git a/base/thread_checker_impl.h b/base/thread_checker_impl.h
index 3a0a6c7..b9867c3 100644
--- a/base/thread_checker_impl.h
+++ b/base/thread_checker_impl.h
@@ -13,9 +13,36 @@
 #ifndef WEBRTC_BASE_THREAD_CHECKER_IMPL_H_
 #define WEBRTC_BASE_THREAD_CHECKER_IMPL_H_
 
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/platform_thread_types.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/thread_checker_impl.h"
+namespace rtc {
+
+// Real implementation of ThreadChecker, for use in debug mode, or
+// for temporary use in release mode (e.g. to RTC_CHECK on a threading issue
+// seen only in the wild).
+//
+// Note: You should almost always use the ThreadChecker class to get the
+// right version for your build configuration.
+class ThreadCheckerImpl {
+ public:
+  ThreadCheckerImpl();
+  ~ThreadCheckerImpl();
+
+  bool CalledOnValidThread() const;
+
+  // Changes the thread that is checked for in CalledOnValidThread.  This may
+  // be useful when an object may be created on one thread and then used
+  // exclusively on another thread.
+  void DetachFromThread();
+
+ private:
+  CriticalSection lock_;
+  // This is mutable so that CalledOnValidThread can set it.
+  // It's guarded by |lock_|.
+  mutable PlatformThreadRef valid_thread_;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_THREAD_CHECKER_IMPL_H_
diff --git a/base/thread_checker_unittest.cc b/base/thread_checker_unittest.cc
new file mode 100644
index 0000000..e995b50
--- /dev/null
+++ b/base/thread_checker_unittest.cc
@@ -0,0 +1,252 @@
+/*
+ *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Borrowed from Chromium's src/base/threading/thread_checker_unittest.cc.
+
+#include <memory>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/task_queue.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/thread_checker.h"
+#include "webrtc/test/gtest.h"
+
+// Duplicated from base/threading/thread_checker.h so that we can be
+// good citizens there and undef the macro.
+#define ENABLE_THREAD_CHECKER RTC_DCHECK_IS_ON
+
+namespace rtc {
+
+namespace {
+
+// Simple class to exercise the basics of ThreadChecker.
+// Both the destructor and DoStuff should verify that they were
+// called on the same thread as the constructor.
+class ThreadCheckerClass : public ThreadChecker {
+ public:
+  ThreadCheckerClass() {}
+
+  // Verifies that it was called on the same thread as the constructor.
+  void DoStuff() { RTC_DCHECK(CalledOnValidThread()); }
+
+  void DetachFromThread() {
+    ThreadChecker::DetachFromThread();
+  }
+
+  static void MethodOnDifferentThreadImpl();
+  static void DetachThenCallFromDifferentThreadImpl();
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
+};
+
+// Calls ThreadCheckerClass::DoStuff on another thread.
+class CallDoStuffOnThread : public Thread {
+ public:
+  explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
+      : Thread(),
+        thread_checker_class_(thread_checker_class) {
+    SetName("call_do_stuff_on_thread", nullptr);
+  }
+
+  void Run() override { thread_checker_class_->DoStuff(); }
+
+  // New method. Needed since Thread::Join is protected, and it is called by
+  // the TEST.
+  void Join() {
+    Thread::Join();
+  }
+
+ private:
+  ThreadCheckerClass* thread_checker_class_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
+};
+
+// Deletes ThreadCheckerClass on a different thread.
+class DeleteThreadCheckerClassOnThread : public Thread {
+ public:
+  explicit DeleteThreadCheckerClassOnThread(
+      ThreadCheckerClass* thread_checker_class)
+      : Thread(),
+        thread_checker_class_(thread_checker_class) {
+    SetName("delete_thread_checker_class_on_thread", nullptr);
+  }
+
+  void Run() override { thread_checker_class_.reset(); }
+
+  // New method. Needed since Thread::Join is protected, and it is called by
+  // the TEST.
+  void Join() {
+    Thread::Join();
+  }
+
+ private:
+  std::unique_ptr<ThreadCheckerClass> thread_checker_class_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
+};
+
+}  // namespace
+
+TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
+  std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+      new ThreadCheckerClass);
+
+  // Verify that DoStuff doesn't assert.
+  thread_checker_class->DoStuff();
+
+  // Verify that the destructor doesn't assert.
+  thread_checker_class.reset();
+}
+
+TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
+  std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+      new ThreadCheckerClass);
+
+  // Verify that the destructor doesn't assert
+  // when called on a different thread.
+  DeleteThreadCheckerClassOnThread delete_on_thread(
+      thread_checker_class.release());
+
+  delete_on_thread.Start();
+  delete_on_thread.Join();
+}
+
+TEST(ThreadCheckerTest, DetachFromThread) {
+  std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+      new ThreadCheckerClass);
+
+  // Verify that DoStuff doesn't assert when called on a different thread after
+  // a call to DetachFromThread.
+  thread_checker_class->DetachFromThread();
+  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
+
+  call_on_thread.Start();
+  call_on_thread.Join();
+}
+
+#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
+
+void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
+  std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+      new ThreadCheckerClass);
+
+  // DoStuff should assert in debug builds only when called on a
+  // different thread.
+  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
+
+  call_on_thread.Start();
+  call_on_thread.Join();
+}
+
+#if ENABLE_THREAD_CHECKER
+TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
+  ASSERT_DEATH({
+      ThreadCheckerClass::MethodOnDifferentThreadImpl();
+    }, "");
+}
+#else
+TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
+  ThreadCheckerClass::MethodOnDifferentThreadImpl();
+}
+#endif  // ENABLE_THREAD_CHECKER
+
+void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
+  std::unique_ptr<ThreadCheckerClass> thread_checker_class(
+      new ThreadCheckerClass);
+
+  // DoStuff doesn't assert when called on a different thread
+  // after a call to DetachFromThread.
+  thread_checker_class->DetachFromThread();
+  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
+
+  call_on_thread.Start();
+  call_on_thread.Join();
+
+  // DoStuff should assert in debug builds only after moving to
+  // another thread.
+  thread_checker_class->DoStuff();
+}
+
+#if ENABLE_THREAD_CHECKER
+TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
+  ASSERT_DEATH({
+    ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
+    }, "");
+}
+#else
+TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
+  ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
+}
+#endif  // ENABLE_THREAD_CHECKER
+
+#endif  // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
+
+class ThreadAnnotateTest {
+ public:
+  // Next two function should create warnings when compile (e.g. if used with
+  // specific T).
+  // TODO(danilchap): Find a way to test they do not compile when thread
+  // annotation checks enabled.
+  template<typename T>
+  void access_var_no_annotate() {
+    var_thread_ = 42;
+  }
+
+  template<typename T>
+  void access_fun_no_annotate() {
+    function();
+  }
+
+  // Functions below should be able to compile.
+  void access_var_annotate_thread() {
+    RTC_DCHECK_RUN_ON(thread_);
+    var_thread_ = 42;
+  }
+
+  void access_var_annotate_checker() {
+    RTC_DCHECK_RUN_ON(&checker_);
+    var_checker_ = 44;
+  }
+
+  void access_var_annotate_queue() {
+    RTC_DCHECK_RUN_ON(queue_);
+    var_queue_ = 46;
+  }
+
+  void access_fun_annotate() {
+    RTC_DCHECK_RUN_ON(thread_);
+    function();
+  }
+
+  void access_fun_and_var() {
+    RTC_DCHECK_RUN_ON(thread_);
+    fun_acccess_var();
+  }
+
+ private:
+  void function() RUN_ON(thread_) {}
+  void fun_acccess_var() RUN_ON(thread_) { var_thread_ = 13; }
+
+  rtc::Thread* thread_;
+  rtc::ThreadChecker checker_;
+  rtc::TaskQueue* queue_;
+
+  int var_thread_ ACCESS_ON(thread_);
+  int var_checker_ GUARDED_BY(checker_);
+  int var_queue_ ACCESS_ON(queue_);
+};
+
+// Just in case we ever get lumped together with other compilation units.
+#undef ENABLE_THREAD_CHECKER
+
+}  // namespace rtc
diff --git a/base/thread_darwin.mm b/base/thread_darwin.mm
new file mode 100644
index 0000000..5f2227d
--- /dev/null
+++ b/base/thread_darwin.mm
@@ -0,0 +1,84 @@
+/*
+ *  Copyright 2017 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/thread.h"
+
+#import <Foundation/Foundation.h>
+
+#include "webrtc/base/platform_thread.h"
+
+/*
+ * This file contains platform-specific implementations for several
+ * methods in rtc::Thread.
+ */
+
+namespace {
+void InitCocoaMultiThreading() {
+  if ([NSThread isMultiThreaded] == NO) {
+    // The sole purpose of this autorelease pool is to avoid a console
+    // message on Leopard that tells us we're autoreleasing the thread
+    // with no autorelease pool in place.
+    @autoreleasepool {
+      [NSThread detachNewThreadSelector:@selector(class)
+                               toTarget:[NSObject class]
+                             withObject:nil];
+    }
+  }
+
+  RTC_DCHECK([NSThread isMultiThreaded]);
+}
+}
+
+namespace rtc {
+
+ThreadManager::ThreadManager() {
+  main_thread_ref_ = CurrentThreadRef();
+  pthread_key_create(&key_, nullptr);
+  // This is necessary to alert the cocoa runtime of the fact that
+  // we are running in a multithreaded environment.
+  InitCocoaMultiThreading();
+}
+
+// static
+void* Thread::PreRun(void* pv) {
+  ThreadInit* init = static_cast<ThreadInit*>(pv);
+  ThreadManager::Instance()->SetCurrentThread(init->thread);
+  rtc::SetCurrentThreadName(init->thread->name_.c_str());
+  @autoreleasepool {
+    if (init->runnable) {
+      init->runnable->Run(init->thread);
+    } else {
+      init->thread->Run();
+    }
+  }
+  delete init;
+  return nullptr;
+}
+
+bool Thread::ProcessMessages(int cmsLoop) {
+  int64_t msEnd = (kForever == cmsLoop) ? 0 : TimeAfter(cmsLoop);
+  int cmsNext = cmsLoop;
+
+  while (true) {
+    @autoreleasepool {
+      Message msg;
+      if (!Get(&msg, cmsNext))
+        return !IsQuitting();
+      Dispatch(&msg);
+
+      if (cmsLoop != kForever) {
+        cmsNext = static_cast<int>(TimeUntil(msEnd));
+        if (cmsNext < 0)
+          return true;
+      }
+    }
+  }
+}
+}  // namespace rtc
diff --git a/base/thread_unittest.cc b/base/thread_unittest.cc
new file mode 100644
index 0000000..c143120
--- /dev/null
+++ b/base/thread_unittest.cc
@@ -0,0 +1,613 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "webrtc/base/asyncinvoker.h"
+#include "webrtc/base/asyncudpsocket.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/thread.h"
+
+#if defined(WEBRTC_WIN)
+#include <comdef.h>  // NOLINT
+#endif
+
+using namespace rtc;
+
+// Generates a sequence of numbers (collaboratively).
+class TestGenerator {
+ public:
+  TestGenerator() : last(0), count(0) {}
+
+  int Next(int prev) {
+    int result = prev + last;
+    last = result;
+    count += 1;
+    return result;
+  }
+
+  int last;
+  int count;
+};
+
+struct TestMessage : public MessageData {
+  explicit TestMessage(int v) : value(v) {}
+  virtual ~TestMessage() {}
+
+  int value;
+};
+
+// Receives on a socket and sends by posting messages.
+class SocketClient : public TestGenerator, public sigslot::has_slots<> {
+ public:
+  SocketClient(AsyncSocket* socket, const SocketAddress& addr,
+               Thread* post_thread, MessageHandler* phandler)
+      : socket_(AsyncUDPSocket::Create(socket, addr)),
+        post_thread_(post_thread),
+        post_handler_(phandler) {
+    socket_->SignalReadPacket.connect(this, &SocketClient::OnPacket);
+  }
+
+  ~SocketClient() {
+    delete socket_;
+  }
+
+  SocketAddress address() const { return socket_->GetLocalAddress(); }
+
+  void OnPacket(AsyncPacketSocket* socket, const char* buf, size_t size,
+                const SocketAddress& remote_addr,
+                const PacketTime& packet_time) {
+    EXPECT_EQ(size, sizeof(uint32_t));
+    uint32_t prev = reinterpret_cast<const uint32_t*>(buf)[0];
+    uint32_t result = Next(prev);
+
+    post_thread_->PostDelayed(RTC_FROM_HERE, 200, post_handler_, 0,
+                              new TestMessage(result));
+  }
+
+ private:
+  AsyncUDPSocket* socket_;
+  Thread* post_thread_;
+  MessageHandler* post_handler_;
+};
+
+// Receives messages and sends on a socket.
+class MessageClient : public MessageHandler, public TestGenerator {
+ public:
+  MessageClient(Thread* pth, Socket* socket)
+      : socket_(socket) {
+  }
+
+  virtual ~MessageClient() {
+    delete socket_;
+  }
+
+  virtual void OnMessage(Message *pmsg) {
+    TestMessage* msg = static_cast<TestMessage*>(pmsg->pdata);
+    int result = Next(msg->value);
+    EXPECT_GE(socket_->Send(&result, sizeof(result)), 0);
+    delete msg;
+  }
+
+ private:
+  Socket* socket_;
+};
+
+class CustomThread : public rtc::Thread {
+ public:
+  CustomThread() {}
+  virtual ~CustomThread() { Stop(); }
+  bool Start() { return false; }
+
+  bool WrapCurrent() {
+    return Thread::WrapCurrent();
+  }
+  void UnwrapCurrent() {
+    Thread::UnwrapCurrent();
+  }
+};
+
+
+// A thread that does nothing when it runs and signals an event
+// when it is destroyed.
+class SignalWhenDestroyedThread : public Thread {
+ public:
+  SignalWhenDestroyedThread(Event* event)
+      : event_(event) {
+  }
+
+  virtual ~SignalWhenDestroyedThread() {
+    Stop();
+    event_->Set();
+  }
+
+  virtual void Run() {
+    // Do nothing.
+  }
+
+ private:
+  Event* event_;
+};
+
+// A bool wrapped in a mutex, to avoid data races. Using a volatile
+// bool should be sufficient for correct code ("eventual consistency"
+// between caches is sufficient), but we can't tell the compiler about
+// that, and then tsan complains about a data race.
+
+// See also discussion at
+// http://stackoverflow.com/questions/7223164/is-mutex-needed-to-synchronize-a-simple-flag-between-pthreads
+
+// Using std::atomic<bool> or std::atomic_flag in C++11 is probably
+// the right thing to do, but those features are not yet allowed. Or
+// rtc::AtomicInt, if/when that is added. Since the use isn't
+// performance critical, use a plain critical section for the time
+// being.
+
+class AtomicBool {
+ public:
+  explicit AtomicBool(bool value = false) : flag_(value) {}
+  AtomicBool& operator=(bool value) {
+    CritScope scoped_lock(&cs_);
+    flag_ = value;
+    return *this;
+  }
+  bool get() const {
+    CritScope scoped_lock(&cs_);
+    return flag_;
+  }
+
+ private:
+  CriticalSection cs_;
+  bool flag_;
+};
+
+// Function objects to test Thread::Invoke.
+struct FunctorA {
+  int operator()() { return 42; }
+};
+class FunctorB {
+ public:
+  explicit FunctorB(AtomicBool* flag) : flag_(flag) {}
+  void operator()() { if (flag_) *flag_ = true; }
+ private:
+  AtomicBool* flag_;
+};
+struct FunctorC {
+  int operator()() {
+    Thread::Current()->ProcessMessages(50);
+    return 24;
+  }
+};
+
+// See: https://code.google.com/p/webrtc/issues/detail?id=2409
+TEST(ThreadTest, DISABLED_Main) {
+  const SocketAddress addr("127.0.0.1", 0);
+
+  // Create the messaging client on its own thread.
+  Thread th1;
+  Socket* socket = th1.socketserver()->CreateAsyncSocket(addr.family(),
+                                                         SOCK_DGRAM);
+  MessageClient msg_client(&th1, socket);
+
+  // Create the socket client on its own thread.
+  Thread th2;
+  AsyncSocket* asocket =
+      th2.socketserver()->CreateAsyncSocket(addr.family(), SOCK_DGRAM);
+  SocketClient sock_client(asocket, addr, &th1, &msg_client);
+
+  socket->Connect(sock_client.address());
+
+  th1.Start();
+  th2.Start();
+
+  // Get the messages started.
+  th1.PostDelayed(RTC_FROM_HERE, 100, &msg_client, 0, new TestMessage(1));
+
+  // Give the clients a little while to run.
+  // Messages will be processed at 100, 300, 500, 700, 900.
+  Thread* th_main = Thread::Current();
+  th_main->ProcessMessages(1000);
+
+  // Stop the sending client. Give the receiver a bit longer to run, in case
+  // it is running on a machine that is under load (e.g. the build machine).
+  th1.Stop();
+  th_main->ProcessMessages(200);
+  th2.Stop();
+
+  // Make sure the results were correct
+  EXPECT_EQ(5, msg_client.count);
+  EXPECT_EQ(34, msg_client.last);
+  EXPECT_EQ(5, sock_client.count);
+  EXPECT_EQ(55, sock_client.last);
+}
+
+// Test that setting thread names doesn't cause a malfunction.
+// There's no easy way to verify the name was set properly at this time.
+TEST(ThreadTest, Names) {
+  // Default name
+  Thread *thread;
+  thread = new Thread();
+  EXPECT_TRUE(thread->Start());
+  thread->Stop();
+  delete thread;
+  thread = new Thread();
+  // Name with no object parameter
+  EXPECT_TRUE(thread->SetName("No object", nullptr));
+  EXPECT_TRUE(thread->Start());
+  thread->Stop();
+  delete thread;
+  // Really long name
+  thread = new Thread();
+  EXPECT_TRUE(thread->SetName("Abcdefghijklmnopqrstuvwxyz1234567890", this));
+  EXPECT_TRUE(thread->Start());
+  thread->Stop();
+  delete thread;
+}
+
+TEST(ThreadTest, Wrap) {
+  Thread* current_thread = Thread::Current();
+  current_thread->UnwrapCurrent();
+  CustomThread* cthread = new CustomThread();
+  EXPECT_TRUE(cthread->WrapCurrent());
+  EXPECT_TRUE(cthread->RunningForTest());
+  EXPECT_FALSE(cthread->IsOwned());
+  cthread->UnwrapCurrent();
+  EXPECT_FALSE(cthread->RunningForTest());
+  delete cthread;
+  current_thread->WrapCurrent();
+}
+
+TEST(ThreadTest, Invoke) {
+  // Create and start the thread.
+  Thread thread;
+  thread.Start();
+  // Try calling functors.
+  EXPECT_EQ(42, thread.Invoke<int>(RTC_FROM_HERE, FunctorA()));
+  AtomicBool called;
+  FunctorB f2(&called);
+  thread.Invoke<void>(RTC_FROM_HERE, f2);
+  EXPECT_TRUE(called.get());
+  // Try calling bare functions.
+  struct LocalFuncs {
+    static int Func1() { return 999; }
+    static void Func2() {}
+  };
+  EXPECT_EQ(999, thread.Invoke<int>(RTC_FROM_HERE, &LocalFuncs::Func1));
+  thread.Invoke<void>(RTC_FROM_HERE, &LocalFuncs::Func2);
+}
+
+// Verifies that two threads calling Invoke on each other at the same time does
+// not deadlock.
+TEST(ThreadTest, TwoThreadsInvokeNoDeadlock) {
+  AutoThread thread;
+  Thread* current_thread = Thread::Current();
+  ASSERT_TRUE(current_thread != nullptr);
+
+  Thread other_thread;
+  other_thread.Start();
+
+  struct LocalFuncs {
+    static void Set(bool* out) { *out = true; }
+    static void InvokeSet(Thread* thread, bool* out) {
+      thread->Invoke<void>(RTC_FROM_HERE, Bind(&Set, out));
+    }
+  };
+
+  bool called = false;
+  other_thread.Invoke<void>(
+      RTC_FROM_HERE, Bind(&LocalFuncs::InvokeSet, current_thread, &called));
+
+  EXPECT_TRUE(called);
+}
+
+// Verifies that if thread A invokes a call on thread B and thread C is trying
+// to invoke A at the same time, thread A does not handle C's invoke while
+// invoking B.
+TEST(ThreadTest, ThreeThreadsInvoke) {
+  AutoThread thread;
+  Thread* thread_a = Thread::Current();
+  Thread thread_b, thread_c;
+  thread_b.Start();
+  thread_c.Start();
+
+  class LockedBool {
+   public:
+    explicit LockedBool(bool value) : value_(value) {}
+
+    void Set(bool value) {
+      CritScope lock(&crit_);
+      value_ = value;
+    }
+
+    bool Get() {
+      CritScope lock(&crit_);
+      return value_;
+    }
+
+   private:
+    CriticalSection crit_;
+    bool value_ GUARDED_BY(crit_);
+  };
+
+  struct LocalFuncs {
+    static void Set(LockedBool* out) { out->Set(true); }
+    static void InvokeSet(Thread* thread, LockedBool* out) {
+      thread->Invoke<void>(RTC_FROM_HERE, Bind(&Set, out));
+    }
+
+    // Set |out| true and call InvokeSet on |thread|.
+    static void SetAndInvokeSet(LockedBool* out,
+                                Thread* thread,
+                                LockedBool* out_inner) {
+      out->Set(true);
+      InvokeSet(thread, out_inner);
+    }
+
+    // Asynchronously invoke SetAndInvokeSet on |thread1| and wait until
+    // |thread1| starts the call.
+    static void AsyncInvokeSetAndWait(AsyncInvoker* invoker,
+                                      Thread* thread1,
+                                      Thread* thread2,
+                                      LockedBool* out) {
+      CriticalSection crit;
+      LockedBool async_invoked(false);
+
+      invoker->AsyncInvoke<void>(
+          RTC_FROM_HERE, thread1,
+          Bind(&SetAndInvokeSet, &async_invoked, thread2, out));
+
+      EXPECT_TRUE_WAIT(async_invoked.Get(), 2000);
+    }
+  };
+
+  AsyncInvoker invoker;
+  LockedBool thread_a_called(false);
+
+  // Start the sequence A --(invoke)--> B --(async invoke)--> C --(invoke)--> A.
+  // Thread B returns when C receives the call and C should be blocked until A
+  // starts to process messages.
+  thread_b.Invoke<void>(RTC_FROM_HERE,
+                        Bind(&LocalFuncs::AsyncInvokeSetAndWait, &invoker,
+                             &thread_c, thread_a, &thread_a_called));
+  EXPECT_FALSE(thread_a_called.Get());
+
+  EXPECT_TRUE_WAIT(thread_a_called.Get(), 2000);
+}
+
+// Set the name on a thread when the underlying QueueDestroyed signal is
+// triggered. This causes an error if the object is already partially
+// destroyed.
+class SetNameOnSignalQueueDestroyedTester : public sigslot::has_slots<> {
+ public:
+  SetNameOnSignalQueueDestroyedTester(Thread* thread) : thread_(thread) {
+    thread->SignalQueueDestroyed.connect(
+        this, &SetNameOnSignalQueueDestroyedTester::OnQueueDestroyed);
+  }
+
+  void OnQueueDestroyed() {
+    // Makes sure that if we access the Thread while it's being destroyed, that
+    // it doesn't cause a problem because the vtable has been modified.
+    thread_->SetName("foo", nullptr);
+  }
+
+ private:
+  Thread* thread_;
+};
+
+TEST(ThreadTest, SetNameOnSignalQueueDestroyed) {
+  Thread* thread1 = new Thread();
+  SetNameOnSignalQueueDestroyedTester tester1(thread1);
+  delete thread1;
+
+  Thread* thread2 = new AutoThread();
+  SetNameOnSignalQueueDestroyedTester tester2(thread2);
+  delete thread2;
+}
+
+class AsyncInvokeTest : public testing::Test {
+ public:
+  void IntCallback(int value) {
+    EXPECT_EQ(expected_thread_, Thread::Current());
+    int_value_ = value;
+  }
+  void SetExpectedThreadForIntCallback(Thread* thread) {
+    expected_thread_ = thread;
+  }
+
+ protected:
+  enum { kWaitTimeout = 1000 };
+  AsyncInvokeTest()
+      : int_value_(0),
+        expected_thread_(nullptr) {}
+
+  int int_value_;
+  Thread* expected_thread_;
+};
+
+TEST_F(AsyncInvokeTest, FireAndForget) {
+  AsyncInvoker invoker;
+  // Create and start the thread.
+  Thread thread;
+  thread.Start();
+  // Try calling functor.
+  AtomicBool called;
+  invoker.AsyncInvoke<void>(RTC_FROM_HERE, &thread, FunctorB(&called));
+  EXPECT_TRUE_WAIT(called.get(), kWaitTimeout);
+}
+
+TEST_F(AsyncInvokeTest, KillInvokerDuringExecute) {
+  // Use these events to get in a state where the functor is in the middle of
+  // executing, and then to wait for it to finish, ensuring the "EXPECT_FALSE"
+  // is run.
+  Event functor_started(false, false);
+  Event functor_continue(false, false);
+  Event functor_finished(false, false);
+
+  Thread thread;
+  thread.Start();
+  volatile bool invoker_destroyed = false;
+  {
+    AsyncInvoker invoker;
+    invoker.AsyncInvoke<void>(RTC_FROM_HERE, &thread,
+                              [&functor_started, &functor_continue,
+                               &functor_finished, &invoker_destroyed] {
+                                functor_started.Set();
+                                functor_continue.Wait(Event::kForever);
+                                rtc::Thread::Current()->SleepMs(kWaitTimeout);
+                                EXPECT_FALSE(invoker_destroyed);
+                                functor_finished.Set();
+                              });
+    functor_started.Wait(Event::kForever);
+
+    // Allow the functor to continue and immediately destroy the invoker.
+    functor_continue.Set();
+  }
+
+  // If the destructor DIDN'T wait for the functor to finish executing, it will
+  // hit the EXPECT_FALSE(invoker_destroyed) after it finishes sleeping for a
+  // second.
+  invoker_destroyed = true;
+  functor_finished.Wait(Event::kForever);
+}
+
+TEST_F(AsyncInvokeTest, Flush) {
+  AsyncInvoker invoker;
+  AtomicBool flag1;
+  AtomicBool flag2;
+  // Queue two async calls to the current thread.
+  invoker.AsyncInvoke<void>(RTC_FROM_HERE, Thread::Current(), FunctorB(&flag1));
+  invoker.AsyncInvoke<void>(RTC_FROM_HERE, Thread::Current(), FunctorB(&flag2));
+  // Because we haven't pumped messages, these should not have run yet.
+  EXPECT_FALSE(flag1.get());
+  EXPECT_FALSE(flag2.get());
+  // Force them to run now.
+  invoker.Flush(Thread::Current());
+  EXPECT_TRUE(flag1.get());
+  EXPECT_TRUE(flag2.get());
+}
+
+TEST_F(AsyncInvokeTest, FlushWithIds) {
+  AsyncInvoker invoker;
+  AtomicBool flag1;
+  AtomicBool flag2;
+  // Queue two async calls to the current thread, one with a message id.
+  invoker.AsyncInvoke<void>(RTC_FROM_HERE, Thread::Current(), FunctorB(&flag1),
+                            5);
+  invoker.AsyncInvoke<void>(RTC_FROM_HERE, Thread::Current(), FunctorB(&flag2));
+  // Because we haven't pumped messages, these should not have run yet.
+  EXPECT_FALSE(flag1.get());
+  EXPECT_FALSE(flag2.get());
+  // Execute pending calls with id == 5.
+  invoker.Flush(Thread::Current(), 5);
+  EXPECT_TRUE(flag1.get());
+  EXPECT_FALSE(flag2.get());
+  flag1 = false;
+  // Execute all pending calls. The id == 5 call should not execute again.
+  invoker.Flush(Thread::Current());
+  EXPECT_FALSE(flag1.get());
+  EXPECT_TRUE(flag2.get());
+}
+
+class GuardedAsyncInvokeTest : public testing::Test {
+ public:
+  void IntCallback(int value) {
+    EXPECT_EQ(expected_thread_, Thread::Current());
+    int_value_ = value;
+  }
+  void SetExpectedThreadForIntCallback(Thread* thread) {
+    expected_thread_ = thread;
+  }
+
+ protected:
+  const static int kWaitTimeout = 1000;
+  GuardedAsyncInvokeTest()
+      : int_value_(0),
+        expected_thread_(nullptr) {}
+
+  int int_value_;
+  Thread* expected_thread_;
+};
+
+// Functor for creating an invoker.
+struct CreateInvoker {
+  CreateInvoker(std::unique_ptr<GuardedAsyncInvoker>* invoker)
+      : invoker_(invoker) {}
+  void operator()() { invoker_->reset(new GuardedAsyncInvoker()); }
+  std::unique_ptr<GuardedAsyncInvoker>* invoker_;
+};
+
+// Test that we can call AsyncInvoke<void>() after the thread died.
+TEST_F(GuardedAsyncInvokeTest, KillThreadFireAndForget) {
+  // Create and start the thread.
+  std::unique_ptr<Thread> thread(new Thread());
+  thread->Start();
+  std::unique_ptr<GuardedAsyncInvoker> invoker;
+  // Create the invoker on |thread|.
+  thread->Invoke<void>(RTC_FROM_HERE, CreateInvoker(&invoker));
+  // Kill |thread|.
+  thread = nullptr;
+  // Try calling functor.
+  AtomicBool called;
+  EXPECT_FALSE(invoker->AsyncInvoke<void>(RTC_FROM_HERE, FunctorB(&called)));
+  // With thread gone, nothing should happen.
+  WAIT(called.get(), kWaitTimeout);
+  EXPECT_FALSE(called.get());
+}
+
+// The remaining tests check that GuardedAsyncInvoker behaves as AsyncInvoker
+// when Thread is still alive.
+TEST_F(GuardedAsyncInvokeTest, FireAndForget) {
+  GuardedAsyncInvoker invoker;
+  // Try calling functor.
+  AtomicBool called;
+  EXPECT_TRUE(invoker.AsyncInvoke<void>(RTC_FROM_HERE, FunctorB(&called)));
+  EXPECT_TRUE_WAIT(called.get(), kWaitTimeout);
+}
+
+TEST_F(GuardedAsyncInvokeTest, Flush) {
+  GuardedAsyncInvoker invoker;
+  AtomicBool flag1;
+  AtomicBool flag2;
+  // Queue two async calls to the current thread.
+  EXPECT_TRUE(invoker.AsyncInvoke<void>(RTC_FROM_HERE, FunctorB(&flag1)));
+  EXPECT_TRUE(invoker.AsyncInvoke<void>(RTC_FROM_HERE, FunctorB(&flag2)));
+  // Because we haven't pumped messages, these should not have run yet.
+  EXPECT_FALSE(flag1.get());
+  EXPECT_FALSE(flag2.get());
+  // Force them to run now.
+  EXPECT_TRUE(invoker.Flush());
+  EXPECT_TRUE(flag1.get());
+  EXPECT_TRUE(flag2.get());
+}
+
+TEST_F(GuardedAsyncInvokeTest, FlushWithIds) {
+  GuardedAsyncInvoker invoker;
+  AtomicBool flag1;
+  AtomicBool flag2;
+  // Queue two async calls to the current thread, one with a message id.
+  EXPECT_TRUE(invoker.AsyncInvoke<void>(RTC_FROM_HERE, FunctorB(&flag1), 5));
+  EXPECT_TRUE(invoker.AsyncInvoke<void>(RTC_FROM_HERE, FunctorB(&flag2)));
+  // Because we haven't pumped messages, these should not have run yet.
+  EXPECT_FALSE(flag1.get());
+  EXPECT_FALSE(flag2.get());
+  // Execute pending calls with id == 5.
+  EXPECT_TRUE(invoker.Flush(5));
+  EXPECT_TRUE(flag1.get());
+  EXPECT_FALSE(flag2.get());
+  flag1 = false;
+  // Execute all pending calls. The id == 5 call should not execute again.
+  EXPECT_TRUE(invoker.Flush());
+  EXPECT_FALSE(flag1.get());
+  EXPECT_TRUE(flag2.get());
+}
diff --git a/base/timedelta.h b/base/timedelta.h
index f2e98a8..71c7f9f 100644
--- a/base/timedelta.h
+++ b/base/timedelta.h
@@ -11,9 +11,119 @@
 #ifndef WEBRTC_BASE_TIMEDELTA_H_
 #define WEBRTC_BASE_TIMEDELTA_H_
 
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/timedelta.h"
+#include "webrtc/base/timeutils.h"
+
+// Convenience class to convert between different units of relative time.
+// Stores time to precision of nanoseconds, as int64_t internally.
+// Doesn't check for overflow/underflow.
+//
+// Based on TimeDelta in:
+// https://code.google.com/p/chromium/codesearch#chromium/src/base/time/time.h
+namespace rtc {
+
+class TimeDelta {
+ public:
+  TimeDelta() : delta_(0) {}
+
+  // Converts units of time to TimeDeltas.
+  static constexpr TimeDelta FromSeconds(int64_t secs) {
+    return TimeDelta(secs * kNumNanosecsPerSec);
+  }
+  static constexpr TimeDelta FromMilliseconds(int64_t ms) {
+    return TimeDelta(ms * kNumNanosecsPerMillisec);
+  }
+  static constexpr TimeDelta FromMicroseconds(int64_t us) {
+    return TimeDelta(us * kNumNanosecsPerMicrosec);
+  }
+  static constexpr TimeDelta FromNanoseconds(int64_t ns) {
+    return TimeDelta(ns);
+  }
+
+  // Returns true if the time delta is zero.
+  bool is_zero() const { return delta_ == 0; }
+
+  // Converts TimeDelta to units of time.
+  int64_t ToSeconds() const { return delta_ / kNumNanosecsPerSec; }
+  int64_t ToMilliseconds() const { return delta_ / kNumNanosecsPerMillisec; }
+  int64_t ToMicroseconds() const { return delta_ / kNumNanosecsPerMicrosec; }
+  int64_t ToNanoseconds() const { return delta_; }
+
+  TimeDelta& operator=(TimeDelta other) {
+    delta_ = other.delta_;
+    return *this;
+  }
+
+  // Computations with other deltas.
+  TimeDelta operator+(TimeDelta other) const {
+    return TimeDelta(delta_ + other.delta_);
+  }
+  TimeDelta operator-(TimeDelta other) const {
+    return TimeDelta(delta_ + other.delta_);
+  }
+
+  TimeDelta& operator+=(TimeDelta other) { return *this = (*this + other); }
+  TimeDelta& operator-=(TimeDelta other) { return *this = (*this - other); }
+  TimeDelta operator-() const { return TimeDelta(-delta_); }
+
+  // Computations with numeric types.
+  template <typename T>
+  TimeDelta operator*(T a) const {
+    return TimeDelta(delta_ * a);
+  }
+  template <typename T>
+  TimeDelta operator/(T a) const {
+    return TimeDelta(delta_ / a);
+  }
+  template <typename T>
+  TimeDelta& operator*=(T a) {
+    return *this = (*this * a);
+  }
+  template <typename T>
+  TimeDelta& operator/=(T a) {
+    return *this = (*this / a);
+  }
+
+  TimeDelta operator%(TimeDelta a) const {
+    return TimeDelta(delta_ % a.delta_);
+  }
+
+  // Comparison operators.
+  constexpr bool operator==(TimeDelta other) const {
+    return delta_ == other.delta_;
+  }
+  constexpr bool operator!=(TimeDelta other) const {
+    return delta_ != other.delta_;
+  }
+  constexpr bool operator<(TimeDelta other) const {
+    return delta_ < other.delta_;
+  }
+  constexpr bool operator<=(TimeDelta other) const {
+    return delta_ <= other.delta_;
+  }
+  constexpr bool operator>(TimeDelta other) const {
+    return delta_ > other.delta_;
+  }
+  constexpr bool operator>=(TimeDelta other) const {
+    return delta_ >= other.delta_;
+  }
+
+ private:
+  // Constructs a delta given the duration in nanoseconds. This is private
+  // to avoid confusion by callers with an integer constructor. Use
+  // FromSeconds, FromMilliseconds, etc. instead.
+  constexpr explicit TimeDelta(int64_t delta_ns) : delta_(delta_ns) {}
+
+  // Delta in nanoseconds.
+  int64_t delta_;
+};
+
+template <typename T>
+inline TimeDelta operator*(T a, TimeDelta td) {
+  return td * a;
+}
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_TIMEDELTA_H_
diff --git a/base/timestampaligner.cc b/base/timestampaligner.cc
new file mode 100644
index 0000000..281da8f
--- /dev/null
+++ b/base/timestampaligner.cc
@@ -0,0 +1,135 @@
+/*
+ *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <limits>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/timestampaligner.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+
+TimestampAligner::TimestampAligner()
+    : frames_seen_(0),
+      offset_us_(0),
+      clip_bias_us_(0),
+      prev_translated_time_us_(std::numeric_limits<int64_t>::min()) {}
+
+TimestampAligner::~TimestampAligner() {}
+
+int64_t TimestampAligner::TranslateTimestamp(int64_t camera_time_us,
+                                             int64_t system_time_us) {
+  return ClipTimestamp(
+      camera_time_us + UpdateOffset(camera_time_us, system_time_us),
+      system_time_us);
+}
+
+int64_t TimestampAligner::UpdateOffset(int64_t camera_time_us,
+                                       int64_t system_time_us) {
+  // Estimate the offset between system monotonic time and the capture
+  // time from the camera. The camera is assumed to provide more
+  // accurate timestamps than we get from the system time. But the
+  // camera may use its own free-running clock with a large offset and
+  // a small drift compared to the system clock. So the model is
+  // basically
+  //
+  //   y_k = c_0 + c_1 * x_k + v_k
+  //
+  // where x_k is the camera timestamp, believed to be accurate in its
+  // own scale. y_k is our reading of the system clock. v_k is the
+  // measurement noise, i.e., the delay from frame capture until the
+  // system clock was read.
+  //
+  // It's possible to do (weighted) least-squares estimation of both
+  // c_0 and c_1. Then we get the constants as c_1 = Cov(x,y) /
+  // Var(x), and c_0 = mean(y) - c_1 * mean(x). Substituting this c_0,
+  // we can rearrange the model as
+  //
+  //   y_k = mean(y) + (x_k - mean(x)) + (c_1 - 1) * (x_k - mean(x)) + v_k
+  //
+  // Now if we use a weighted average which gradually forgets old
+  // values, x_k - mean(x) is bounded, of the same order as the time
+  // constant (and close to constant for a steady frame rate). In
+  // addition, the frequency error |c_1 - 1| should be small. Cameras
+  // with a frequency error up to 3000 ppm (3 ms drift per second)
+  // have been observed, but frequency errors below 100 ppm could be
+  // expected of any cheap crystal.
+  //
+  // Bottom line is that we ignore the c_1 term, and use only the estimator
+  //
+  //    x_k + mean(y-x)
+  //
+  // where mean is plain averaging for initial samples, followed by
+  // exponential averaging.
+
+  // The input for averaging, y_k - x_k in the above notation.
+  int64_t diff_us = system_time_us - camera_time_us;
+  // The deviation from the current average.
+  int64_t error_us = diff_us - offset_us_;
+
+  // If the current difference is far from the currently estimated
+  // offset, the filter is reset. This could happen, e.g., if the
+  // camera clock is reset, or cameras are plugged in and out, or if
+  // the application process is temporarily suspended. Expected to
+  // happen for the very first timestamp (|frames_seen_| = 0). The
+  // threshold of 300 ms should make this unlikely in normal
+  // operation, and at the same time, converging gradually rather than
+  // resetting the filter should be tolerable for jumps in camera time
+  // below this threshold.
+  static const int64_t kResetThresholdUs = 300000;
+  if (std::abs(error_us) > kResetThresholdUs) {
+    LOG(LS_INFO) << "Resetting timestamp translation after averaging "
+                 << frames_seen_ << " frames. Old offset: " << offset_us_
+                 << ", new offset: " << diff_us;
+    frames_seen_ = 0;
+    clip_bias_us_ = 0;
+  }
+
+  static const int kWindowSize = 100;
+  if (frames_seen_ < kWindowSize) {
+    ++frames_seen_;
+  }
+  offset_us_ += error_us / frames_seen_;
+  return offset_us_;
+}
+
+int64_t TimestampAligner::ClipTimestamp(int64_t filtered_time_us,
+                                        int64_t system_time_us) {
+  const int64_t kMinFrameIntervalUs = rtc::kNumMicrosecsPerMillisec;
+  // Clip to make sure we don't produce timestamps in the future.
+  int64_t time_us = filtered_time_us - clip_bias_us_;
+  if (time_us > system_time_us) {
+    clip_bias_us_ += time_us - system_time_us;
+    time_us = system_time_us;
+  }
+  // Make timestamps monotonic, with a minimum inter-frame interval of 1 ms.
+  else if (time_us < prev_translated_time_us_ + kMinFrameIntervalUs) {
+    time_us = prev_translated_time_us_ + kMinFrameIntervalUs;
+    if (time_us > system_time_us) {
+      // In the anomalous case that this function is called with values of
+      // |system_time_us| less than |kMinFrameIntervalUs| apart, we may output
+      // timestamps with with too short inter-frame interval. We may even return
+      // duplicate timestamps in case this function is called several times with
+      // exactly the same |system_time_us|.
+      LOG(LS_WARNING) << "too short translated timestamp interval: "
+                      << "system time (us) = " << system_time_us
+                      << ", interval (us) = "
+                      << system_time_us - prev_translated_time_us_;
+      time_us = system_time_us;
+    }
+  }
+  RTC_DCHECK_GE(time_us, prev_translated_time_us_);
+  RTC_DCHECK_LE(time_us, system_time_us);
+  prev_translated_time_us_ = time_us;
+  return time_us;
+}
+
+}  // namespace rtc
diff --git a/base/timestampaligner.h b/base/timestampaligner.h
index 60c3631..9c2cc7a 100644
--- a/base/timestampaligner.h
+++ b/base/timestampaligner.h
@@ -11,9 +11,64 @@
 #ifndef WEBRTC_BASE_TIMESTAMPALIGNER_H_
 #define WEBRTC_BASE_TIMESTAMPALIGNER_H_
 
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/timestampaligner.h"
+#include "webrtc/base/constructormagic.h"
+
+namespace rtc {
+
+// The TimestampAligner class helps translating camera timestamps into
+// the same timescale as is used by rtc::TimeMicros(). Some cameras
+// have built in timestamping which is more accurate than reading the
+// system clock, but using a different epoch and unknown clock drift.
+// Frame timestamps in webrtc should use rtc::TimeMicros (system monotonic
+// time), and this class provides a filter which lets us use the
+// rtc::TimeMicros timescale, and at the same time take advantage of
+// higher accuracy of the camera clock.
+
+// This class is not thread safe, so all calls to it must be synchronized
+// externally.
+class TimestampAligner {
+ public:
+  TimestampAligner();
+  ~TimestampAligner();
+
+ public:
+  // Translates camera timestamps to the same timescale as is used by
+  // rtc::TimeMicros(). |camera_time_us| is assumed to be accurate, but
+  // with an unknown epoch and clock drift. |system_time_us| is
+  // time according to rtc::TimeMicros(), preferably read as soon as
+  // possible when the frame is captured. It may have poor accuracy
+  // due to poor resolution or scheduling delays. Returns the
+  // translated timestamp.
+  int64_t TranslateTimestamp(int64_t camera_time_us, int64_t system_time_us);
+
+ protected:
+  // Update the estimated offset between camera time and system monotonic time.
+  int64_t UpdateOffset(int64_t camera_time_us, int64_t system_time_us);
+
+  // Clip timestamp, return value is always
+  //    <= |system_time_us|, and
+  //    >= min(|prev_translated_time_us_| + |kMinFrameIntervalUs|,
+  //           |system_time_us|).
+  int64_t ClipTimestamp(int64_t filtered_time_us, int64_t system_time_us);
+
+ private:
+  // State for the timestamp translation.
+  int frames_seen_;
+  // Estimated offset between camera time and system monotonic time.
+  int64_t offset_us_;
+
+  // State for the ClipTimestamp method, applied after the filter.
+  // A large negative camera clock drift tends to push translated
+  // timestamps into the future. |clip_bias_us_| is subtracted from the
+  // translated timestamps, to get them back from the future.
+  int64_t clip_bias_us_;
+  // Used to ensure that translated timestamps are monotonous.
+  int64_t prev_translated_time_us_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(TimestampAligner);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_TIMESTAMPALIGNER_H_
diff --git a/base/timestampaligner_unittest.cc b/base/timestampaligner_unittest.cc
new file mode 100644
index 0000000..a4c0e5a
--- /dev/null
+++ b/base/timestampaligner_unittest.cc
@@ -0,0 +1,187 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <math.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/random.h"
+#include "webrtc/base/timestampaligner.h"
+
+namespace rtc {
+
+namespace {
+// Computes the difference x_k - mean(x), when x_k is the linear sequence x_k =
+// k, and the "mean" is plain mean for the first |window_size| samples, followed
+// by exponential averaging with weight 1 / |window_size| for each new sample.
+// This is needed to predict the effect of camera clock drift on the timestamp
+// translation. See the comment on TimestampAligner::UpdateOffset for more
+// context.
+double MeanTimeDifference(int nsamples, int window_size) {
+  if (nsamples <= window_size) {
+    // Plain averaging.
+    return nsamples / 2.0;
+  } else {
+    // Exponential convergence towards
+    // interval_error * (window_size - 1)
+    double alpha = 1.0 - 1.0 / window_size;
+
+    return ((window_size - 1) -
+            (window_size / 2.0 - 1) * pow(alpha, nsamples - window_size));
+  }
+}
+
+class TimestampAlignerForTest : public TimestampAligner {
+  // Make internal methods accessible to testing.
+ public:
+  using TimestampAligner::UpdateOffset;
+  using TimestampAligner::ClipTimestamp;
+};
+
+void TestTimestampFilter(double rel_freq_error) {
+  TimestampAlignerForTest timestamp_aligner_for_test;
+  TimestampAligner timestamp_aligner;
+  const int64_t kEpoch = 10000;
+  const int64_t kJitterUs = 5000;
+  const int64_t kIntervalUs = 33333;  // 30 FPS
+  const int kWindowSize = 100;
+  const int kNumFrames = 3 * kWindowSize;
+
+  int64_t interval_error_us = kIntervalUs * rel_freq_error;
+  int64_t system_start_us = rtc::TimeMicros();
+  webrtc::Random random(17);
+
+  int64_t prev_translated_time_us = system_start_us;
+
+  for (int i = 0; i < kNumFrames; i++) {
+    // Camera time subject to drift.
+    int64_t camera_time_us = kEpoch + i * (kIntervalUs + interval_error_us);
+    int64_t system_time_us = system_start_us + i * kIntervalUs;
+    // And system time readings are subject to jitter.
+    int64_t system_measured_us = system_time_us + random.Rand(kJitterUs);
+
+    int64_t offset_us = timestamp_aligner_for_test.UpdateOffset(
+        camera_time_us, system_measured_us);
+
+    int64_t filtered_time_us = camera_time_us + offset_us;
+    int64_t translated_time_us = timestamp_aligner_for_test.ClipTimestamp(
+        filtered_time_us, system_measured_us);
+
+    // Check that we get identical result from the all-in-one helper method.
+    ASSERT_EQ(translated_time_us, timestamp_aligner.TranslateTimestamp(
+                                      camera_time_us, system_measured_us));
+
+    EXPECT_LE(translated_time_us, system_measured_us);
+    EXPECT_GE(translated_time_us,
+              prev_translated_time_us + rtc::kNumMicrosecsPerMillisec);
+
+    // The relative frequency error contributes to the expected error
+    // by a factor which is the difference between the current time
+    // and the average of earlier sample times.
+    int64_t expected_error_us =
+        kJitterUs / 2 +
+        rel_freq_error * kIntervalUs * MeanTimeDifference(i, kWindowSize);
+
+    int64_t bias_us = filtered_time_us - translated_time_us;
+    EXPECT_GE(bias_us, 0);
+
+    if (i == 0) {
+      EXPECT_EQ(translated_time_us, system_measured_us);
+    } else {
+      EXPECT_NEAR(filtered_time_us, system_time_us + expected_error_us,
+                  2.0 * kJitterUs / sqrt(std::max(i, kWindowSize)));
+    }
+    // If the camera clock runs too fast (rel_freq_error > 0.0), The
+    // bias is expected to roughly cancel the expected error from the
+    // clock drift, as this grows. Otherwise, it reflects the
+    // measurement noise. The tolerances here were selected after some
+    // trial and error.
+    if (i < 10 || rel_freq_error <= 0.0) {
+      EXPECT_LE(bias_us, 3000);
+    } else {
+      EXPECT_NEAR(bias_us, expected_error_us, 1500);
+    }
+    prev_translated_time_us = translated_time_us;
+  }
+}
+
+}  // Anonymous namespace
+
+TEST(TimestampAlignerTest, AttenuateTimestampJitterNoDrift) {
+  TestTimestampFilter(0.0);
+}
+
+// 100 ppm is a worst case for a reasonable crystal.
+TEST(TimestampAlignerTest, AttenuateTimestampJitterSmallPosDrift) {
+  TestTimestampFilter(0.0001);
+}
+
+TEST(TimestampAlignerTest, AttenuateTimestampJitterSmallNegDrift) {
+  TestTimestampFilter(-0.0001);
+}
+
+// 3000 ppm, 3 ms / s, is the worst observed drift, see
+// https://bugs.chromium.org/p/webrtc/issues/detail?id=5456
+TEST(TimestampAlignerTest, AttenuateTimestampJitterLargePosDrift) {
+  TestTimestampFilter(0.003);
+}
+
+TEST(TimestampAlignerTest, AttenuateTimestampJitterLargeNegDrift) {
+  TestTimestampFilter(-0.003);
+}
+
+// Exhibits a mostly hypothetical problem, where certain inputs to the
+// TimestampAligner.UpdateOffset filter result in non-monotonous
+// translated timestamps. This test verifies that the ClipTimestamp
+// logic handles this case correctly.
+TEST(TimestampAlignerTest, ClipToMonotonous) {
+  TimestampAlignerForTest timestamp_aligner;
+
+  // For system time stamps { 0, s1, s1 + s2 }, and camera timestamps
+  // {0, c1, c1 + c2}, we exhibit non-monotonous behaviour if and only
+  // if c1 > s1 + 2 s2 + 4 c2.
+  const int kNumSamples = 3;
+  const int64_t camera_time_us[kNumSamples] = {0, 80000, 90001};
+  const int64_t system_time_us[kNumSamples] = {0, 10000, 20000};
+  const int64_t expected_offset_us[kNumSamples] = {0, -35000, -46667};
+
+  // Non-monotonic translated timestamps can happen when only for
+  // translated timestamps in the future. Which is tolerated if
+  // |timestamp_aligner.clip_bias_us| is large enough. Instead of
+  // changing that private member for this test, just add the bias to
+  // |system_time_us| when calling ClipTimestamp.
+  const int64_t kClipBiasUs = 100000;
+
+  bool did_clip = false;
+  int64_t prev_timestamp_us = std::numeric_limits<int64_t>::min();
+  for (int i = 0; i < kNumSamples; i++) {
+    int64_t offset_us =
+        timestamp_aligner.UpdateOffset(camera_time_us[i], system_time_us[i]);
+    EXPECT_EQ(offset_us, expected_offset_us[i]);
+
+    int64_t translated_timestamp_us = camera_time_us[i] + offset_us;
+    int64_t clip_timestamp_us = timestamp_aligner.ClipTimestamp(
+        translated_timestamp_us, system_time_us[i] + kClipBiasUs);
+    if (translated_timestamp_us <= prev_timestamp_us) {
+      did_clip = true;
+      EXPECT_EQ(clip_timestamp_us,
+                prev_timestamp_us + rtc::kNumMicrosecsPerMillisec);
+    } else {
+      // No change from clipping.
+      EXPECT_EQ(clip_timestamp_us, translated_timestamp_us);
+    }
+    prev_timestamp_us = clip_timestamp_us;
+  }
+  EXPECT_TRUE(did_clip);
+}
+
+}  // namespace rtc
diff --git a/base/timeutils.cc b/base/timeutils.cc
new file mode 100644
index 0000000..ee1415f
--- /dev/null
+++ b/base/timeutils.cc
@@ -0,0 +1,210 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <stdint.h>
+
+#if defined(WEBRTC_POSIX)
+#include <sys/time.h>
+#if defined(WEBRTC_MAC)
+#include <mach/mach_time.h>
+#endif
+#endif
+
+#if defined(WEBRTC_WIN)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <mmsystem.h>
+#include <sys/timeb.h>
+#endif
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+
+ClockInterface* g_clock = nullptr;
+
+ClockInterface* SetClockForTesting(ClockInterface* clock) {
+  ClockInterface* prev = g_clock;
+  g_clock = clock;
+  return prev;
+}
+
+ClockInterface* GetClockForTesting() {
+  return g_clock;
+}
+
+int64_t SystemTimeNanos() {
+  int64_t ticks;
+#if defined(WEBRTC_MAC)
+  static mach_timebase_info_data_t timebase;
+  if (timebase.denom == 0) {
+    // Get the timebase if this is the first time we run.
+    // Recommended by Apple's QA1398.
+    if (mach_timebase_info(&timebase) != KERN_SUCCESS) {
+      RTC_NOTREACHED();
+    }
+  }
+  // Use timebase to convert absolute time tick units into nanoseconds.
+  ticks = mach_absolute_time() * timebase.numer / timebase.denom;
+#elif defined(WEBRTC_POSIX)
+  struct timespec ts;
+  // TODO(deadbeef): Do we need to handle the case when CLOCK_MONOTONIC is not
+  // supported?
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  ticks = kNumNanosecsPerSec * static_cast<int64_t>(ts.tv_sec) +
+          static_cast<int64_t>(ts.tv_nsec);
+#elif defined(WEBRTC_WIN)
+  static volatile LONG last_timegettime = 0;
+  static volatile int64_t num_wrap_timegettime = 0;
+  volatile LONG* last_timegettime_ptr = &last_timegettime;
+  DWORD now = timeGetTime();
+  // Atomically update the last gotten time
+  DWORD old = InterlockedExchange(last_timegettime_ptr, now);
+  if (now < old) {
+    // If now is earlier than old, there may have been a race between threads.
+    // 0x0fffffff ~3.1 days, the code will not take that long to execute
+    // so it must have been a wrap around.
+    if (old > 0xf0000000 && now < 0x0fffffff) {
+      num_wrap_timegettime++;
+    }
+  }
+  ticks = now + (num_wrap_timegettime << 32);
+  // TODO(deadbeef): Calculate with nanosecond precision. Otherwise, we're
+  // just wasting a multiply and divide when doing Time() on Windows.
+  ticks = ticks * kNumNanosecsPerMillisec;
+#else
+#error Unsupported platform.
+#endif
+  return ticks;
+}
+
+int64_t SystemTimeMillis() {
+  return static_cast<int64_t>(SystemTimeNanos() / kNumNanosecsPerMillisec);
+}
+
+int64_t TimeNanos() {
+  if (g_clock) {
+    return g_clock->TimeNanos();
+  }
+  return SystemTimeNanos();
+}
+
+uint32_t Time32() {
+  return static_cast<uint32_t>(TimeNanos() / kNumNanosecsPerMillisec);
+}
+
+int64_t TimeMillis() {
+  return TimeNanos() / kNumNanosecsPerMillisec;
+}
+
+int64_t TimeMicros() {
+  return TimeNanos() / kNumNanosecsPerMicrosec;
+}
+
+int64_t TimeAfter(int64_t elapsed) {
+  RTC_DCHECK_GE(elapsed, 0);
+  return TimeMillis() + elapsed;
+}
+
+int32_t TimeDiff32(uint32_t later, uint32_t earlier) {
+  return later - earlier;
+}
+
+int64_t TimeDiff(int64_t later, int64_t earlier) {
+  return later - earlier;
+}
+
+TimestampWrapAroundHandler::TimestampWrapAroundHandler()
+    : last_ts_(0), num_wrap_(-1) {}
+
+int64_t TimestampWrapAroundHandler::Unwrap(uint32_t ts) {
+  if (num_wrap_ == -1) {
+    last_ts_ = ts;
+    num_wrap_ = 0;
+    return ts;
+  }
+
+  if (ts < last_ts_) {
+    if (last_ts_ >= 0xf0000000 && ts < 0x0fffffff)
+      ++num_wrap_;
+  } else if ((ts - last_ts_) > 0xf0000000) {
+    // Backwards wrap. Unwrap with last wrap count and don't update last_ts_.
+    return ts + ((num_wrap_ - 1) << 32);
+  }
+
+  last_ts_ = ts;
+  return ts + (num_wrap_ << 32);
+}
+
+int64_t TmToSeconds(const std::tm& tm) {
+  static short int mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+  static short int cumul_mdays[12] = {0,   31,  59,  90,  120, 151,
+                                      181, 212, 243, 273, 304, 334};
+  int year = tm.tm_year + 1900;
+  int month = tm.tm_mon;
+  int day = tm.tm_mday - 1;  // Make 0-based like the rest.
+  int hour = tm.tm_hour;
+  int min = tm.tm_min;
+  int sec = tm.tm_sec;
+
+  bool expiry_in_leap_year = (year % 4 == 0 &&
+                              (year % 100 != 0 || year % 400 == 0));
+
+  if (year < 1970)
+    return -1;
+  if (month < 0 || month > 11)
+    return -1;
+  if (day < 0 || day >= mdays[month] + (expiry_in_leap_year && month == 2 - 1))
+    return -1;
+  if (hour < 0 || hour > 23)
+    return -1;
+  if (min < 0 || min > 59)
+    return -1;
+  if (sec < 0 || sec > 59)
+    return -1;
+
+  day += cumul_mdays[month];
+
+  // Add number of leap days between 1970 and the expiration year, inclusive.
+  day += ((year / 4 - 1970 / 4) - (year / 100 - 1970 / 100) +
+          (year / 400 - 1970 / 400));
+
+  // We will have added one day too much above if expiration is during a leap
+  // year, and expiration is in January or February.
+  if (expiry_in_leap_year && month <= 2 - 1) // |month| is zero based.
+    day -= 1;
+
+  // Combine all variables into seconds from 1970-01-01 00:00 (except |month|
+  // which was accumulated into |day| above).
+  return (((static_cast<int64_t>
+            (year - 1970) * 365 + day) * 24 + hour) * 60 + min) * 60 + sec;
+}
+
+int64_t TimeUTCMicros() {
+#if defined(WEBRTC_POSIX)
+  struct timeval time;
+  gettimeofday(&time, nullptr);
+  // Convert from second (1.0) and microsecond (1e-6).
+  return (static_cast<int64_t>(time.tv_sec) * rtc::kNumMicrosecsPerSec +
+          time.tv_usec);
+
+#elif defined(WEBRTC_WIN)
+  struct _timeb time;
+  _ftime(&time);
+  // Convert from second (1.0) and milliseconds (1e-3).
+  return (static_cast<int64_t>(time.time) * rtc::kNumMicrosecsPerSec +
+          static_cast<int64_t>(time.millitm) * rtc::kNumMicrosecsPerMillisec);
+#endif
+}
+
+} // namespace rtc
diff --git a/base/timeutils.h b/base/timeutils.h
index 1569b58..735af4a 100644
--- a/base/timeutils.h
+++ b/base/timeutils.h
@@ -11,9 +11,119 @@
 #ifndef WEBRTC_BASE_TIMEUTILS_H_
 #define WEBRTC_BASE_TIMEUTILS_H_
 
+#include <stdint.h>
+#include <time.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/timeutils.h"
+#include <ctime>
+
+namespace rtc {
+
+static const int64_t kNumMillisecsPerSec = INT64_C(1000);
+static const int64_t kNumMicrosecsPerSec = INT64_C(1000000);
+static const int64_t kNumNanosecsPerSec = INT64_C(1000000000);
+
+static const int64_t kNumMicrosecsPerMillisec =
+    kNumMicrosecsPerSec / kNumMillisecsPerSec;
+static const int64_t kNumNanosecsPerMillisec =
+    kNumNanosecsPerSec / kNumMillisecsPerSec;
+static const int64_t kNumNanosecsPerMicrosec =
+    kNumNanosecsPerSec / kNumMicrosecsPerSec;
+
+// TODO(honghaiz): Define a type for the time value specifically.
+
+class ClockInterface {
+ public:
+  virtual ~ClockInterface() {}
+  virtual int64_t TimeNanos() const = 0;
+};
+
+// Sets the global source of time. This is useful mainly for unit tests.
+//
+// Returns the previously set ClockInterface, or nullptr if none is set.
+//
+// Does not transfer ownership of the clock. SetClockForTesting(nullptr)
+// should be called before the ClockInterface is deleted.
+//
+// This method is not thread-safe; it should only be used when no other thread
+// is running (for example, at the start/end of a unit test, or start/end of
+// main()).
+//
+// TODO(deadbeef): Instead of having functions that access this global
+// ClockInterface, we may want to pass the ClockInterface into everything
+// that uses it, eliminating the need for a global variable and this function.
+ClockInterface* SetClockForTesting(ClockInterface* clock);
+
+// Returns previously set clock, or nullptr if no custom clock is being used.
+ClockInterface* GetClockForTesting();
+
+// Returns the actual system time, even if a clock is set for testing.
+// Useful for timeouts while using a test clock, or for logging.
+int64_t SystemTimeNanos();
+int64_t SystemTimeMillis();
+
+// Returns the current time in milliseconds in 32 bits.
+uint32_t Time32();
+
+// Returns the current time in milliseconds in 64 bits.
+int64_t TimeMillis();
+// Deprecated. Do not use this in any new code.
+inline int64_t Time() {
+  return TimeMillis();
+}
+
+// Returns the current time in microseconds.
+int64_t TimeMicros();
+
+// Returns the current time in nanoseconds.
+int64_t TimeNanos();
+
+
+// Returns a future timestamp, 'elapsed' milliseconds from now.
+int64_t TimeAfter(int64_t elapsed);
+
+// Number of milliseconds that would elapse between 'earlier' and 'later'
+// timestamps.  The value is negative if 'later' occurs before 'earlier'.
+int64_t TimeDiff(int64_t later, int64_t earlier);
+int32_t TimeDiff32(uint32_t later, uint32_t earlier);
+
+// The number of milliseconds that have elapsed since 'earlier'.
+inline int64_t TimeSince(int64_t earlier) {
+  return TimeMillis() - earlier;
+}
+
+// The number of milliseconds that will elapse between now and 'later'.
+inline int64_t TimeUntil(int64_t later) {
+  return later - TimeMillis();
+}
+
+class TimestampWrapAroundHandler {
+ public:
+  TimestampWrapAroundHandler();
+
+  int64_t Unwrap(uint32_t ts);
+
+ private:
+  uint32_t last_ts_;
+  int64_t num_wrap_;
+};
+
+// Convert from std::tm, which is relative to 1900-01-01 00:00 to number of
+// seconds from 1970-01-01 00:00 ("epoch").  Don't return time_t since that
+// is still 32 bits on many systems.
+int64_t TmToSeconds(const std::tm& tm);
+
+// Return the number of microseconds since January 1, 1970, UTC.
+// Useful mainly when producing logs to be correlated with other
+// devices, and when the devices in question all have properly
+// synchronized clocks.
+//
+// Note that this function obeys the system's idea about what the time
+// is. It is not guaranteed to be monotonic; it will jump in case the
+// system time is changed, e.g., by some other process calling
+// settimeofday. Always use rtc::TimeMicros(), not this function, for
+// measuring time intervals and timeouts.
+int64_t TimeUTCMicros();
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_TIMEUTILS_H_
diff --git a/base/timeutils_unittest.cc b/base/timeutils_unittest.cc
new file mode 100644
index 0000000..bac26c1
--- /dev/null
+++ b/base/timeutils_unittest.cc
@@ -0,0 +1,382 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/event.h"
+#include "webrtc/base/fakeclock.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+
+TEST(TimeTest, TimeInMs) {
+  int64_t ts_earlier = TimeMillis();
+  Thread::SleepMs(100);
+  int64_t ts_now = TimeMillis();
+  // Allow for the thread to wakeup ~20ms early.
+  EXPECT_GE(ts_now, ts_earlier + 80);
+  // Make sure the Time is not returning in smaller unit like microseconds.
+  EXPECT_LT(ts_now, ts_earlier + 1000);
+}
+
+TEST(TimeTest, Intervals) {
+  int64_t ts_earlier = TimeMillis();
+  int64_t ts_later = TimeAfter(500);
+
+  // We can't depend on ts_later and ts_earlier to be exactly 500 apart
+  // since time elapses between the calls to TimeMillis() and TimeAfter(500)
+  EXPECT_LE(500,  TimeDiff(ts_later, ts_earlier));
+  EXPECT_GE(-500, TimeDiff(ts_earlier, ts_later));
+
+  // Time has elapsed since ts_earlier
+  EXPECT_GE(TimeSince(ts_earlier), 0);
+
+  // ts_earlier is earlier than now, so TimeUntil ts_earlier is -ve
+  EXPECT_LE(TimeUntil(ts_earlier), 0);
+
+  // ts_later likely hasn't happened yet, so TimeSince could be -ve
+  // but within 500
+  EXPECT_GE(TimeSince(ts_later), -500);
+
+  // TimeUntil ts_later is at most 500
+  EXPECT_LE(TimeUntil(ts_later), 500);
+}
+
+TEST(TimeTest, TestTimeDiff64) {
+  int64_t ts_diff = 100;
+  int64_t ts_earlier = rtc::TimeMillis();
+  int64_t ts_later = ts_earlier + ts_diff;
+  EXPECT_EQ(ts_diff, rtc::TimeDiff(ts_later, ts_earlier));
+  EXPECT_EQ(-ts_diff, rtc::TimeDiff(ts_earlier, ts_later));
+}
+
+class TimestampWrapAroundHandlerTest : public testing::Test {
+ public:
+  TimestampWrapAroundHandlerTest() {}
+
+ protected:
+  TimestampWrapAroundHandler wraparound_handler_;
+};
+
+TEST_F(TimestampWrapAroundHandlerTest, Unwrap) {
+  // Start value.
+  int64_t ts = 2;
+  EXPECT_EQ(ts,
+            wraparound_handler_.Unwrap(static_cast<uint32_t>(ts & 0xffffffff)));
+
+  // Wrap backwards.
+  ts = -2;
+  EXPECT_EQ(ts,
+            wraparound_handler_.Unwrap(static_cast<uint32_t>(ts & 0xffffffff)));
+
+  // Forward to 2 again.
+  ts = 2;
+  EXPECT_EQ(ts,
+            wraparound_handler_.Unwrap(static_cast<uint32_t>(ts & 0xffffffff)));
+
+  // Max positive skip ahead, until max value (0xffffffff).
+  for (uint32_t i = 0; i <= 0xf; ++i) {
+    ts = (i << 28) + 0x0fffffff;
+    EXPECT_EQ(
+        ts, wraparound_handler_.Unwrap(static_cast<uint32_t>(ts & 0xffffffff)));
+  }
+
+  // Wrap around.
+  ts += 2;
+  EXPECT_EQ(ts,
+            wraparound_handler_.Unwrap(static_cast<uint32_t>(ts & 0xffffffff)));
+
+  // Max wrap backward...
+  ts -= 0x0fffffff;
+  EXPECT_EQ(ts,
+            wraparound_handler_.Unwrap(static_cast<uint32_t>(ts & 0xffffffff)));
+
+  // ...and back again.
+  ts += 0x0fffffff;
+  EXPECT_EQ(ts,
+            wraparound_handler_.Unwrap(static_cast<uint32_t>(ts & 0xffffffff)));
+}
+
+TEST_F(TimestampWrapAroundHandlerTest, NoNegativeStart) {
+  int64_t ts = 0xfffffff0;
+  EXPECT_EQ(ts,
+            wraparound_handler_.Unwrap(static_cast<uint32_t>(ts & 0xffffffff)));
+}
+
+class TmToSeconds : public testing::Test {
+ public:
+  TmToSeconds() {
+    // Set use of the test RNG to get deterministic expiration timestamp.
+    rtc::SetRandomTestMode(true);
+  }
+  ~TmToSeconds() override {
+    // Put it back for the next test.
+    rtc::SetRandomTestMode(false);
+  }
+
+  void TestTmToSeconds(int times) {
+    static char mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+    for (int i = 0; i < times; i++) {
+
+      // First generate something correct and check that TmToSeconds is happy.
+      int year = rtc::CreateRandomId() % 400 + 1970;
+
+      bool leap_year = false;
+      if (year % 4 == 0)
+        leap_year = true;
+      if (year % 100 == 0)
+        leap_year = false;
+      if (year % 400 == 0)
+        leap_year = true;
+
+      std::tm tm;
+      tm.tm_year = year - 1900;  // std::tm is year 1900 based.
+      tm.tm_mon = rtc::CreateRandomId() % 12;
+      tm.tm_mday = rtc::CreateRandomId() % mdays[tm.tm_mon] + 1;
+      tm.tm_hour = rtc::CreateRandomId() % 24;
+      tm.tm_min = rtc::CreateRandomId() % 60;
+      tm.tm_sec = rtc::CreateRandomId() % 60;
+      int64_t t = rtc::TmToSeconds(tm);
+      EXPECT_TRUE(t >= 0);
+
+      // Now damage a random field and check that TmToSeconds is unhappy.
+      switch (rtc::CreateRandomId() % 11) {
+        case 0:
+          tm.tm_year = 1969 - 1900;
+          break;
+        case 1:
+          tm.tm_mon = -1;
+          break;
+        case 2:
+          tm.tm_mon = 12;
+          break;
+        case 3:
+          tm.tm_mday = 0;
+          break;
+        case 4:
+          tm.tm_mday = mdays[tm.tm_mon] + (leap_year && tm.tm_mon == 1) + 1;
+          break;
+        case 5:
+          tm.tm_hour = -1;
+          break;
+        case 6:
+          tm.tm_hour = 24;
+          break;
+        case 7:
+          tm.tm_min = -1;
+          break;
+        case 8:
+          tm.tm_min = 60;
+          break;
+        case 9:
+          tm.tm_sec = -1;
+          break;
+        case 10:
+          tm.tm_sec = 60;
+          break;
+      }
+      EXPECT_EQ(rtc::TmToSeconds(tm), -1);
+    }
+    // Check consistency with the system gmtime_r.  With time_t, we can only
+    // portably test dates until 2038, which is achieved by the % 0x80000000.
+    for (int i = 0; i < times; i++) {
+      time_t t = rtc::CreateRandomId() % 0x80000000;
+#if defined(WEBRTC_WIN)
+      std::tm* tm = std::gmtime(&t);
+      EXPECT_TRUE(tm);
+      EXPECT_TRUE(rtc::TmToSeconds(*tm) == t);
+#else
+      std::tm tm;
+      EXPECT_TRUE(gmtime_r(&t, &tm));
+      EXPECT_TRUE(rtc::TmToSeconds(tm) == t);
+#endif
+    }
+  }
+};
+
+TEST_F(TmToSeconds, TestTmToSeconds) {
+  TestTmToSeconds(100000);
+}
+
+TEST(TimeDelta, FromAndTo) {
+  EXPECT_TRUE(TimeDelta::FromSeconds(2) == TimeDelta::FromMilliseconds(2000));
+  EXPECT_TRUE(TimeDelta::FromMilliseconds(3) ==
+              TimeDelta::FromMicroseconds(3000));
+  EXPECT_TRUE(TimeDelta::FromMicroseconds(4) ==
+              TimeDelta::FromNanoseconds(4000));
+  EXPECT_EQ(13, TimeDelta::FromSeconds(13).ToSeconds());
+  EXPECT_EQ(13, TimeDelta::FromMilliseconds(13).ToMilliseconds());
+  EXPECT_EQ(13, TimeDelta::FromMicroseconds(13).ToMicroseconds());
+  EXPECT_EQ(13, TimeDelta::FromNanoseconds(13).ToNanoseconds());
+}
+
+TEST(TimeDelta, ComparisonOperators) {
+  EXPECT_LT(TimeDelta::FromSeconds(1), TimeDelta::FromSeconds(2));
+  EXPECT_EQ(TimeDelta::FromSeconds(3), TimeDelta::FromSeconds(3));
+  EXPECT_GT(TimeDelta::FromSeconds(5), TimeDelta::FromSeconds(4));
+}
+
+TEST(TimeDelta, NumericOperators) {
+  double d = 0.5;
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) * d);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) / d);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) *= d);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) /= d);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            d * TimeDelta::FromMilliseconds(1000));
+
+  float f = 0.5;
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) * f);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) / f);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) *= f);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) /= f);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            f * TimeDelta::FromMilliseconds(1000));
+
+  int i = 2;
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) * i);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) / i);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) *= i);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) /= i);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            i * TimeDelta::FromMilliseconds(1000));
+
+  int64_t i64 = 2;
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) * i64);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) / i64);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) *= i64);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) /= i64);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            i64 * TimeDelta::FromMilliseconds(1000));
+
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) * 0.5);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) / 0.5);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) *= 0.5);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) /= 0.5);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            0.5 * TimeDelta::FromMilliseconds(1000));
+
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) * 2);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) / 2);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            TimeDelta::FromMilliseconds(1000) *= 2);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+            TimeDelta::FromMilliseconds(1000) /= 2);
+  EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+            2 * TimeDelta::FromMilliseconds(1000));
+}
+
+// Test that all the time functions exposed by TimeUtils get time from the
+// fake clock when it's set.
+TEST(FakeClock, TimeFunctionsUseFakeClock) {
+  FakeClock clock;
+  SetClockForTesting(&clock);
+
+  clock.SetTimeNanos(987654321);
+  EXPECT_EQ(987u, Time32());
+  EXPECT_EQ(987, TimeMillis());
+  EXPECT_EQ(987654, TimeMicros());
+  EXPECT_EQ(987654321, TimeNanos());
+  EXPECT_EQ(1000u, TimeAfter(13));
+
+  SetClockForTesting(nullptr);
+  // After it's unset, we should get a normal time.
+  EXPECT_NE(987, TimeMillis());
+}
+
+TEST(FakeClock, InitialTime) {
+  FakeClock clock;
+  EXPECT_EQ(0, clock.TimeNanos());
+}
+
+TEST(FakeClock, SetTimeNanos) {
+  FakeClock clock;
+  clock.SetTimeNanos(123);
+  EXPECT_EQ(123, clock.TimeNanos());
+  clock.SetTimeNanos(456);
+  EXPECT_EQ(456, clock.TimeNanos());
+}
+
+TEST(FakeClock, AdvanceTime) {
+  FakeClock clock;
+  clock.AdvanceTime(TimeDelta::FromNanoseconds(1111u));
+  EXPECT_EQ(1111, clock.TimeNanos());
+  clock.AdvanceTime(TimeDelta::FromMicroseconds(2222u));
+  EXPECT_EQ(2223111, clock.TimeNanos());
+  clock.AdvanceTime(TimeDelta::FromMilliseconds(3333u));
+  EXPECT_EQ(3335223111, clock.TimeNanos());
+  clock.AdvanceTime(TimeDelta::FromSeconds(4444u));
+  EXPECT_EQ(4447335223111, clock.TimeNanos());
+}
+
+// When the clock is advanced, threads that are waiting in a socket select
+// should wake up and look at the new time. This allows tests using the
+// fake clock to run much faster, if the test is bound by time constraints
+// (such as a test for a STUN ping timeout).
+TEST(FakeClock, SettingTimeWakesThreads) {
+  int64_t real_start_time_ms = TimeMillis();
+
+  FakeClock clock;
+  SetClockForTesting(&clock);
+
+  Thread worker;
+  worker.Start();
+
+  // Post an event that won't be executed for 10 seconds.
+  Event message_handler_dispatched(false, false);
+  auto functor = [&message_handler_dispatched] {
+    message_handler_dispatched.Set();
+  };
+  FunctorMessageHandler<void, decltype(functor)> handler(functor);
+  worker.PostDelayed(RTC_FROM_HERE, 60000, &handler);
+
+  // Wait for a bit for the worker thread to be started and enter its socket
+  // select(). Otherwise this test would be trivial since the worker thread
+  // would process the event as soon as it was started.
+  Thread::Current()->SleepMs(1000);
+
+  // Advance the fake clock, expecting the worker thread to wake up
+  // and dispatch the message instantly.
+  clock.AdvanceTime(TimeDelta::FromSeconds(60u));
+  EXPECT_TRUE(message_handler_dispatched.Wait(0));
+  worker.Stop();
+
+  SetClockForTesting(nullptr);
+
+  // The message should have been dispatched long before the 60 seconds fully
+  // elapsed (just a sanity check).
+  int64_t real_end_time_ms = TimeMillis();
+  EXPECT_LT(real_end_time_ms - real_start_time_ms, 10000);
+}
+
+}  // namespace rtc
diff --git a/base/trace_event.h b/base/trace_event.h
index 1bea5f4..3e99374 100644
--- a/base/trace_event.h
+++ b/base/trace_event.h
@@ -6,9 +6,905 @@
 #ifndef WEBRTC_BASE_TRACE_EVENT_H_
 #define WEBRTC_BASE_TRACE_EVENT_H_
 
+#include <string>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/trace_event.h"
+#include "webrtc/base/event_tracer.h"
+
+#if defined(TRACE_EVENT0)
+#error "Another copy of trace_event.h has already been included."
+#endif
+
+// Extracted from Chromium's src/base/debug/trace_event.h.
+
+// This header is designed to give you trace_event macros without specifying
+// how the events actually get collected and stored. If you need to expose trace
+// event to some other universe, you can copy-and-paste this file,
+// implement the TRACE_EVENT_API macros, and do any other necessary fixup for
+// the target platform. The end result is that multiple libraries can funnel
+// events through to a shared trace event collector.
+
+// Trace events are for tracking application performance and resource usage.
+// Macros are provided to track:
+//    Begin and end of function calls
+//    Counters
+//
+// Events are issued against categories. Whereas LOG's
+// categories are statically defined, TRACE categories are created
+// implicitly with a string. For example:
+//   TRACE_EVENT_INSTANT0("MY_SUBSYSTEM", "SomeImportantEvent")
+//
+// Events can be INSTANT, or can be pairs of BEGIN and END in the same scope:
+//   TRACE_EVENT_BEGIN0("MY_SUBSYSTEM", "SomethingCostly")
+//   doSomethingCostly()
+//   TRACE_EVENT_END0("MY_SUBSYSTEM", "SomethingCostly")
+// Note: our tools can't always determine the correct BEGIN/END pairs unless
+// these are used in the same scope. Use ASYNC_BEGIN/ASYNC_END macros if you
+// need them to be in separate scopes.
+//
+// A common use case is to trace entire function scopes. This
+// issues a trace BEGIN and END automatically:
+//   void doSomethingCostly() {
+//     TRACE_EVENT0("MY_SUBSYSTEM", "doSomethingCostly");
+//     ...
+//   }
+//
+// Additional parameters can be associated with an event:
+//   void doSomethingCostly2(int howMuch) {
+//     TRACE_EVENT1("MY_SUBSYSTEM", "doSomethingCostly",
+//         "howMuch", howMuch);
+//     ...
+//   }
+//
+// The trace system will automatically add to this information the
+// current process id, thread id, and a timestamp in microseconds.
+//
+// To trace an asynchronous procedure such as an IPC send/receive, use
+// ASYNC_BEGIN and ASYNC_END:
+//   [single threaded sender code]
+//     static int send_count = 0;
+//     ++send_count;
+//     TRACE_EVENT_ASYNC_BEGIN0("ipc", "message", send_count);
+//     Send(new MyMessage(send_count));
+//   [receive code]
+//     void OnMyMessage(send_count) {
+//       TRACE_EVENT_ASYNC_END0("ipc", "message", send_count);
+//     }
+// The third parameter is a unique ID to match ASYNC_BEGIN/ASYNC_END pairs.
+// ASYNC_BEGIN and ASYNC_END can occur on any thread of any traced process.
+// Pointers can be used for the ID parameter, and they will be mangled
+// internally so that the same pointer on two different processes will not
+// match. For example:
+//   class MyTracedClass {
+//    public:
+//     MyTracedClass() {
+//       TRACE_EVENT_ASYNC_BEGIN0("category", "MyTracedClass", this);
+//     }
+//     ~MyTracedClass() {
+//       TRACE_EVENT_ASYNC_END0("category", "MyTracedClass", this);
+//     }
+//   }
+//
+// Trace event also supports counters, which is a way to track a quantity
+// as it varies over time. Counters are created with the following macro:
+//   TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter", g_myCounterValue);
+//
+// Counters are process-specific. The macro itself can be issued from any
+// thread, however.
+//
+// Sometimes, you want to track two counters at once. You can do this with two
+// counter macros:
+//   TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter0", g_myCounterValue[0]);
+//   TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter1", g_myCounterValue[1]);
+// Or you can do it with a combined macro:
+//   TRACE_COUNTER2("MY_SUBSYSTEM", "myCounter",
+//       "bytesPinned", g_myCounterValue[0],
+//       "bytesAllocated", g_myCounterValue[1]);
+// This indicates to the tracing UI that these counters should be displayed
+// in a single graph, as a summed area chart.
+//
+// Since counters are in a global namespace, you may want to disembiguate with a
+// unique ID, by using the TRACE_COUNTER_ID* variations.
+//
+// By default, trace collection is compiled in, but turned off at runtime.
+// Collecting trace data is the responsibility of the embedding
+// application. In Chrome's case, navigating to about:tracing will turn on
+// tracing and display data collected across all active processes.
+//
+//
+// Memory scoping note:
+// Tracing copies the pointers, not the string content, of the strings passed
+// in for category, name, and arg_names.  Thus, the following code will
+// cause problems:
+//     char* str = strdup("impprtantName");
+//     TRACE_EVENT_INSTANT0("SUBSYSTEM", str);  // BAD!
+//     free(str);                   // Trace system now has dangling pointer
+//
+// To avoid this issue with the |name| and |arg_name| parameters, use the
+// TRACE_EVENT_COPY_XXX overloads of the macros at additional runtime overhead.
+// Notes: The category must always be in a long-lived char* (i.e. static const).
+//        The |arg_values|, when used, are always deep copied with the _COPY
+//        macros.
+//
+// When are string argument values copied:
+// const char* arg_values are only referenced by default:
+//     TRACE_EVENT1("category", "name",
+//                  "arg1", "literal string is only referenced");
+// Use TRACE_STR_COPY to force copying of a const char*:
+//     TRACE_EVENT1("category", "name",
+//                  "arg1", TRACE_STR_COPY("string will be copied"));
+// std::string arg_values are always copied:
+//     TRACE_EVENT1("category", "name",
+//                  "arg1", std::string("string will be copied"));
+//
+//
+// Thread Safety:
+// Thread safety is provided by methods defined in event_tracer.h. See the file
+// for details.
+
+
+// By default, const char* argument values are assumed to have long-lived scope
+// and will not be copied. Use this macro to force a const char* to be copied.
+#define TRACE_STR_COPY(str) \
+    webrtc::trace_event_internal::TraceStringWithCopy(str)
+
+// This will mark the trace event as disabled by default. The user will need
+// to explicitly enable the event.
+#define TRACE_DISABLED_BY_DEFAULT(name) "disabled-by-default-" name
+
+// By default, uint64 ID argument values are not mangled with the Process ID in
+// TRACE_EVENT_ASYNC macros. Use this macro to force Process ID mangling.
+#define TRACE_ID_MANGLE(id) \
+    webrtc::trace_event_internal::TraceID::ForceMangle(id)
+
+// Records a pair of begin and end events called "name" for the current
+// scope, with 0, 1 or 2 associated arguments. If the category is not
+// enabled, then this does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_EVENT0(category, name) \
+    INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name)
+#define TRACE_EVENT1(category, name, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, arg1_name, arg1_val)
+#define TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, arg1_name, arg1_val, \
+        arg2_name, arg2_val)
+
+// Same as TRACE_EVENT except that they are not included in official builds.
+#ifdef OFFICIAL_BUILD
+#define UNSHIPPED_TRACE_EVENT0(category, name) (void)0
+#define UNSHIPPED_TRACE_EVENT1(category, name, arg1_name, arg1_val) (void)0
+#define UNSHIPPED_TRACE_EVENT2(category, name, arg1_name, arg1_val, \
+                               arg2_name, arg2_val) (void)0
+#define UNSHIPPED_TRACE_EVENT_INSTANT0(category, name) (void)0
+#define UNSHIPPED_TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \
+    (void)0
+#define UNSHIPPED_TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \
+                                       arg2_name, arg2_val) (void)0
+#else
+#define UNSHIPPED_TRACE_EVENT0(category, name) \
+    TRACE_EVENT0(category, name)
+#define UNSHIPPED_TRACE_EVENT1(category, name, arg1_name, arg1_val) \
+    TRACE_EVENT1(category, name, arg1_name, arg1_val)
+#define UNSHIPPED_TRACE_EVENT2(category, name, arg1_name, arg1_val, \
+                               arg2_name, arg2_val) \
+    TRACE_EVENT2(category, name, arg1_name, arg1_val, arg2_name, arg2_val)
+#define UNSHIPPED_TRACE_EVENT_INSTANT0(category, name) \
+    TRACE_EVENT_INSTANT0(category, name)
+#define UNSHIPPED_TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \
+    TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val)
+#define UNSHIPPED_TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \
+                                       arg2_name, arg2_val) \
+    TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \
+                         arg2_name, arg2_val)
+#endif
+
+// Records a single event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_EVENT_INSTANT0(category, name) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+        category, name, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_INSTANT1(category, name, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+        category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_INSTANT2(category, name, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+        category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \
+        arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_INSTANT0(category, name) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+        category, name, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_INSTANT1(category, name, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+        category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_INSTANT2(category, name, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, \
+        category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
+        arg2_name, arg2_val)
+
+// Records a single BEGIN event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_EVENT_BEGIN0(category, name) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+        category, name, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_BEGIN1(category, name, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+        category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_BEGIN2(category, name, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+        category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \
+        arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_BEGIN0(category, name) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+        category, name, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_BEGIN1(category, name, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+        category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_BEGIN2(category, name, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, \
+        category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
+        arg2_name, arg2_val)
+
+// Records a single END event for "name" immediately. If the category
+// is not enabled, then this does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_EVENT_END0(category, name) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+        category, name, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_END1(category, name, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+        category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_END2(category, name, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+        category, name, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \
+        arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_END0(category, name) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+        category, name, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_END1(category, name, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+        category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_END2(category, name, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, \
+        category, name, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \
+        arg2_name, arg2_val)
+
+// Records the value of a counter called "name" immediately. Value
+// must be representable as a 32 bit integer.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_COUNTER1(category, name, value) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \
+        category, name, TRACE_EVENT_FLAG_NONE, \
+        "value", static_cast<int>(value))
+#define TRACE_COPY_COUNTER1(category, name, value) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \
+        category, name, TRACE_EVENT_FLAG_COPY, \
+        "value", static_cast<int>(value))
+
+// Records the values of a multi-parted counter called "name" immediately.
+// The UI will treat value1 and value2 as parts of a whole, displaying their
+// values as a stacked-bar chart.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+#define TRACE_COUNTER2(category, name, value1_name, value1_val, \
+        value2_name, value2_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \
+        category, name, TRACE_EVENT_FLAG_NONE, \
+        value1_name, static_cast<int>(value1_val), \
+        value2_name, static_cast<int>(value2_val))
+#define TRACE_COPY_COUNTER2(category, name, value1_name, value1_val, \
+        value2_name, value2_val) \
+    INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, \
+        category, name, TRACE_EVENT_FLAG_COPY, \
+        value1_name, static_cast<int>(value1_val), \
+        value2_name, static_cast<int>(value2_val))
+
+// Records the value of a counter called "name" immediately. Value
+// must be representable as a 32 bit integer.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+// - |id| is used to disambiguate counters with the same name. It must either
+//   be a pointer or an integer value up to 64 bits. If it's a pointer, the bits
+//   will be xored with a hash of the process ID so that the same pointer on
+//   two different processes will not collide.
+#define TRACE_COUNTER_ID1(category, name, id, value) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, \
+        "value", static_cast<int>(value))
+#define TRACE_COPY_COUNTER_ID1(category, name, id, value) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        "value", static_cast<int>(value))
+
+// Records the values of a multi-parted counter called "name" immediately.
+// The UI will treat value1 and value2 as parts of a whole, displaying their
+// values as a stacked-bar chart.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+// - |id| is used to disambiguate counters with the same name. It must either
+//   be a pointer or an integer value up to 64 bits. If it's a pointer, the bits
+//   will be xored with a hash of the process ID so that the same pointer on
+//   two different processes will not collide.
+#define TRACE_COUNTER_ID2(category, name, id, value1_name, value1_val, \
+        value2_name, value2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, \
+        value1_name, static_cast<int>(value1_val), \
+        value2_name, static_cast<int>(value2_val))
+#define TRACE_COPY_COUNTER_ID2(category, name, id, value1_name, value1_val, \
+        value2_name, value2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        value1_name, static_cast<int>(value1_val), \
+        value2_name, static_cast<int>(value2_val))
+
+
+// Records a single ASYNC_BEGIN event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+// - |id| is used to match the ASYNC_BEGIN event with the ASYNC_END event. ASYNC
+//   events are considered to match if their category, name and id values all
+//   match. |id| must either be a pointer or an integer value up to 64 bits. If
+//   it's a pointer, the bits will be xored with a hash of the process ID so
+//   that the same pointer on two different processes will not collide.
+// An asynchronous operation can consist of multiple phases. The first phase is
+// defined by the ASYNC_BEGIN calls. Additional phases can be defined using the
+// ASYNC_STEP macros. When the operation completes, call ASYNC_END.
+// An ASYNC trace typically occur on a single thread (if not, they will only be
+// drawn on the thread defined in the ASYNC_BEGIN event), but all events in that
+// operation must use the same |name| and |id|. Each event can have its own
+// args.
+#define TRACE_EVENT_ASYNC_BEGIN0(category, name, id) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, \
+        arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN0(category, name, id) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN1(category, name, id, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category, name, id, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        arg1_name, arg1_val, arg2_name, arg2_val)
+
+// Records a single ASYNC_STEP event for |step| immediately. If the category
+// is not enabled, then this does nothing. The |name| and |id| must match the
+// ASYNC_BEGIN event above. The |step| param identifies this step within the
+// async event. This should be called at the beginning of the next phase of an
+// asynchronous operation.
+#define TRACE_EVENT_ASYNC_STEP0(category, name, id, step) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, "step", step)
+#define TRACE_EVENT_ASYNC_STEP1(category, name, id, step, \
+                                      arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \
+        arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_STEP0(category, name, id, step) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, "step", step)
+#define TRACE_EVENT_COPY_ASYNC_STEP1(category, name, id, step, \
+        arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, "step", step, \
+        arg1_name, arg1_val)
+
+// Records a single ASYNC_END event for "name" immediately. If the category
+// is not enabled, then this does nothing.
+#define TRACE_EVENT_ASYNC_END0(category, name, id) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+        category, name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_ASYNC_END1(category, name, id, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_ASYNC_END2(category, name, id, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, \
+        arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_ASYNC_END0(category, name, id) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+        category, name, id, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_ASYNC_END1(category, name, id, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_END2(category, name, id, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        arg1_name, arg1_val, arg2_name, arg2_val)
+
+
+// Records a single FLOW_BEGIN event called "name" immediately, with 0, 1 or 2
+// associated arguments. If the category is not enabled, then this
+// does nothing.
+// - category and name strings must have application lifetime (statics or
+//   literals). They may not include " chars.
+// - |id| is used to match the FLOW_BEGIN event with the FLOW_END event. FLOW
+//   events are considered to match if their category, name and id values all
+//   match. |id| must either be a pointer or an integer value up to 64 bits. If
+//   it's a pointer, the bits will be xored with a hash of the process ID so
+//   that the same pointer on two different processes will not collide.
+// FLOW events are different from ASYNC events in how they are drawn by the
+// tracing UI. A FLOW defines asynchronous data flow, such as posting a task
+// (FLOW_BEGIN) and later executing that task (FLOW_END). Expect FLOWs to be
+// drawn as lines or arrows from FLOW_BEGIN scopes to FLOW_END scopes. Similar
+// to ASYNC, a FLOW can consist of multiple phases. The first phase is defined
+// by the FLOW_BEGIN calls. Additional phases can be defined using the FLOW_STEP
+// macros. When the operation completes, call FLOW_END. An async operation can
+// span threads and processes, but all events in that operation must use the
+// same |name| and |id|. Each event can have its own args.
+#define TRACE_EVENT_FLOW_BEGIN0(category, name, id) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, \
+        arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_FLOW_BEGIN0(category, name, id) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        arg1_name, arg1_val, arg2_name, arg2_val)
+
+// Records a single FLOW_STEP event for |step| immediately. If the category
+// is not enabled, then this does nothing. The |name| and |id| must match the
+// FLOW_BEGIN event above. The |step| param identifies this step within the
+// async event. This should be called at the beginning of the next phase of an
+// asynchronous operation.
+#define TRACE_EVENT_FLOW_STEP0(category, name, id, step) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, "step", step)
+#define TRACE_EVENT_FLOW_STEP1(category, name, id, step, \
+        arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \
+        arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_FLOW_STEP0(category, name, id, step) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, "step", step)
+#define TRACE_EVENT_COPY_FLOW_STEP1(category, name, id, step, \
+        arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, "step", step, \
+        arg1_name, arg1_val)
+
+// Records a single FLOW_END event for "name" immediately. If the category
+// is not enabled, then this does nothing.
+#define TRACE_EVENT_FLOW_END0(category, name, id) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+        category, name, id, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_FLOW_END1(category, name, id, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_FLOW_END2(category, name, id, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+        category, name, id, TRACE_EVENT_FLAG_NONE, \
+        arg1_name, arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_FLOW_END0(category, name, id) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+        category, name, id, TRACE_EVENT_FLAG_COPY)
+#define TRACE_EVENT_COPY_FLOW_END1(category, name, id, arg1_name, arg1_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_FLOW_END2(category, name, id, arg1_name, arg1_val, \
+        arg2_name, arg2_val) \
+    INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
+        category, name, id, TRACE_EVENT_FLAG_COPY, \
+        arg1_name, arg1_val, arg2_name, arg2_val)
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation specific tracing API definitions.
+
+// Get a pointer to the enabled state of the given trace category. Only
+// long-lived literal strings should be given as the category name. The returned
+// pointer can be held permanently in a local static for example. If the
+// unsigned char is non-zero, tracing is enabled. If tracing is enabled,
+// TRACE_EVENT_API_ADD_TRACE_EVENT can be called. It's OK if tracing is disabled
+// between the load of the tracing state and the call to
+// TRACE_EVENT_API_ADD_TRACE_EVENT, because this flag only provides an early out
+// for best performance when tracing is disabled.
+// const unsigned char*
+//     TRACE_EVENT_API_GET_CATEGORY_ENABLED(const char* category_name)
+#define TRACE_EVENT_API_GET_CATEGORY_ENABLED \
+    webrtc::EventTracer::GetCategoryEnabled
+
+// Add a trace event to the platform tracing system.
+// void TRACE_EVENT_API_ADD_TRACE_EVENT(
+//                    char phase,
+//                    const unsigned char* category_enabled,
+//                    const char* name,
+//                    unsigned long long id,
+//                    int num_args,
+//                    const char** arg_names,
+//                    const unsigned char* arg_types,
+//                    const unsigned long long* arg_values,
+//                    unsigned char flags)
+#define TRACE_EVENT_API_ADD_TRACE_EVENT webrtc::EventTracer::AddTraceEvent
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Implementation detail: trace event macros create temporary variables
+// to keep instrumentation overhead low. These macros give each temporary
+// variable a unique name based on the line number to prevent name collissions.
+#define INTERNAL_TRACE_EVENT_UID3(a,b) \
+    trace_event_unique_##a##b
+#define INTERNAL_TRACE_EVENT_UID2(a,b) \
+    INTERNAL_TRACE_EVENT_UID3(a,b)
+#define INTERNAL_TRACE_EVENT_UID(name_prefix) \
+    INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__)
+
+// Implementation detail: internal macro to create static category.
+#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category) \
+    static const unsigned char* INTERNAL_TRACE_EVENT_UID(catstatic) = \
+        TRACE_EVENT_API_GET_CATEGORY_ENABLED(category);
+
+// Implementation detail: internal macro to create static category and add
+// event if the category is enabled.
+#define INTERNAL_TRACE_EVENT_ADD(phase, category, name, flags, ...) \
+    do { \
+      INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \
+      if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+        webrtc::trace_event_internal::AddTraceEvent(          \
+            phase, INTERNAL_TRACE_EVENT_UID(catstatic), name, \
+            webrtc::trace_event_internal::kNoEventId, flags, ##__VA_ARGS__); \
+      } \
+    } while (0)
+
+// Implementation detail: internal macro to create static category and add begin
+// event if the category is enabled. Also adds the end event when the scope
+// ends.
+#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category, name, ...) \
+    INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \
+    webrtc::trace_event_internal::TraceEndOnScopeClose  \
+        INTERNAL_TRACE_EVENT_UID(profileScope); \
+    if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+      webrtc::trace_event_internal::AddTraceEvent(      \
+          TRACE_EVENT_PHASE_BEGIN, \
+          INTERNAL_TRACE_EVENT_UID(catstatic), \
+          name, webrtc::trace_event_internal::kNoEventId,       \
+          TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__); \
+      INTERNAL_TRACE_EVENT_UID(profileScope).Initialize( \
+          INTERNAL_TRACE_EVENT_UID(catstatic), name); \
+    }
+
+// Implementation detail: internal macro to create static category and add
+// event if the category is enabled.
+#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category, name, id, flags, \
+                                         ...) \
+    do { \
+      INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category); \
+      if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+        unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
+        webrtc::trace_event_internal::TraceID trace_event_trace_id( \
+            id, &trace_event_flags); \
+        webrtc::trace_event_internal::AddTraceEvent( \
+            phase, INTERNAL_TRACE_EVENT_UID(catstatic), \
+            name, trace_event_trace_id.data(), trace_event_flags, \
+            ##__VA_ARGS__); \
+      } \
+    } while (0)
+
+// Notes regarding the following definitions:
+// New values can be added and propagated to third party libraries, but existing
+// definitions must never be changed, because third party libraries may use old
+// definitions.
+
+// Phase indicates the nature of an event entry. E.g. part of a begin/end pair.
+#define TRACE_EVENT_PHASE_BEGIN    ('B')
+#define TRACE_EVENT_PHASE_END      ('E')
+#define TRACE_EVENT_PHASE_INSTANT  ('I')
+#define TRACE_EVENT_PHASE_ASYNC_BEGIN ('S')
+#define TRACE_EVENT_PHASE_ASYNC_STEP  ('T')
+#define TRACE_EVENT_PHASE_ASYNC_END   ('F')
+#define TRACE_EVENT_PHASE_FLOW_BEGIN ('s')
+#define TRACE_EVENT_PHASE_FLOW_STEP  ('t')
+#define TRACE_EVENT_PHASE_FLOW_END   ('f')
+#define TRACE_EVENT_PHASE_METADATA ('M')
+#define TRACE_EVENT_PHASE_COUNTER  ('C')
+
+// Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
+#define TRACE_EVENT_FLAG_NONE        (static_cast<unsigned char>(0))
+#define TRACE_EVENT_FLAG_COPY        (static_cast<unsigned char>(1 << 0))
+#define TRACE_EVENT_FLAG_HAS_ID      (static_cast<unsigned char>(1 << 1))
+#define TRACE_EVENT_FLAG_MANGLE_ID   (static_cast<unsigned char>(1 << 2))
+
+// Type values for identifying types in the TraceValue union.
+#define TRACE_VALUE_TYPE_BOOL         (static_cast<unsigned char>(1))
+#define TRACE_VALUE_TYPE_UINT         (static_cast<unsigned char>(2))
+#define TRACE_VALUE_TYPE_INT          (static_cast<unsigned char>(3))
+#define TRACE_VALUE_TYPE_DOUBLE       (static_cast<unsigned char>(4))
+#define TRACE_VALUE_TYPE_POINTER      (static_cast<unsigned char>(5))
+#define TRACE_VALUE_TYPE_STRING       (static_cast<unsigned char>(6))
+#define TRACE_VALUE_TYPE_COPY_STRING  (static_cast<unsigned char>(7))
+
+namespace webrtc {
+namespace trace_event_internal {
+
+// Specify these values when the corresponding argument of AddTraceEvent is not
+// used.
+const int kZeroNumArgs = 0;
+const unsigned long long kNoEventId = 0;
+
+// TraceID encapsulates an ID that can either be an integer or pointer. Pointers
+// are mangled with the Process ID so that they are unlikely to collide when the
+// same pointer is used on different processes.
+class TraceID {
+ public:
+  class ForceMangle {
+    public:
+     explicit ForceMangle(unsigned long long id) : data_(id) {}
+     explicit ForceMangle(unsigned long id) : data_(id) {}
+     explicit ForceMangle(unsigned int id) : data_(id) {}
+     explicit ForceMangle(unsigned short id) : data_(id) {}
+     explicit ForceMangle(unsigned char id) : data_(id) {}
+     explicit ForceMangle(long long id)
+         : data_(static_cast<unsigned long long>(id)) {}
+     explicit ForceMangle(long id)
+         : data_(static_cast<unsigned long long>(id)) {}
+     explicit ForceMangle(int id)
+         : data_(static_cast<unsigned long long>(id)) {}
+     explicit ForceMangle(short id)
+         : data_(static_cast<unsigned long long>(id)) {}
+     explicit ForceMangle(signed char id)
+         : data_(static_cast<unsigned long long>(id)) {}
+
+     unsigned long long data() const { return data_; }
+
+    private:
+     unsigned long long data_;
+  };
+
+  explicit TraceID(const void* id, unsigned char* flags)
+      : data_(static_cast<unsigned long long>(
+              reinterpret_cast<uintptr_t>(id))) {
+    *flags |= TRACE_EVENT_FLAG_MANGLE_ID;
+  }
+  explicit TraceID(ForceMangle id, unsigned char* flags) : data_(id.data()) {
+    *flags |= TRACE_EVENT_FLAG_MANGLE_ID;
+  }
+  explicit TraceID(unsigned long long id, unsigned char* flags)
+      : data_(id) { (void)flags; }
+  explicit TraceID(unsigned long id, unsigned char* flags)
+      : data_(id) { (void)flags; }
+  explicit TraceID(unsigned int id, unsigned char* flags)
+      : data_(id) { (void)flags; }
+  explicit TraceID(unsigned short id, unsigned char* flags)
+      : data_(id) { (void)flags; }
+  explicit TraceID(unsigned char id, unsigned char* flags)
+      : data_(id) { (void)flags; }
+  explicit TraceID(long long id, unsigned char* flags)
+      : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+  explicit TraceID(long id, unsigned char* flags)
+      : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+  explicit TraceID(int id, unsigned char* flags)
+      : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+  explicit TraceID(short id, unsigned char* flags)
+      : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+  explicit TraceID(signed char id, unsigned char* flags)
+      : data_(static_cast<unsigned long long>(id)) { (void)flags; }
+
+  unsigned long long data() const { return data_; }
+
+ private:
+  unsigned long long data_;
+};
+
+// Simple union to store various types as unsigned long long.
+union TraceValueUnion {
+  bool as_bool;
+  unsigned long long as_uint;
+  long long as_int;
+  double as_double;
+  const void* as_pointer;
+  const char* as_string;
+};
+
+// Simple container for const char* that should be copied instead of retained.
+class TraceStringWithCopy {
+ public:
+  explicit TraceStringWithCopy(const char* str) : str_(str) {}
+  operator const char* () const { return str_; }
+ private:
+  const char* str_;
+};
+
+// Define SetTraceValue for each allowed type. It stores the type and
+// value in the return arguments. This allows this API to avoid declaring any
+// structures so that it is portable to third_party libraries.
+#define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, \
+                                         union_member, \
+                                         value_type_id) \
+    static inline void SetTraceValue(actual_type arg, \
+                                     unsigned char* type, \
+                                     unsigned long long* value) { \
+      TraceValueUnion type_value; \
+      type_value.union_member = arg; \
+      *type = value_type_id; \
+      *value = type_value.as_uint; \
+    }
+// Simpler form for int types that can be safely casted.
+#define INTERNAL_DECLARE_SET_TRACE_VALUE_INT(actual_type, \
+                                             value_type_id) \
+    static inline void SetTraceValue(actual_type arg, \
+                                     unsigned char* type, \
+                                     unsigned long long* value) { \
+      *type = value_type_id; \
+      *value = static_cast<unsigned long long>(arg); \
+    }
+
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned long long, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned long, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned int, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned short, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned char, TRACE_VALUE_TYPE_UINT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(long long, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(long, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(short, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE_INT(signed char, TRACE_VALUE_TYPE_INT)
+INTERNAL_DECLARE_SET_TRACE_VALUE(bool, as_bool, TRACE_VALUE_TYPE_BOOL)
+INTERNAL_DECLARE_SET_TRACE_VALUE(double, as_double, TRACE_VALUE_TYPE_DOUBLE)
+INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, as_pointer,
+                                 TRACE_VALUE_TYPE_POINTER)
+INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, as_string,
+                                 TRACE_VALUE_TYPE_STRING)
+INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, as_string,
+                                 TRACE_VALUE_TYPE_COPY_STRING)
+
+#undef INTERNAL_DECLARE_SET_TRACE_VALUE
+#undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT
+
+// std::string version of SetTraceValue so that trace arguments can be strings.
+static inline void SetTraceValue(const std::string& arg,
+                                 unsigned char* type,
+                                 unsigned long long* value) {
+  TraceValueUnion type_value;
+  type_value.as_string = arg.c_str();
+  *type = TRACE_VALUE_TYPE_COPY_STRING;
+  *value = type_value.as_uint;
+}
+
+// These AddTraceEvent template functions are defined here instead of in the
+// macro, because the arg_values could be temporary objects, such as
+// std::string. In order to store pointers to the internal c_str and pass
+// through to the tracing API, the arg_values must live throughout
+// these procedures.
+
+static inline void AddTraceEvent(char phase,
+                                const unsigned char* category_enabled,
+                                const char* name,
+                                unsigned long long id,
+                                unsigned char flags) {
+  TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_enabled, name, id,
+                                  kZeroNumArgs, nullptr, nullptr, nullptr,
+                                  flags);
+}
+
+template<class ARG1_TYPE>
+static inline void AddTraceEvent(char phase,
+                                const unsigned char* category_enabled,
+                                const char* name,
+                                unsigned long long id,
+                                unsigned char flags,
+                                const char* arg1_name,
+                                const ARG1_TYPE& arg1_val) {
+  const int num_args = 1;
+  unsigned char arg_types[1];
+  unsigned long long arg_values[1];
+  SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]);
+  TRACE_EVENT_API_ADD_TRACE_EVENT(
+      phase, category_enabled, name, id,
+      num_args, &arg1_name, arg_types, arg_values,
+      flags);
+}
+
+template<class ARG1_TYPE, class ARG2_TYPE>
+static inline void AddTraceEvent(char phase,
+                                const unsigned char* category_enabled,
+                                const char* name,
+                                unsigned long long id,
+                                unsigned char flags,
+                                const char* arg1_name,
+                                const ARG1_TYPE& arg1_val,
+                                const char* arg2_name,
+                                const ARG2_TYPE& arg2_val) {
+  const int num_args = 2;
+  const char* arg_names[2] = { arg1_name, arg2_name };
+  unsigned char arg_types[2];
+  unsigned long long arg_values[2];
+  SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]);
+  SetTraceValue(arg2_val, &arg_types[1], &arg_values[1]);
+  TRACE_EVENT_API_ADD_TRACE_EVENT(
+      phase, category_enabled, name, id,
+      num_args, arg_names, arg_types, arg_values,
+      flags);
+}
+
+// Used by TRACE_EVENTx macro. Do not use directly.
+class TraceEndOnScopeClose {
+ public:
+  // Note: members of data_ intentionally left uninitialized. See Initialize.
+  TraceEndOnScopeClose() : p_data_(nullptr) {}
+  ~TraceEndOnScopeClose() {
+    if (p_data_)
+      AddEventIfEnabled();
+  }
+
+  void Initialize(const unsigned char* category_enabled,
+                  const char* name) {
+    data_.category_enabled = category_enabled;
+    data_.name = name;
+    p_data_ = &data_;
+  }
+
+ private:
+  // Add the end event if the category is still enabled.
+  void AddEventIfEnabled() {
+    // Only called when p_data_ is non-null.
+    if (*p_data_->category_enabled) {
+      TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_END,
+                                      p_data_->category_enabled, p_data_->name,
+                                      kNoEventId, kZeroNumArgs, nullptr,
+                                      nullptr, nullptr, TRACE_EVENT_FLAG_NONE);
+    }
+  }
+
+  // This Data struct workaround is to avoid initializing all the members
+  // in Data during construction of this object, since this object is always
+  // constructed, even when tracing is disabled. If the members of Data were
+  // members of this class instead, compiler warnings occur about potential
+  // uninitialized accesses.
+  struct Data {
+    const unsigned char* category_enabled;
+    const char* name;
+  };
+  Data* p_data_;
+  Data data_;
+};
+
+}  // namespace trace_event_internal
+}  // namespace webrtc
 
 #endif  // WEBRTC_BASE_TRACE_EVENT_H_
diff --git a/base/transformadapter.cc b/base/transformadapter.cc
new file mode 100644
index 0000000..1f1a2a0
--- /dev/null
+++ b/base/transformadapter.cc
@@ -0,0 +1,197 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/transformadapter.h"
+
+#include <string.h>
+
+#include "webrtc/base/checks.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+
+TransformAdapter::TransformAdapter(StreamInterface * stream,
+                                   TransformInterface * transform,
+                                   bool direction_read)
+    : StreamAdapterInterface(stream), transform_(transform),
+      direction_read_(direction_read), state_(ST_PROCESSING), len_(0) {
+}
+
+TransformAdapter::~TransformAdapter() {
+  TransformAdapter::Close();
+  delete transform_;
+}
+
+StreamResult
+TransformAdapter::Read(void * buffer, size_t buffer_len,
+                       size_t * read, int * error) {
+  if (!direction_read_)
+    return SR_EOS;
+
+  while (state_ != ST_ERROR) {
+    if (state_ == ST_COMPLETE)
+      return SR_EOS;
+
+    // Buffer more data
+    if ((state_ == ST_PROCESSING) && (len_ < sizeof(buffer_))) {
+      size_t subread;
+      StreamResult result = StreamAdapterInterface::Read(
+                              buffer_ + len_,
+                              sizeof(buffer_) - len_,
+                              &subread,
+                              &error_);
+      if (result == SR_BLOCK) {
+        return SR_BLOCK;
+      } else if (result == SR_ERROR) {
+        state_ = ST_ERROR;
+        break;
+      } else if (result == SR_EOS) {
+        state_ = ST_FLUSHING;
+      } else {
+        len_ += subread;
+      }
+    }
+
+    // Process buffered data
+    size_t in_len = len_;
+    size_t out_len = buffer_len;
+    StreamResult result = transform_->Transform(buffer_, &in_len,
+                                                buffer, &out_len,
+                                                (state_ == ST_FLUSHING));
+    RTC_DCHECK(result != SR_BLOCK);
+    if (result == SR_EOS) {
+      // Note: Don't signal SR_EOS this iteration, unless out_len is zero
+      state_ = ST_COMPLETE;
+    } else if (result == SR_ERROR) {
+      state_ = ST_ERROR;
+      error_ = -1; // TODO: propagate error
+      break;
+    } else if ((out_len == 0) && (state_ == ST_FLUSHING)) {
+      // If there is no output AND no more input, then something is wrong
+      state_ = ST_ERROR;
+      error_ = -1; // TODO: better error code?
+      break;
+    }
+
+    len_ -= in_len;
+    if (len_ > 0)
+      memmove(buffer_, buffer_ + in_len, len_);
+
+    if (out_len == 0)
+      continue;
+
+    if (read)
+      *read = out_len;
+    return SR_SUCCESS;
+  }
+
+  if (error)
+    *error = error_;
+  return SR_ERROR;
+}
+
+StreamResult
+TransformAdapter::Write(const void * data, size_t data_len,
+                        size_t * written, int * error) {
+  if (direction_read_)
+    return SR_EOS;
+
+  size_t bytes_written = 0;
+  while (state_ != ST_ERROR) {
+    if (state_ == ST_COMPLETE)
+      return SR_EOS;
+
+    if (len_ < sizeof(buffer_)) {
+      // Process buffered data
+      size_t in_len = data_len;
+      size_t out_len = sizeof(buffer_) - len_;
+      StreamResult result = transform_->Transform(data, &in_len,
+                                                  buffer_ + len_, &out_len,
+                                                  (state_ == ST_FLUSHING));
+
+      RTC_DCHECK(result != SR_BLOCK);
+      if (result == SR_EOS) {
+        // Note: Don't signal SR_EOS this iteration, unless no data written
+        state_ = ST_COMPLETE;
+      } else if (result == SR_ERROR) {
+        RTC_NOTREACHED();  // When this happens, think about what should be done
+        state_ = ST_ERROR;
+        error_ = -1; // TODO: propagate error
+        break;
+      }
+
+      len_ = out_len;
+      bytes_written = in_len;
+    }
+
+    size_t pos = 0;
+    while (pos < len_) {
+      size_t subwritten;
+      StreamResult result = StreamAdapterInterface::Write(buffer_ + pos,
+                                                          len_ - pos,
+                                                          &subwritten,
+                                                          &error_);
+      if (result == SR_BLOCK) {
+        RTC_NOTREACHED();  // We should handle this
+        return SR_BLOCK;
+      } else if (result == SR_ERROR) {
+        state_ = ST_ERROR;
+        break;
+      } else if (result == SR_EOS) {
+        state_ = ST_COMPLETE;
+        break;
+      }
+
+      pos += subwritten;
+    }
+
+    len_ -= pos;
+    if (len_ > 0)
+      memmove(buffer_, buffer_ + pos, len_);
+
+    if (bytes_written == 0)
+      continue;
+
+    if (written)
+      *written = bytes_written;
+    return SR_SUCCESS;
+  }
+
+  if (error)
+    *error = error_;
+  return SR_ERROR;
+}
+
+void
+TransformAdapter::Close() {
+  if (!direction_read_ && (state_ == ST_PROCESSING)) {
+    state_ = ST_FLUSHING;
+    do {
+      Write(0, 0, nullptr, nullptr);
+    } while (state_ == ST_FLUSHING);
+  }
+  state_ = ST_COMPLETE;
+  StreamAdapterInterface::Close();
+}
+
+bool TransformAdapter::GetAvailable(size_t* size) const {
+  return false;
+}
+
+bool TransformAdapter::ReserveSize(size_t size) {
+  return true;
+}
+
+bool TransformAdapter::Rewind() {
+  return false;
+}
+
+} // namespace rtc
diff --git a/base/transformadapter.h b/base/transformadapter.h
index 3d9c86b..290d560 100644
--- a/base/transformadapter.h
+++ b/base/transformadapter.h
@@ -8,12 +8,77 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_TRANSFORMADAPTER_H_
-#define WEBRTC_BASE_TRANSFORMADAPTER_H_
+#ifndef WEBRTC_BASE_TRANSFORMADAPTER_H__
+#define WEBRTC_BASE_TRANSFORMADAPTER_H__
 
+#include "webrtc/base/stream.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/transformadapter.h"
+namespace rtc {
+///////////////////////////////////////////////////////////////////////////////
 
-#endif // WEBRTC_BASE_TRANSFORMADAPTER_H_
+class TransformInterface {
+public:
+  virtual ~TransformInterface() { }
+
+  // Transform should convert the in_len bytes of input into the out_len-sized
+  // output buffer.  If flush is true, there will be no more data following
+  // input.
+  // After the transformation, in_len contains the number of bytes consumed, and
+  // out_len contains the number of bytes ready in output.
+  // Note: Transform should not return SR_BLOCK, as there is no asynchronous
+  // notification available.
+  virtual StreamResult Transform(const void * input, size_t * in_len,
+                                 void * output, size_t * out_len,
+                                 bool flush) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// TransformAdapter causes all data passed through to be transformed by the
+// supplied TransformInterface object, which may apply compression, encryption,
+// etc.
+
+class TransformAdapter : public StreamAdapterInterface {
+public:
+  // Note that the transformation is unidirectional, in the direction specified
+  // by the constructor.  Operations in the opposite direction result in SR_EOS.
+  TransformAdapter(StreamInterface * stream,
+                   TransformInterface * transform,
+                   bool direction_read);
+  ~TransformAdapter() override;
+
+  StreamResult Read(void* buffer,
+                    size_t buffer_len,
+                    size_t* read,
+                    int* error) override;
+  StreamResult Write(const void* data,
+                     size_t data_len,
+                     size_t* written,
+                     int* error) override;
+  void Close() override;
+
+  // Apriori, we can't tell what the transformation does to the stream length.
+  bool GetAvailable(size_t* size) const override;
+  bool ReserveSize(size_t size) override;
+
+  // Transformations might not be restartable
+  virtual bool Rewind();
+
+private:
+  enum State { ST_PROCESSING, ST_FLUSHING, ST_COMPLETE, ST_ERROR };
+  enum { BUFFER_SIZE = 1024 };
+
+  TransformInterface * transform_;
+  bool direction_read_;
+  State state_;
+  int error_;
+
+  char buffer_[BUFFER_SIZE];
+  size_t len_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace rtc
+
+#endif // WEBRTC_BASE_TRANSFORMADAPTER_H__
diff --git a/base/type_traits.h b/base/type_traits.h
index 6a4ac8d..a57bead 100644
--- a/base/type_traits.h
+++ b/base/type_traits.h
@@ -11,9 +11,130 @@
 #ifndef WEBRTC_BASE_TYPE_TRAITS_H_
 #define WEBRTC_BASE_TYPE_TRAITS_H_
 
+#include <cstddef>
+#include <type_traits>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/type_traits.h"
+namespace rtc {
+
+// Determines if the given class has zero-argument .data() and .size() methods
+// whose return values are convertible to T* and size_t, respectively.
+template <typename DS, typename T>
+class HasDataAndSize {
+ private:
+  template <
+      typename C,
+      typename std::enable_if<
+          std::is_convertible<decltype(std::declval<C>().data()), T*>::value &&
+          std::is_convertible<decltype(std::declval<C>().size()),
+                              std::size_t>::value>::type* = nullptr>
+  static int Test(int);
+
+  template <typename>
+  static char Test(...);
+
+ public:
+  static constexpr bool value = std::is_same<decltype(Test<DS>(0)), int>::value;
+};
+
+namespace test_has_data_and_size {
+
+template <typename DR, typename SR>
+struct Test1 {
+  DR data();
+  SR size();
+};
+static_assert(HasDataAndSize<Test1<int*, int>, int>::value, "");
+static_assert(HasDataAndSize<Test1<int*, int>, const int>::value, "");
+static_assert(HasDataAndSize<Test1<const int*, int>, const int>::value, "");
+static_assert(!HasDataAndSize<Test1<const int*, int>, int>::value,
+              "implicit cast of const int* to int*");
+static_assert(!HasDataAndSize<Test1<char*, size_t>, int>::value,
+              "implicit cast of char* to int*");
+
+struct Test2 {
+  int* data;
+  size_t size;
+};
+static_assert(!HasDataAndSize<Test2, int>::value,
+              ".data and .size aren't functions");
+
+struct Test3 {
+  int* data();
+};
+static_assert(!HasDataAndSize<Test3, int>::value, ".size() is missing");
+
+class Test4 {
+  int* data();
+  size_t size();
+};
+static_assert(!HasDataAndSize<Test4, int>::value,
+              ".data() and .size() are private");
+
+}  // namespace test_has_data_and_size
+
+namespace type_traits_impl {
+
+// Determines if the given type is an enum that converts implicitly to
+// an integral type.
+template <typename T>
+struct IsIntEnum {
+ private:
+  // This overload is used if the type is an enum, and unary plus
+  // compiles and turns it into an integral type.
+  template <typename X,
+            typename std::enable_if<
+                std::is_enum<X>::value &&
+                std::is_integral<decltype(+std::declval<X>())>::value>::type* =
+                nullptr>
+  static int Test(int);
+
+  // Otherwise, this overload is used.
+  template <typename>
+  static char Test(...);
+
+ public:
+  static constexpr bool value =
+      std::is_same<decltype(Test<typename std::remove_reference<T>::type>(0)),
+                   int>::value;
+};
+
+}  // namespace type_traits_impl
+
+// Determines if the given type is integral, or an enum that
+// converts implicitly to an integral type.
+template <typename T>
+struct IsIntlike {
+ private:
+  using X = typename std::remove_reference<T>::type;
+
+ public:
+  static constexpr bool value =
+      std::is_integral<X>::value || type_traits_impl::IsIntEnum<X>::value;
+};
+
+namespace test_enum_intlike {
+
+enum E1 { e1 };
+enum { e2 };
+enum class E3 { e3 };
+struct S {};
+
+static_assert(type_traits_impl::IsIntEnum<E1>::value, "");
+static_assert(type_traits_impl::IsIntEnum<decltype(e2)>::value, "");
+static_assert(!type_traits_impl::IsIntEnum<E3>::value, "");
+static_assert(!type_traits_impl::IsIntEnum<int>::value, "");
+static_assert(!type_traits_impl::IsIntEnum<float>::value, "");
+static_assert(!type_traits_impl::IsIntEnum<S>::value, "");
+
+static_assert(IsIntlike<E1>::value, "");
+static_assert(IsIntlike<decltype(e2)>::value, "");
+static_assert(!IsIntlike<E3>::value, "");
+static_assert(IsIntlike<int>::value, "");
+static_assert(!IsIntlike<float>::value, "");
+static_assert(!IsIntlike<S>::value, "");
+
+}  // test_enum_intlike
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_TYPE_TRAITS_H_
diff --git a/base/unittest_main.cc b/base/unittest_main.cc
new file mode 100644
index 0000000..2f8f52c
--- /dev/null
+++ b/base/unittest_main.cc
@@ -0,0 +1,129 @@
+/*
+ *  Copyright 2007 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+//
+// A reuseable entry point for gunit tests.
+
+#if defined(WEBRTC_WIN)
+#include <crtdbg.h>
+#endif
+
+#include "webrtc/base/flags.h"
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/ssladapter.h"
+#include "webrtc/base/sslstreamadapter.h"
+#include "webrtc/test/field_trial.h"
+#include "webrtc/test/testsupport/fileutils.h"
+
+#if defined(WEBRTC_IOS)
+#include "webrtc/test/ios/test_support.h"
+#endif
+
+DEFINE_bool(help, false, "prints this message");
+DEFINE_string(log, "", "logging options to use");
+DEFINE_string(
+    force_fieldtrials,
+    "",
+    "Field trials control experimental feature code which can be forced. "
+    "E.g. running with --force_fieldtrials=WebRTC-FooFeature/Enable/"
+    " will assign the group Enable to field trial WebRTC-FooFeature.");
+#if defined(WEBRTC_WIN)
+DEFINE_int(crt_break_alloc, -1, "memory allocation to break on");
+DEFINE_bool(default_error_handlers, false,
+            "leave the default exception/dbg handler functions in place");
+
+void TestInvalidParameterHandler(const wchar_t* expression,
+                                 const wchar_t* function,
+                                 const wchar_t* file,
+                                 unsigned int line,
+                                 uintptr_t pReserved) {
+  LOG(LS_ERROR) << "InvalidParameter Handler called.  Exiting.";
+  LOG(LS_ERROR) << expression << std::endl << function << std::endl << file
+                << std::endl << line;
+  exit(1);
+}
+void TestPureCallHandler() {
+  LOG(LS_ERROR) << "Purecall Handler called.  Exiting.";
+  exit(1);
+}
+int TestCrtReportHandler(int report_type, char* msg, int* retval) {
+    LOG(LS_ERROR) << "CrtReport Handler called...";
+    LOG(LS_ERROR) << msg;
+  if (report_type == _CRT_ASSERT) {
+    exit(1);
+  } else {
+    *retval = 0;
+    return TRUE;
+  }
+}
+#endif  // WEBRTC_WIN
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, false);
+  if (FLAG_help) {
+    rtc::FlagList::Print(nullptr, false);
+    return 0;
+  }
+
+  webrtc::test::SetExecutablePath(argv[0]);
+  webrtc::test::InitFieldTrialsFromString(FLAG_force_fieldtrials);
+
+#if defined(WEBRTC_WIN)
+  if (!FLAG_default_error_handlers) {
+    // Make sure any errors don't throw dialogs hanging the test run.
+    _set_invalid_parameter_handler(TestInvalidParameterHandler);
+    _set_purecall_handler(TestPureCallHandler);
+    _CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, TestCrtReportHandler);
+  }
+
+#if !defined(NDEBUG)  // Turn on memory leak checking on Windows.
+  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF |_CRTDBG_LEAK_CHECK_DF);
+  if (FLAG_crt_break_alloc >= 0) {
+    _crtBreakAlloc = FLAG_crt_break_alloc;
+  }
+#endif
+#endif  // WEBRTC_WIN
+
+  // By default, log timestamps. Allow overrides by used of a --log flag.
+  rtc::LogMessage::LogTimestamps();
+  if (*FLAG_log != '\0') {
+    rtc::LogMessage::ConfigureLogging(FLAG_log);
+  } else if (rtc::LogMessage::GetLogToDebug() > rtc::LS_INFO) {
+    // Default to LS_INFO, even for release builds to provide better test
+    // logging.
+    rtc::LogMessage::LogToDebug(rtc::LS_INFO);
+  }
+
+  // Initialize SSL which are used by several tests.
+  rtc::InitializeSSL();
+  rtc::SSLStreamAdapter::enable_time_callback_for_testing();
+
+#if defined(WEBRTC_IOS)
+  rtc::test::InitTestSuite(RUN_ALL_TESTS, argc, argv);
+  rtc::test::RunTestsFromIOSApp();
+#endif
+  const int res = RUN_ALL_TESTS();
+
+  rtc::CleanupSSL();
+
+  // clean up logging so we don't appear to leak memory.
+  rtc::LogMessage::ConfigureLogging("");
+
+#if defined(WEBRTC_WIN)
+  // Unhook crt function so that we don't ever log after statics have been
+  // uninitialized.
+  if (!FLAG_default_error_handlers)
+    _CrtSetReportHook2(_CRT_RPTHOOK_REMOVE, TestCrtReportHandler);
+#endif
+
+  return res;
+}
diff --git a/base/unixfilesystem.cc b/base/unixfilesystem.cc
new file mode 100644
index 0000000..4901412
--- /dev/null
+++ b/base/unixfilesystem.cc
@@ -0,0 +1,241 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/unixfilesystem.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+#include <CoreServices/CoreServices.h>
+#include <IOKit/IOCFBundle.h>
+#include <sys/statvfs.h>
+#include "webrtc/base/macutils.h"
+#endif  // WEBRTC_MAC && !defined(WEBRTC_IOS)
+
+#if defined(WEBRTC_POSIX) && !defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
+#include <sys/types.h>
+#if defined(WEBRTC_ANDROID)
+#include <sys/statfs.h>
+#elif !defined(__native_client__)
+#include <sys/statvfs.h>
+#endif  //  !defined(__native_client__)
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#endif  // WEBRTC_POSIX && !WEBRTC_MAC || WEBRTC_IOS
+
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+#include <ctype.h>
+#include <algorithm>
+#endif
+
+#if defined(__native_client__) && !defined(__GLIBC__)
+#include <sys/syslimits.h>
+#endif
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringutils.h"
+
+#if defined(WEBRTC_MAC)
+// Defined in applefilesystem.mm.  No header file to discourage use
+// elsewhere; other places should use GetApp{Data,Temp}Folder() in
+// this file.  Don't copy/paste.  I mean it.
+char* AppleDataDirectory();
+char* AppleTempDirectory();
+void AppleAppName(rtc::Pathname* path);
+#endif
+
+namespace rtc {
+
+#if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_MAC)
+char* UnixFilesystem::app_temp_path_ = nullptr;
+#else
+char* UnixFilesystem::provided_app_data_folder_ = nullptr;
+char* UnixFilesystem::provided_app_temp_folder_ = nullptr;
+
+void UnixFilesystem::SetAppDataFolder(const std::string& folder) {
+  delete [] provided_app_data_folder_;
+  provided_app_data_folder_ = CopyString(folder);
+}
+
+void UnixFilesystem::SetAppTempFolder(const std::string& folder) {
+  delete [] provided_app_temp_folder_;
+  provided_app_temp_folder_ = CopyString(folder);
+}
+#endif
+
+UnixFilesystem::UnixFilesystem() {
+#if defined(WEBRTC_MAC)
+  if (!provided_app_data_folder_)
+    provided_app_data_folder_ = AppleDataDirectory();
+  if (!provided_app_temp_folder_)
+    provided_app_temp_folder_ = AppleTempDirectory();
+#endif
+}
+
+UnixFilesystem::~UnixFilesystem() {}
+
+bool UnixFilesystem::CreateFolder(const Pathname &path, mode_t mode) {
+  std::string pathname(path.pathname());
+  int len = pathname.length();
+  if ((len == 0) || (pathname[len - 1] != '/'))
+    return false;
+
+  struct stat st;
+  int res = ::stat(pathname.c_str(), &st);
+  if (res == 0) {
+    // Something exists at this location, check if it is a directory
+    return S_ISDIR(st.st_mode) != 0;
+  } else if (errno != ENOENT) {
+    // Unexpected error
+    return false;
+  }
+
+  // Directory doesn't exist, look up one directory level
+  do {
+    --len;
+  } while ((len > 0) && (pathname[len - 1] != '/'));
+
+  if (!CreateFolder(Pathname(pathname.substr(0, len)), mode)) {
+    return false;
+  }
+
+  LOG(LS_INFO) << "Creating folder: " << pathname;
+  return (0 == ::mkdir(pathname.c_str(), mode));
+}
+
+bool UnixFilesystem::CreateFolder(const Pathname &path) {
+  return CreateFolder(path, 0755);
+}
+
+bool UnixFilesystem::DeleteFile(const Pathname &filename) {
+  LOG(LS_INFO) << "Deleting file:" << filename.pathname();
+
+  if (!IsFile(filename)) {
+    RTC_DCHECK(IsFile(filename));
+    return false;
+  }
+  return ::unlink(filename.pathname().c_str()) == 0;
+}
+
+bool UnixFilesystem::GetTemporaryFolder(Pathname &pathname, bool create,
+                                        const std::string *append) {
+#if defined(WEBRTC_ANDROID) || defined(WEBRTC_MAC)
+  RTC_DCHECK(provided_app_temp_folder_ != nullptr);
+  pathname.SetPathname(provided_app_temp_folder_, "");
+#else
+  if (const char* tmpdir = getenv("TMPDIR")) {
+    pathname.SetPathname(tmpdir, "");
+  } else if (const char* tmp = getenv("TMP")) {
+    pathname.SetPathname(tmp, "");
+  } else {
+#ifdef P_tmpdir
+    pathname.SetPathname(P_tmpdir, "");
+#else  // !P_tmpdir
+    pathname.SetPathname("/tmp/", "");
+#endif  // !P_tmpdir
+  }
+#endif  // defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)
+  if (append) {
+    RTC_DCHECK(!append->empty());
+    pathname.AppendFolder(*append);
+  }
+  return !create || CreateFolder(pathname);
+}
+
+std::string UnixFilesystem::TempFilename(const Pathname &dir,
+                                         const std::string &prefix) {
+  int len = dir.pathname().size() + prefix.size() + 2 + 6;
+  char *tempname = new char[len];
+
+  snprintf(tempname, len, "%s/%sXXXXXX", dir.pathname().c_str(),
+           prefix.c_str());
+  int fd = ::mkstemp(tempname);
+  if (fd != -1)
+    ::close(fd);
+  std::string ret(tempname);
+  delete[] tempname;
+
+  return ret;
+}
+
+bool UnixFilesystem::MoveFile(const Pathname &old_path,
+                              const Pathname &new_path) {
+  if (!IsFile(old_path)) {
+    RTC_DCHECK(IsFile(old_path));
+    return false;
+  }
+  LOG(LS_VERBOSE) << "Moving " << old_path.pathname()
+                  << " to " << new_path.pathname();
+  if (rename(old_path.pathname().c_str(), new_path.pathname().c_str()) != 0) {
+    return false;
+  }
+  return true;
+}
+
+bool UnixFilesystem::IsFolder(const Pathname &path) {
+  struct stat st;
+  if (stat(path.pathname().c_str(), &st) < 0)
+    return false;
+  return S_ISDIR(st.st_mode);
+}
+
+bool UnixFilesystem::IsFile(const Pathname& pathname) {
+  struct stat st;
+  int res = ::stat(pathname.pathname().c_str(), &st);
+  // Treat symlinks, named pipes, etc. all as files.
+  return res == 0 && !S_ISDIR(st.st_mode);
+}
+
+bool UnixFilesystem::IsAbsent(const Pathname& pathname) {
+  struct stat st;
+  int res = ::stat(pathname.pathname().c_str(), &st);
+  // Note: we specifically maintain ENOTDIR as an error, because that implies
+  // that you could not call CreateFolder(pathname).
+  return res != 0 && ENOENT == errno;
+}
+
+bool UnixFilesystem::GetFileSize(const Pathname& pathname, size_t *size) {
+  struct stat st;
+  if (::stat(pathname.pathname().c_str(), &st) != 0)
+    return false;
+  *size = st.st_size;
+  return true;
+}
+
+char* UnixFilesystem::CopyString(const std::string& str) {
+  size_t size = str.length() + 1;
+
+  char* buf = new char[size];
+  if (!buf) {
+    return nullptr;
+  }
+
+  strcpyn(buf, size, str.c_str());
+  return buf;
+}
+
+}  // namespace rtc
+
+#if defined(__native_client__)
+extern "C" int __attribute__((weak))
+link(const char* oldpath, const char* newpath) {
+  errno = EACCES;
+  return -1;
+}
+#endif
diff --git a/base/unixfilesystem.h b/base/unixfilesystem.h
index 7a18205..742f4c2 100644
--- a/base/unixfilesystem.h
+++ b/base/unixfilesystem.h
@@ -11,9 +11,79 @@
 #ifndef WEBRTC_BASE_UNIXFILESYSTEM_H_
 #define WEBRTC_BASE_UNIXFILESYSTEM_H_
 
+#include <sys/types.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/unixfilesystem.h"
+#include "webrtc/base/fileutils.h"
+
+namespace rtc {
+
+class UnixFilesystem : public FilesystemInterface {
+ public:
+  UnixFilesystem();
+  ~UnixFilesystem() override;
+
+#if defined(WEBRTC_ANDROID) || defined(WEBRTC_MAC)
+  // Android does not have a native code API to fetch the app data or temp
+  // folders. That needs to be passed into this class from Java. Similarly, iOS
+  // only supports an Objective-C API for fetching the folder locations, so that
+  // needs to be passed in here from Objective-C.  Or at least that used to be
+  // the case; now the ctor will do the work if necessary and possible.
+  // TODO(fischman): add an Android version that uses JNI and drop the
+  // SetApp*Folder() APIs once external users stop using them.
+  static void SetAppDataFolder(const std::string& folder);
+  static void SetAppTempFolder(const std::string& folder);
+#endif
+
+  // This will attempt to delete the file located at filename.
+  // It will fail with VERIY if you pass it a non-existant file, or a directory.
+  bool DeleteFile(const Pathname& filename) override;
+
+  // Creates a directory. This will call itself recursively to create /foo/bar
+  // even if /foo does not exist. All created directories are created with the
+  // given mode.
+  // Returns TRUE if function succeeds
+  virtual bool CreateFolder(const Pathname &pathname, mode_t mode);
+
+  // As above, with mode = 0755.
+  bool CreateFolder(const Pathname& pathname) override;
+
+  // This moves a file from old_path to new_path, where "file" can be a plain
+  // file or directory, which will be moved recursively.
+  // Returns true if function succeeds.
+  bool MoveFile(const Pathname& old_path, const Pathname& new_path) override;
+
+  // Returns true if a pathname is a directory
+  bool IsFolder(const Pathname& pathname) override;
+
+  // Returns true of pathname represents an existing file
+  bool IsFile(const Pathname& pathname) override;
+
+  // Returns true if pathname refers to no filesystem object, every parent
+  // directory either exists, or is also absent.
+  bool IsAbsent(const Pathname& pathname) override;
+
+  std::string TempFilename(const Pathname& dir,
+                           const std::string& prefix) override;
+
+  // A folder appropriate for storing temporary files (Contents are
+  // automatically deleted when the program exists)
+  bool GetTemporaryFolder(Pathname& path,
+                          bool create,
+                          const std::string* append) override;
+
+  bool GetFileSize(const Pathname& path, size_t* size) override;
+
+ private:
+#if defined(WEBRTC_ANDROID) || defined(WEBRTC_MAC)
+  static char* provided_app_data_folder_;
+  static char* provided_app_temp_folder_;
+#else
+  static char* app_temp_path_;
+#endif
+
+  static char* CopyString(const std::string& str);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_UNIXFILESYSTEM_H_
diff --git a/base/virtualsocket_unittest.cc b/base/virtualsocket_unittest.cc
new file mode 100644
index 0000000..34ee036
--- /dev/null
+++ b/base/virtualsocket_unittest.cc
@@ -0,0 +1,1146 @@
+/*
+ *  Copyright 2006 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <math.h>
+#include <time.h>
+#if defined(WEBRTC_POSIX)
+#include <netinet/in.h>
+#endif
+
+#include <memory>
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/fakeclock.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/ptr_util.h"
+#include "webrtc/base/testclient.h"
+#include "webrtc/base/testutils.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/base/virtualsocketserver.h"
+
+using namespace rtc;
+
+using webrtc::testing::SSE_CLOSE;
+using webrtc::testing::SSE_ERROR;
+using webrtc::testing::SSE_OPEN;
+using webrtc::testing::SSE_READ;
+using webrtc::testing::SSE_WRITE;
+using webrtc::testing::StreamSink;
+
+// Sends at a constant rate but with random packet sizes.
+struct Sender : public MessageHandler {
+  Sender(Thread* th, AsyncSocket* s, uint32_t rt)
+      : thread(th),
+        socket(MakeUnique<AsyncUDPSocket>(s)),
+        done(false),
+        rate(rt),
+        count(0) {
+    last_send = rtc::TimeMillis();
+    thread->PostDelayed(RTC_FROM_HERE, NextDelay(), this, 1);
+  }
+
+  uint32_t NextDelay() {
+    uint32_t size = (rand() % 4096) + 1;
+    return 1000 * size / rate;
+  }
+
+  void OnMessage(Message* pmsg) {
+    ASSERT_EQ(1u, pmsg->message_id);
+
+    if (done)
+      return;
+
+    int64_t cur_time = rtc::TimeMillis();
+    int64_t delay = cur_time - last_send;
+    uint32_t size = static_cast<uint32_t>(rate * delay / 1000);
+    size = std::min<uint32_t>(size, 4096);
+    size = std::max<uint32_t>(size, sizeof(uint32_t));
+
+    count += size;
+    memcpy(dummy, &cur_time, sizeof(cur_time));
+    socket->Send(dummy, size, options);
+
+    last_send = cur_time;
+    thread->PostDelayed(RTC_FROM_HERE, NextDelay(), this, 1);
+  }
+
+  Thread* thread;
+  std::unique_ptr<AsyncUDPSocket> socket;
+  rtc::PacketOptions options;
+  bool done;
+  uint32_t rate;  // bytes per second
+  uint32_t count;
+  int64_t last_send;
+  char dummy[4096];
+};
+
+struct Receiver : public MessageHandler, public sigslot::has_slots<> {
+  Receiver(Thread* th, AsyncSocket* s, uint32_t bw)
+      : thread(th),
+        socket(MakeUnique<AsyncUDPSocket>(s)),
+        bandwidth(bw),
+        done(false),
+        count(0),
+        sec_count(0),
+        sum(0),
+        sum_sq(0),
+        samples(0) {
+    socket->SignalReadPacket.connect(this, &Receiver::OnReadPacket);
+    thread->PostDelayed(RTC_FROM_HERE, 1000, this, 1);
+  }
+
+  ~Receiver() {
+    thread->Clear(this);
+  }
+
+  void OnReadPacket(AsyncPacketSocket* s, const char* data, size_t size,
+                    const SocketAddress& remote_addr,
+                    const PacketTime& packet_time) {
+    ASSERT_EQ(socket.get(), s);
+    ASSERT_GE(size, 4U);
+
+    count += size;
+    sec_count += size;
+
+    uint32_t send_time = *reinterpret_cast<const uint32_t*>(data);
+    uint32_t recv_time = rtc::TimeMillis();
+    uint32_t delay = recv_time - send_time;
+    sum += delay;
+    sum_sq += delay * delay;
+    samples += 1;
+  }
+
+  void OnMessage(Message* pmsg) {
+    ASSERT_EQ(1u, pmsg->message_id);
+
+    if (done)
+      return;
+
+    // It is always possible for us to receive more than expected because
+    // packets can be further delayed in delivery.
+    if (bandwidth > 0)
+      ASSERT_TRUE(sec_count <= 5 * bandwidth / 4);
+    sec_count = 0;
+    thread->PostDelayed(RTC_FROM_HERE, 1000, this, 1);
+  }
+
+  Thread* thread;
+  std::unique_ptr<AsyncUDPSocket> socket;
+  uint32_t bandwidth;
+  bool done;
+  size_t count;
+  size_t sec_count;
+  double sum;
+  double sum_sq;
+  uint32_t samples;
+};
+
+// Note: This test uses a fake clock in addition to a virtual network.
+class VirtualSocketServerTest : public testing::Test {
+ public:
+  VirtualSocketServerTest()
+      : ss_(&fake_clock_),
+        thread_(&ss_),
+        kIPv4AnyAddress(IPAddress(INADDR_ANY), 0),
+        kIPv6AnyAddress(IPAddress(in6addr_any), 0) {}
+
+  void CheckPortIncrementalization(const SocketAddress& post,
+                                   const SocketAddress& pre) {
+    EXPECT_EQ(post.port(), pre.port() + 1);
+    IPAddress post_ip = post.ipaddr();
+    IPAddress pre_ip = pre.ipaddr();
+    EXPECT_EQ(pre_ip.family(), post_ip.family());
+    if (post_ip.family() == AF_INET) {
+      in_addr pre_ipv4 = pre_ip.ipv4_address();
+      in_addr post_ipv4 = post_ip.ipv4_address();
+      EXPECT_EQ(post_ipv4.s_addr, pre_ipv4.s_addr);
+    } else if (post_ip.family() == AF_INET6) {
+      in6_addr post_ip6 = post_ip.ipv6_address();
+      in6_addr pre_ip6 = pre_ip.ipv6_address();
+      uint32_t* post_as_ints = reinterpret_cast<uint32_t*>(&post_ip6.s6_addr);
+      uint32_t* pre_as_ints = reinterpret_cast<uint32_t*>(&pre_ip6.s6_addr);
+      EXPECT_EQ(post_as_ints[3], pre_as_ints[3]);
+    }
+  }
+
+  // Test a client can bind to the any address, and all sent packets will have
+  // the default route as the source address. Also, it can receive packets sent
+  // to the default route.
+  void TestDefaultRoute(const IPAddress& default_route) {
+    ss_.SetDefaultRoute(default_route);
+
+    // Create client1 bound to the any address.
+    AsyncSocket* socket =
+        ss_.CreateAsyncSocket(default_route.family(), SOCK_DGRAM);
+    socket->Bind(EmptySocketAddressWithFamily(default_route.family()));
+    SocketAddress client1_any_addr = socket->GetLocalAddress();
+    EXPECT_TRUE(client1_any_addr.IsAnyIP());
+    auto client1 = MakeUnique<TestClient>(MakeUnique<AsyncUDPSocket>(socket),
+                                          &fake_clock_);
+
+    // Create client2 bound to the default route.
+    AsyncSocket* socket2 =
+        ss_.CreateAsyncSocket(default_route.family(), SOCK_DGRAM);
+    socket2->Bind(SocketAddress(default_route, 0));
+    SocketAddress client2_addr = socket2->GetLocalAddress();
+    EXPECT_FALSE(client2_addr.IsAnyIP());
+    auto client2 = MakeUnique<TestClient>(MakeUnique<AsyncUDPSocket>(socket2),
+                                          &fake_clock_);
+
+    // Client1 sends to client2, client2 should see the default route as
+    // client1's address.
+    SocketAddress client1_addr;
+    EXPECT_EQ(6, client1->SendTo("bizbaz", 6, client2_addr));
+    EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &client1_addr));
+    EXPECT_EQ(client1_addr,
+              SocketAddress(default_route, client1_any_addr.port()));
+
+    // Client2 can send back to client1's default route address.
+    EXPECT_EQ(3, client2->SendTo("foo", 3, client1_addr));
+    EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &client2_addr));
+  }
+
+  void BasicTest(const SocketAddress& initial_addr) {
+    AsyncSocket* socket =
+        ss_.CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM);
+    socket->Bind(initial_addr);
+    SocketAddress server_addr = socket->GetLocalAddress();
+    // Make sure VSS didn't switch families on us.
+    EXPECT_EQ(server_addr.family(), initial_addr.family());
+
+    auto client1 = MakeUnique<TestClient>(MakeUnique<AsyncUDPSocket>(socket),
+                                          &fake_clock_);
+    AsyncSocket* socket2 =
+        ss_.CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM);
+    auto client2 = MakeUnique<TestClient>(MakeUnique<AsyncUDPSocket>(socket2),
+                                          &fake_clock_);
+
+    SocketAddress client2_addr;
+    EXPECT_EQ(3, client2->SendTo("foo", 3, server_addr));
+    EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &client2_addr));
+
+    SocketAddress client1_addr;
+    EXPECT_EQ(6, client1->SendTo("bizbaz", 6, client2_addr));
+    EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &client1_addr));
+    EXPECT_EQ(client1_addr, server_addr);
+
+    SocketAddress empty = EmptySocketAddressWithFamily(initial_addr.family());
+    for (int i = 0; i < 10; i++) {
+      client2 = MakeUnique<TestClient>(
+          WrapUnique(AsyncUDPSocket::Create(&ss_, empty)), &fake_clock_);
+
+      SocketAddress next_client2_addr;
+      EXPECT_EQ(3, client2->SendTo("foo", 3, server_addr));
+      EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &next_client2_addr));
+      CheckPortIncrementalization(next_client2_addr, client2_addr);
+      // EXPECT_EQ(next_client2_addr.port(), client2_addr.port() + 1);
+
+      SocketAddress server_addr2;
+      EXPECT_EQ(6, client1->SendTo("bizbaz", 6, next_client2_addr));
+      EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &server_addr2));
+      EXPECT_EQ(server_addr2, server_addr);
+
+      client2_addr = next_client2_addr;
+    }
+  }
+
+  // initial_addr should be made from either INADDR_ANY or in6addr_any.
+  void ConnectTest(const SocketAddress& initial_addr) {
+    StreamSink sink;
+    SocketAddress accept_addr;
+    const SocketAddress kEmptyAddr =
+        EmptySocketAddressWithFamily(initial_addr.family());
+
+    // Create client
+    std::unique_ptr<AsyncSocket> client =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(client.get());
+    EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED);
+    EXPECT_TRUE(client->GetLocalAddress().IsNil());
+
+    // Create server
+    std::unique_ptr<AsyncSocket> server =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(server.get());
+    EXPECT_NE(0, server->Listen(5));  // Bind required
+    EXPECT_EQ(0, server->Bind(initial_addr));
+    EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family());
+    EXPECT_EQ(0, server->Listen(5));
+    EXPECT_EQ(server->GetState(), AsyncSocket::CS_CONNECTING);
+
+    // No pending server connections
+    EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
+    EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
+    EXPECT_EQ(AF_UNSPEC, accept_addr.family());
+
+    // Attempt connect to listening socket
+    EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+    EXPECT_NE(client->GetLocalAddress(), kEmptyAddr);  // Implicit Bind
+    EXPECT_NE(AF_UNSPEC, client->GetLocalAddress().family());  // Implicit Bind
+    EXPECT_NE(client->GetLocalAddress(), server->GetLocalAddress());
+
+    // Client is connecting
+    EXPECT_EQ(client->GetState(), AsyncSocket::CS_CONNECTING);
+    EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
+    EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+
+    ss_.ProcessMessagesUntilIdle();
+
+    // Client still connecting
+    EXPECT_EQ(client->GetState(), AsyncSocket::CS_CONNECTING);
+    EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
+    EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+
+    // Server has pending connection
+    EXPECT_TRUE(sink.Check(server.get(), SSE_READ));
+    std::unique_ptr<Socket> accepted = WrapUnique(server->Accept(&accept_addr));
+    EXPECT_TRUE(nullptr != accepted);
+    EXPECT_NE(accept_addr, kEmptyAddr);
+    EXPECT_EQ(accepted->GetRemoteAddress(), accept_addr);
+
+    EXPECT_EQ(accepted->GetState(), AsyncSocket::CS_CONNECTED);
+    EXPECT_EQ(accepted->GetLocalAddress(), server->GetLocalAddress());
+    EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress());
+
+    ss_.ProcessMessagesUntilIdle();
+
+    // Client has connected
+    EXPECT_EQ(client->GetState(), AsyncSocket::CS_CONNECTED);
+    EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
+    EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+    EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
+    EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+  }
+
+  void ConnectToNonListenerTest(const SocketAddress& initial_addr) {
+    StreamSink sink;
+    SocketAddress accept_addr;
+    const SocketAddress nil_addr;
+    const SocketAddress empty_addr =
+        EmptySocketAddressWithFamily(initial_addr.family());
+
+    // Create client
+    std::unique_ptr<AsyncSocket> client =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(client.get());
+
+    // Create server
+    std::unique_ptr<AsyncSocket> server =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(server.get());
+    EXPECT_EQ(0, server->Bind(initial_addr));
+    EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family());
+    // Attempt connect to non-listening socket
+    EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+    ss_.ProcessMessagesUntilIdle();
+
+    // No pending server connections
+    EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
+    EXPECT_TRUE(nullptr == server->Accept(&accept_addr));
+    EXPECT_EQ(accept_addr, nil_addr);
+
+    // Connection failed
+    EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED);
+    EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
+    EXPECT_TRUE(sink.Check(client.get(), SSE_ERROR));
+    EXPECT_EQ(client->GetRemoteAddress(), nil_addr);
+  }
+
+  void CloseDuringConnectTest(const SocketAddress& initial_addr) {
+    StreamSink sink;
+    SocketAddress accept_addr;
+    const SocketAddress empty_addr =
+        EmptySocketAddressWithFamily(initial_addr.family());
+
+    // Create client and server
+    std::unique_ptr<AsyncSocket> client(
+        ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(client.get());
+    std::unique_ptr<AsyncSocket> server(
+        ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(server.get());
+
+    // Initiate connect
+    EXPECT_EQ(0, server->Bind(initial_addr));
+    EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family());
+
+    EXPECT_EQ(0, server->Listen(5));
+    EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+    // Server close before socket enters accept queue
+    EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
+    server->Close();
+
+    ss_.ProcessMessagesUntilIdle();
+
+    // Result: connection failed
+    EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED);
+    EXPECT_TRUE(sink.Check(client.get(), SSE_ERROR));
+
+    server.reset(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(server.get());
+
+    // Initiate connect
+    EXPECT_EQ(0, server->Bind(initial_addr));
+    EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family());
+
+    EXPECT_EQ(0, server->Listen(5));
+    EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+    ss_.ProcessMessagesUntilIdle();
+
+    // Server close while socket is in accept queue
+    EXPECT_TRUE(sink.Check(server.get(), SSE_READ));
+    server->Close();
+
+    ss_.ProcessMessagesUntilIdle();
+
+    // Result: connection failed
+    EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED);
+    EXPECT_TRUE(sink.Check(client.get(), SSE_ERROR));
+
+    // New server
+    server.reset(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(server.get());
+
+    // Initiate connect
+    EXPECT_EQ(0, server->Bind(initial_addr));
+    EXPECT_EQ(server->GetLocalAddress().family(), initial_addr.family());
+
+    EXPECT_EQ(0, server->Listen(5));
+    EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+    ss_.ProcessMessagesUntilIdle();
+
+    // Server accepts connection
+    EXPECT_TRUE(sink.Check(server.get(), SSE_READ));
+    std::unique_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+    ASSERT_TRUE(nullptr != accepted.get());
+    sink.Monitor(accepted.get());
+
+    // Client closes before connection complets
+    EXPECT_EQ(accepted->GetState(), AsyncSocket::CS_CONNECTED);
+
+    // Connected message has not been processed yet.
+    EXPECT_EQ(client->GetState(), AsyncSocket::CS_CONNECTING);
+    client->Close();
+
+    ss_.ProcessMessagesUntilIdle();
+
+    // Result: accepted socket closes
+    EXPECT_EQ(accepted->GetState(), AsyncSocket::CS_CLOSED);
+    EXPECT_TRUE(sink.Check(accepted.get(), SSE_CLOSE));
+    EXPECT_FALSE(sink.Check(client.get(), SSE_CLOSE));
+  }
+
+  void CloseTest(const SocketAddress& initial_addr) {
+    StreamSink sink;
+    const SocketAddress kEmptyAddr;
+
+    // Create clients
+    std::unique_ptr<AsyncSocket> a =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(a.get());
+    a->Bind(initial_addr);
+    EXPECT_EQ(a->GetLocalAddress().family(), initial_addr.family());
+
+    std::unique_ptr<AsyncSocket> b =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(b.get());
+    b->Bind(initial_addr);
+    EXPECT_EQ(b->GetLocalAddress().family(), initial_addr.family());
+
+    EXPECT_EQ(0, a->Connect(b->GetLocalAddress()));
+    EXPECT_EQ(0, b->Connect(a->GetLocalAddress()));
+
+    ss_.ProcessMessagesUntilIdle();
+
+    EXPECT_TRUE(sink.Check(a.get(), SSE_OPEN));
+    EXPECT_EQ(a->GetState(), AsyncSocket::CS_CONNECTED);
+    EXPECT_EQ(a->GetRemoteAddress(), b->GetLocalAddress());
+
+    EXPECT_TRUE(sink.Check(b.get(), SSE_OPEN));
+    EXPECT_EQ(b->GetState(), AsyncSocket::CS_CONNECTED);
+    EXPECT_EQ(b->GetRemoteAddress(), a->GetLocalAddress());
+
+    EXPECT_EQ(1, a->Send("a", 1));
+    b->Close();
+    EXPECT_EQ(1, a->Send("b", 1));
+
+    ss_.ProcessMessagesUntilIdle();
+
+    char buffer[10];
+    EXPECT_FALSE(sink.Check(b.get(), SSE_READ));
+    EXPECT_EQ(-1, b->Recv(buffer, 10, nullptr));
+
+    EXPECT_TRUE(sink.Check(a.get(), SSE_CLOSE));
+    EXPECT_EQ(a->GetState(), AsyncSocket::CS_CLOSED);
+    EXPECT_EQ(a->GetRemoteAddress(), kEmptyAddr);
+
+    // No signal for Closer
+    EXPECT_FALSE(sink.Check(b.get(), SSE_CLOSE));
+    EXPECT_EQ(b->GetState(), AsyncSocket::CS_CLOSED);
+    EXPECT_EQ(b->GetRemoteAddress(), kEmptyAddr);
+  }
+
+  void TcpSendTest(const SocketAddress& initial_addr) {
+    StreamSink sink;
+    const SocketAddress kEmptyAddr;
+
+    // Connect two sockets
+    std::unique_ptr<AsyncSocket> a =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(a.get());
+    a->Bind(initial_addr);
+    EXPECT_EQ(a->GetLocalAddress().family(), initial_addr.family());
+
+    std::unique_ptr<AsyncSocket> b =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    sink.Monitor(b.get());
+    b->Bind(initial_addr);
+    EXPECT_EQ(b->GetLocalAddress().family(), initial_addr.family());
+
+    EXPECT_EQ(0, a->Connect(b->GetLocalAddress()));
+    EXPECT_EQ(0, b->Connect(a->GetLocalAddress()));
+
+    ss_.ProcessMessagesUntilIdle();
+
+    const size_t kBufferSize = 2000;
+    ss_.set_send_buffer_capacity(kBufferSize);
+    ss_.set_recv_buffer_capacity(kBufferSize);
+
+    const size_t kDataSize = 5000;
+    char send_buffer[kDataSize], recv_buffer[kDataSize];
+    for (size_t i = 0; i < kDataSize; ++i)
+      send_buffer[i] = static_cast<char>(i % 256);
+    memset(recv_buffer, 0, sizeof(recv_buffer));
+    size_t send_pos = 0, recv_pos = 0;
+
+    // Can't send more than send buffer in one write
+    int result = a->Send(send_buffer + send_pos, kDataSize - send_pos);
+    EXPECT_EQ(static_cast<int>(kBufferSize), result);
+    send_pos += result;
+
+    ss_.ProcessMessagesUntilIdle();
+    EXPECT_FALSE(sink.Check(a.get(), SSE_WRITE));
+    EXPECT_TRUE(sink.Check(b.get(), SSE_READ));
+
+    // Receive buffer is already filled, fill send buffer again
+    result = a->Send(send_buffer + send_pos, kDataSize - send_pos);
+    EXPECT_EQ(static_cast<int>(kBufferSize), result);
+    send_pos += result;
+
+    ss_.ProcessMessagesUntilIdle();
+    EXPECT_FALSE(sink.Check(a.get(), SSE_WRITE));
+    EXPECT_FALSE(sink.Check(b.get(), SSE_READ));
+
+    // No more room in send or receive buffer
+    result = a->Send(send_buffer + send_pos, kDataSize - send_pos);
+    EXPECT_EQ(-1, result);
+    EXPECT_TRUE(a->IsBlocking());
+
+    // Read a subset of the data
+    result = b->Recv(recv_buffer + recv_pos, 500, nullptr);
+    EXPECT_EQ(500, result);
+    recv_pos += result;
+
+    ss_.ProcessMessagesUntilIdle();
+    EXPECT_TRUE(sink.Check(a.get(), SSE_WRITE));
+    EXPECT_TRUE(sink.Check(b.get(), SSE_READ));
+
+    // Room for more on the sending side
+    result = a->Send(send_buffer + send_pos, kDataSize - send_pos);
+    EXPECT_EQ(500, result);
+    send_pos += result;
+
+    // Empty the recv buffer
+    while (true) {
+      result = b->Recv(recv_buffer + recv_pos, kDataSize - recv_pos, nullptr);
+      if (result < 0) {
+        EXPECT_EQ(-1, result);
+        EXPECT_TRUE(b->IsBlocking());
+        break;
+      }
+      recv_pos += result;
+    }
+
+    ss_.ProcessMessagesUntilIdle();
+    EXPECT_TRUE(sink.Check(b.get(), SSE_READ));
+
+    // Continue to empty the recv buffer
+    while (true) {
+      result = b->Recv(recv_buffer + recv_pos, kDataSize - recv_pos, nullptr);
+      if (result < 0) {
+        EXPECT_EQ(-1, result);
+        EXPECT_TRUE(b->IsBlocking());
+        break;
+      }
+      recv_pos += result;
+    }
+
+    // Send last of the data
+    result = a->Send(send_buffer + send_pos, kDataSize - send_pos);
+    EXPECT_EQ(500, result);
+    send_pos += result;
+
+    ss_.ProcessMessagesUntilIdle();
+    EXPECT_TRUE(sink.Check(b.get(), SSE_READ));
+
+    // Receive the last of the data
+    while (true) {
+      result = b->Recv(recv_buffer + recv_pos, kDataSize - recv_pos, nullptr);
+      if (result < 0) {
+        EXPECT_EQ(-1, result);
+        EXPECT_TRUE(b->IsBlocking());
+        break;
+      }
+      recv_pos += result;
+    }
+
+    ss_.ProcessMessagesUntilIdle();
+    EXPECT_FALSE(sink.Check(b.get(), SSE_READ));
+
+    // The received data matches the sent data
+    EXPECT_EQ(kDataSize, send_pos);
+    EXPECT_EQ(kDataSize, recv_pos);
+    EXPECT_EQ(0, memcmp(recv_buffer, send_buffer, kDataSize));
+  }
+
+  void TcpSendsPacketsInOrderTest(const SocketAddress& initial_addr) {
+    const SocketAddress kEmptyAddr;
+
+    // Connect two sockets
+    std::unique_ptr<AsyncSocket> a =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    std::unique_ptr<AsyncSocket> b =
+        WrapUnique(ss_.CreateAsyncSocket(initial_addr.family(), SOCK_STREAM));
+    a->Bind(initial_addr);
+    EXPECT_EQ(a->GetLocalAddress().family(), initial_addr.family());
+
+    b->Bind(initial_addr);
+    EXPECT_EQ(b->GetLocalAddress().family(), initial_addr.family());
+
+    EXPECT_EQ(0, a->Connect(b->GetLocalAddress()));
+    EXPECT_EQ(0, b->Connect(a->GetLocalAddress()));
+    ss_.ProcessMessagesUntilIdle();
+
+    // First, deliver all packets in 0 ms.
+    char buffer[2] = { 0, 0 };
+    const char cNumPackets = 10;
+    for (char i = 0; i < cNumPackets; ++i) {
+      buffer[0] = '0' + i;
+      EXPECT_EQ(1, a->Send(buffer, 1));
+    }
+
+    ss_.ProcessMessagesUntilIdle();
+
+    for (char i = 0; i < cNumPackets; ++i) {
+      EXPECT_EQ(1, b->Recv(buffer, sizeof(buffer), nullptr));
+      EXPECT_EQ(static_cast<char>('0' + i), buffer[0]);
+    }
+
+    // Next, deliver packets at random intervals
+    const uint32_t mean = 50;
+    const uint32_t stddev = 50;
+
+    ss_.set_delay_mean(mean);
+    ss_.set_delay_stddev(stddev);
+    ss_.UpdateDelayDistribution();
+
+    for (char i = 0; i < cNumPackets; ++i) {
+      buffer[0] = 'A' + i;
+      EXPECT_EQ(1, a->Send(buffer, 1));
+    }
+
+    ss_.ProcessMessagesUntilIdle();
+
+    for (char i = 0; i < cNumPackets; ++i) {
+      EXPECT_EQ(1, b->Recv(buffer, sizeof(buffer), nullptr));
+      EXPECT_EQ(static_cast<char>('A' + i), buffer[0]);
+    }
+  }
+
+  // It is important that initial_addr's port has to be 0 such that the
+  // incremental port behavior could ensure the 2 Binds result in different
+  // address.
+  void BandwidthTest(const SocketAddress& initial_addr) {
+    AsyncSocket* send_socket =
+        ss_.CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM);
+    AsyncSocket* recv_socket =
+        ss_.CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM);
+    ASSERT_EQ(0, send_socket->Bind(initial_addr));
+    ASSERT_EQ(0, recv_socket->Bind(initial_addr));
+    EXPECT_EQ(send_socket->GetLocalAddress().family(), initial_addr.family());
+    EXPECT_EQ(recv_socket->GetLocalAddress().family(), initial_addr.family());
+    ASSERT_EQ(0, send_socket->Connect(recv_socket->GetLocalAddress()));
+
+    uint32_t bandwidth = 64 * 1024;
+    ss_.set_bandwidth(bandwidth);
+
+    Thread* pthMain = Thread::Current();
+    Sender sender(pthMain, send_socket, 80 * 1024);
+    Receiver receiver(pthMain, recv_socket, bandwidth);
+
+    // Allow the sender to run for 5 (simulated) seconds, then be stopped for 5
+    // seconds.
+    SIMULATED_WAIT(false, 5000, fake_clock_);
+    sender.done = true;
+    SIMULATED_WAIT(false, 5000, fake_clock_);
+
+    // Ensure the observed bandwidth fell within a reasonable margin of error.
+    EXPECT_TRUE(receiver.count >= 5 * 3 * bandwidth / 4);
+    EXPECT_TRUE(receiver.count <= 6 * bandwidth);  // queue could drain for 1s
+
+    ss_.set_bandwidth(0);
+  }
+
+  // It is important that initial_addr's port has to be 0 such that the
+  // incremental port behavior could ensure the 2 Binds result in different
+  // address.
+  void DelayTest(const SocketAddress& initial_addr) {
+    time_t seed = ::time(nullptr);
+    LOG(LS_VERBOSE) << "seed = " << seed;
+    srand(static_cast<unsigned int>(seed));
+
+    const uint32_t mean = 2000;
+    const uint32_t stddev = 500;
+
+    ss_.set_delay_mean(mean);
+    ss_.set_delay_stddev(stddev);
+    ss_.UpdateDelayDistribution();
+
+    AsyncSocket* send_socket =
+        ss_.CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM);
+    AsyncSocket* recv_socket =
+        ss_.CreateAsyncSocket(initial_addr.family(), SOCK_DGRAM);
+    ASSERT_EQ(0, send_socket->Bind(initial_addr));
+    ASSERT_EQ(0, recv_socket->Bind(initial_addr));
+    EXPECT_EQ(send_socket->GetLocalAddress().family(), initial_addr.family());
+    EXPECT_EQ(recv_socket->GetLocalAddress().family(), initial_addr.family());
+    ASSERT_EQ(0, send_socket->Connect(recv_socket->GetLocalAddress()));
+
+    Thread* pthMain = Thread::Current();
+    // Avg packet size is 2K, so at 200KB/s for 10s, we should see about
+    // 1000 packets, which is necessary to get a good distribution.
+    Sender sender(pthMain, send_socket, 100 * 2 * 1024);
+    Receiver receiver(pthMain, recv_socket, 0);
+
+    // Simulate 10 seconds of packets being sent, then check the observed delay
+    // distribution.
+    SIMULATED_WAIT(false, 10000, fake_clock_);
+    sender.done = receiver.done = true;
+    ss_.ProcessMessagesUntilIdle();
+
+    const double sample_mean = receiver.sum / receiver.samples;
+    double num =
+        receiver.samples * receiver.sum_sq - receiver.sum * receiver.sum;
+    double den = receiver.samples * (receiver.samples - 1);
+    const double sample_stddev = sqrt(num / den);
+    LOG(LS_VERBOSE) << "mean=" << sample_mean << " stddev=" << sample_stddev;
+
+    EXPECT_LE(500u, receiver.samples);
+    // We initially used a 0.1 fudge factor, but on the build machine, we
+    // have seen the value differ by as much as 0.13.
+    EXPECT_NEAR(mean, sample_mean, 0.15 * mean);
+    EXPECT_NEAR(stddev, sample_stddev, 0.15 * stddev);
+
+    ss_.set_delay_mean(0);
+    ss_.set_delay_stddev(0);
+    ss_.UpdateDelayDistribution();
+  }
+
+  // Test cross-family communication between a client bound to client_addr and a
+  // server bound to server_addr. shouldSucceed indicates if communication is
+  // expected to work or not.
+  void CrossFamilyConnectionTest(const SocketAddress& client_addr,
+                                 const SocketAddress& server_addr,
+                                 bool shouldSucceed) {
+    StreamSink sink;
+    SocketAddress accept_address;
+    const SocketAddress kEmptyAddr;
+
+    // Client gets a IPv4 address
+    std::unique_ptr<AsyncSocket> client =
+        WrapUnique(ss_.CreateAsyncSocket(client_addr.family(), SOCK_STREAM));
+    sink.Monitor(client.get());
+    EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED);
+    EXPECT_EQ(client->GetLocalAddress(), kEmptyAddr);
+    client->Bind(client_addr);
+
+    // Server gets a non-mapped non-any IPv6 address.
+    // IPv4 sockets should not be able to connect to this.
+    std::unique_ptr<AsyncSocket> server =
+        WrapUnique(ss_.CreateAsyncSocket(server_addr.family(), SOCK_STREAM));
+    sink.Monitor(server.get());
+    server->Bind(server_addr);
+    server->Listen(5);
+
+    if (shouldSucceed) {
+      EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+      ss_.ProcessMessagesUntilIdle();
+      EXPECT_TRUE(sink.Check(server.get(), SSE_READ));
+      std::unique_ptr<Socket> accepted =
+          WrapUnique(server->Accept(&accept_address));
+      EXPECT_TRUE(nullptr != accepted);
+      EXPECT_NE(kEmptyAddr, accept_address);
+      ss_.ProcessMessagesUntilIdle();
+      EXPECT_TRUE(sink.Check(client.get(), SSE_OPEN));
+      EXPECT_EQ(client->GetRemoteAddress(), server->GetLocalAddress());
+    } else {
+      // Check that the connection failed.
+      EXPECT_EQ(-1, client->Connect(server->GetLocalAddress()));
+      ss_.ProcessMessagesUntilIdle();
+
+      EXPECT_FALSE(sink.Check(server.get(), SSE_READ));
+      EXPECT_TRUE(nullptr == server->Accept(&accept_address));
+      EXPECT_EQ(accept_address, kEmptyAddr);
+      EXPECT_EQ(client->GetState(), AsyncSocket::CS_CLOSED);
+      EXPECT_FALSE(sink.Check(client.get(), SSE_OPEN));
+      EXPECT_EQ(client->GetRemoteAddress(), kEmptyAddr);
+    }
+  }
+
+  // Test cross-family datagram sending between a client bound to client_addr
+  // and a server bound to server_addr. shouldSucceed indicates if sending is
+  // expected to succeed or not.
+  void CrossFamilyDatagramTest(const SocketAddress& client_addr,
+                               const SocketAddress& server_addr,
+                               bool shouldSucceed) {
+    AsyncSocket* socket = ss_.CreateAsyncSocket(SOCK_DGRAM);
+    socket->Bind(server_addr);
+    SocketAddress bound_server_addr = socket->GetLocalAddress();
+    auto client1 = MakeUnique<TestClient>(MakeUnique<AsyncUDPSocket>(socket),
+                                          &fake_clock_);
+
+    AsyncSocket* socket2 = ss_.CreateAsyncSocket(SOCK_DGRAM);
+    socket2->Bind(client_addr);
+    auto client2 = MakeUnique<TestClient>(MakeUnique<AsyncUDPSocket>(socket2),
+                                          &fake_clock_);
+    SocketAddress client2_addr;
+
+    if (shouldSucceed) {
+      EXPECT_EQ(3, client2->SendTo("foo", 3, bound_server_addr));
+      EXPECT_TRUE(client1->CheckNextPacket("foo", 3, &client2_addr));
+      SocketAddress client1_addr;
+      EXPECT_EQ(6, client1->SendTo("bizbaz", 6, client2_addr));
+      EXPECT_TRUE(client2->CheckNextPacket("bizbaz", 6, &client1_addr));
+      EXPECT_EQ(client1_addr, bound_server_addr);
+    } else {
+      EXPECT_EQ(-1, client2->SendTo("foo", 3, bound_server_addr));
+      EXPECT_TRUE(client1->CheckNoPacket());
+    }
+  }
+
+ protected:
+  rtc::ScopedFakeClock fake_clock_;
+  VirtualSocketServer ss_;
+  AutoSocketServerThread thread_;
+  const SocketAddress kIPv4AnyAddress;
+  const SocketAddress kIPv6AnyAddress;
+};
+
+TEST_F(VirtualSocketServerTest, basic_v4) {
+  SocketAddress ipv4_test_addr(IPAddress(INADDR_ANY), 5000);
+  BasicTest(ipv4_test_addr);
+}
+
+TEST_F(VirtualSocketServerTest, basic_v6) {
+  SocketAddress ipv6_test_addr(IPAddress(in6addr_any), 5000);
+  BasicTest(ipv6_test_addr);
+}
+
+TEST_F(VirtualSocketServerTest, TestDefaultRoute_v4) {
+  IPAddress ipv4_default_addr(0x01020304);
+  TestDefaultRoute(ipv4_default_addr);
+}
+
+TEST_F(VirtualSocketServerTest, TestDefaultRoute_v6) {
+  IPAddress ipv6_default_addr;
+  EXPECT_TRUE(
+      IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c3", &ipv6_default_addr));
+  TestDefaultRoute(ipv6_default_addr);
+}
+
+TEST_F(VirtualSocketServerTest, connect_v4) {
+  ConnectTest(kIPv4AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, connect_v6) {
+  ConnectTest(kIPv6AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, connect_to_non_listener_v4) {
+  ConnectToNonListenerTest(kIPv4AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, connect_to_non_listener_v6) {
+  ConnectToNonListenerTest(kIPv6AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, close_during_connect_v4) {
+  CloseDuringConnectTest(kIPv4AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, close_during_connect_v6) {
+  CloseDuringConnectTest(kIPv6AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, close_v4) {
+  CloseTest(kIPv4AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, close_v6) {
+  CloseTest(kIPv6AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, tcp_send_v4) {
+  TcpSendTest(kIPv4AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, tcp_send_v6) {
+  TcpSendTest(kIPv6AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, TcpSendsPacketsInOrder_v4) {
+  TcpSendsPacketsInOrderTest(kIPv4AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, TcpSendsPacketsInOrder_v6) {
+  TcpSendsPacketsInOrderTest(kIPv6AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, bandwidth_v4) {
+  BandwidthTest(kIPv4AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, bandwidth_v6) {
+  BandwidthTest(kIPv6AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, delay_v4) {
+  DelayTest(kIPv4AnyAddress);
+}
+
+TEST_F(VirtualSocketServerTest, delay_v6) {
+  DelayTest(kIPv6AnyAddress);
+}
+
+// Works, receiving socket sees 127.0.0.2.
+TEST_F(VirtualSocketServerTest, CanConnectFromMappedIPv6ToIPv4Any) {
+  CrossFamilyConnectionTest(SocketAddress("::ffff:127.0.0.2", 0),
+                            SocketAddress("0.0.0.0", 5000),
+                            true);
+}
+
+// Fails.
+TEST_F(VirtualSocketServerTest, CantConnectFromUnMappedIPv6ToIPv4Any) {
+  CrossFamilyConnectionTest(SocketAddress("::2", 0),
+                            SocketAddress("0.0.0.0", 5000),
+                            false);
+}
+
+// Fails.
+TEST_F(VirtualSocketServerTest, CantConnectFromUnMappedIPv6ToMappedIPv6) {
+  CrossFamilyConnectionTest(SocketAddress("::2", 0),
+                            SocketAddress("::ffff:127.0.0.1", 5000),
+                            false);
+}
+
+// Works. receiving socket sees ::ffff:127.0.0.2.
+TEST_F(VirtualSocketServerTest, CanConnectFromIPv4ToIPv6Any) {
+  CrossFamilyConnectionTest(SocketAddress("127.0.0.2", 0),
+                            SocketAddress("::", 5000),
+                            true);
+}
+
+// Fails.
+TEST_F(VirtualSocketServerTest, CantConnectFromIPv4ToUnMappedIPv6) {
+  CrossFamilyConnectionTest(SocketAddress("127.0.0.2", 0),
+                            SocketAddress("::1", 5000),
+                            false);
+}
+
+// Works. Receiving socket sees ::ffff:127.0.0.1.
+TEST_F(VirtualSocketServerTest, CanConnectFromIPv4ToMappedIPv6) {
+  CrossFamilyConnectionTest(SocketAddress("127.0.0.1", 0),
+                            SocketAddress("::ffff:127.0.0.2", 5000),
+                            true);
+}
+
+// Works, receiving socket sees a result from GetNextIP.
+TEST_F(VirtualSocketServerTest, CanConnectFromUnboundIPv6ToIPv4Any) {
+  CrossFamilyConnectionTest(SocketAddress("::", 0),
+                            SocketAddress("0.0.0.0", 5000),
+                            true);
+}
+
+// Works, receiving socket sees whatever GetNextIP gave the client.
+TEST_F(VirtualSocketServerTest, CanConnectFromUnboundIPv4ToIPv6Any) {
+  CrossFamilyConnectionTest(SocketAddress("0.0.0.0", 0),
+                            SocketAddress("::", 5000),
+                            true);
+}
+
+TEST_F(VirtualSocketServerTest, CanSendDatagramFromUnboundIPv4ToIPv6Any) {
+  CrossFamilyDatagramTest(SocketAddress("0.0.0.0", 0),
+                          SocketAddress("::", 5000),
+                          true);
+}
+
+TEST_F(VirtualSocketServerTest, CanSendDatagramFromMappedIPv6ToIPv4Any) {
+  CrossFamilyDatagramTest(SocketAddress("::ffff:127.0.0.1", 0),
+                          SocketAddress("0.0.0.0", 5000),
+                          true);
+}
+
+TEST_F(VirtualSocketServerTest, CantSendDatagramFromUnMappedIPv6ToIPv4Any) {
+  CrossFamilyDatagramTest(SocketAddress("::2", 0),
+                          SocketAddress("0.0.0.0", 5000),
+                          false);
+}
+
+TEST_F(VirtualSocketServerTest, CantSendDatagramFromUnMappedIPv6ToMappedIPv6) {
+  CrossFamilyDatagramTest(SocketAddress("::2", 0),
+                          SocketAddress("::ffff:127.0.0.1", 5000),
+                          false);
+}
+
+TEST_F(VirtualSocketServerTest, CanSendDatagramFromIPv4ToIPv6Any) {
+  CrossFamilyDatagramTest(SocketAddress("127.0.0.2", 0),
+                          SocketAddress("::", 5000),
+                          true);
+}
+
+TEST_F(VirtualSocketServerTest, CantSendDatagramFromIPv4ToUnMappedIPv6) {
+  CrossFamilyDatagramTest(SocketAddress("127.0.0.2", 0),
+                          SocketAddress("::1", 5000),
+                          false);
+}
+
+TEST_F(VirtualSocketServerTest, CanSendDatagramFromIPv4ToMappedIPv6) {
+  CrossFamilyDatagramTest(SocketAddress("127.0.0.1", 0),
+                          SocketAddress("::ffff:127.0.0.2", 5000),
+                          true);
+}
+
+TEST_F(VirtualSocketServerTest, CanSendDatagramFromUnboundIPv6ToIPv4Any) {
+  CrossFamilyDatagramTest(SocketAddress("::", 0),
+                          SocketAddress("0.0.0.0", 5000),
+                          true);
+}
+
+TEST_F(VirtualSocketServerTest, SetSendingBlockedWithUdpSocket) {
+  AsyncSocket* socket1 =
+      ss_.CreateAsyncSocket(kIPv4AnyAddress.family(), SOCK_DGRAM);
+  std::unique_ptr<AsyncSocket> socket2 =
+      WrapUnique(ss_.CreateAsyncSocket(kIPv4AnyAddress.family(), SOCK_DGRAM));
+  socket1->Bind(kIPv4AnyAddress);
+  socket2->Bind(kIPv4AnyAddress);
+  auto client1 =
+      MakeUnique<TestClient>(MakeUnique<AsyncUDPSocket>(socket1), &fake_clock_);
+
+  ss_.SetSendingBlocked(true);
+  EXPECT_EQ(-1, client1->SendTo("foo", 3, socket2->GetLocalAddress()));
+  EXPECT_TRUE(socket1->IsBlocking());
+  EXPECT_EQ(0, client1->ready_to_send_count());
+
+  ss_.SetSendingBlocked(false);
+  EXPECT_EQ(1, client1->ready_to_send_count());
+  EXPECT_EQ(3, client1->SendTo("foo", 3, socket2->GetLocalAddress()));
+}
+
+TEST_F(VirtualSocketServerTest, SetSendingBlockedWithTcpSocket) {
+  constexpr size_t kBufferSize = 1024;
+  ss_.set_send_buffer_capacity(kBufferSize);
+  ss_.set_recv_buffer_capacity(kBufferSize);
+
+  StreamSink sink;
+  std::unique_ptr<AsyncSocket> socket1 =
+      WrapUnique(ss_.CreateAsyncSocket(kIPv4AnyAddress.family(), SOCK_STREAM));
+  std::unique_ptr<AsyncSocket> socket2 =
+      WrapUnique(ss_.CreateAsyncSocket(kIPv4AnyAddress.family(), SOCK_STREAM));
+  sink.Monitor(socket1.get());
+  sink.Monitor(socket2.get());
+  socket1->Bind(kIPv4AnyAddress);
+  socket2->Bind(kIPv4AnyAddress);
+
+  // Connect sockets.
+  EXPECT_EQ(0, socket1->Connect(socket2->GetLocalAddress()));
+  EXPECT_EQ(0, socket2->Connect(socket1->GetLocalAddress()));
+  ss_.ProcessMessagesUntilIdle();
+
+  char data[kBufferSize] = {};
+
+  // First Send call will fill the send buffer but not send anything.
+  ss_.SetSendingBlocked(true);
+  EXPECT_EQ(static_cast<int>(kBufferSize), socket1->Send(data, kBufferSize));
+  ss_.ProcessMessagesUntilIdle();
+  EXPECT_FALSE(sink.Check(socket1.get(), SSE_WRITE));
+  EXPECT_FALSE(sink.Check(socket2.get(), SSE_READ));
+  EXPECT_FALSE(socket1->IsBlocking());
+
+  // Since the send buffer is full, next Send will result in EWOULDBLOCK.
+  EXPECT_EQ(-1, socket1->Send(data, kBufferSize));
+  EXPECT_FALSE(sink.Check(socket1.get(), SSE_WRITE));
+  EXPECT_FALSE(sink.Check(socket2.get(), SSE_READ));
+  EXPECT_TRUE(socket1->IsBlocking());
+
+  // When sending is unblocked, the buffered data should be sent and
+  // SignalWriteEvent should fire.
+  ss_.SetSendingBlocked(false);
+  ss_.ProcessMessagesUntilIdle();
+  EXPECT_TRUE(sink.Check(socket1.get(), SSE_WRITE));
+  EXPECT_TRUE(sink.Check(socket2.get(), SSE_READ));
+}
+
+TEST_F(VirtualSocketServerTest, CreatesStandardDistribution) {
+  const uint32_t kTestMean[] = {10, 100, 333, 1000};
+  const double kTestDev[] = { 0.25, 0.1, 0.01 };
+  // TODO(deadbeef): The current code only works for 1000 data points or more.
+  const uint32_t kTestSamples[] = {/*10, 100,*/ 1000};
+  for (size_t midx = 0; midx < arraysize(kTestMean); ++midx) {
+    for (size_t didx = 0; didx < arraysize(kTestDev); ++didx) {
+      for (size_t sidx = 0; sidx < arraysize(kTestSamples); ++sidx) {
+        ASSERT_LT(0u, kTestSamples[sidx]);
+        const uint32_t kStdDev =
+            static_cast<uint32_t>(kTestDev[didx] * kTestMean[midx]);
+        VirtualSocketServer::Function* f =
+            VirtualSocketServer::CreateDistribution(kTestMean[midx],
+                                                    kStdDev,
+                                                    kTestSamples[sidx]);
+        ASSERT_TRUE(nullptr != f);
+        ASSERT_EQ(kTestSamples[sidx], f->size());
+        double sum = 0;
+        for (uint32_t i = 0; i < f->size(); ++i) {
+          sum += (*f)[i].second;
+        }
+        const double mean = sum / f->size();
+        double sum_sq_dev = 0;
+        for (uint32_t i = 0; i < f->size(); ++i) {
+          double dev = (*f)[i].second - mean;
+          sum_sq_dev += dev * dev;
+        }
+        const double stddev = sqrt(sum_sq_dev / f->size());
+        EXPECT_NEAR(kTestMean[midx], mean, 0.1 * kTestMean[midx])
+          << "M=" << kTestMean[midx]
+          << " SD=" << kStdDev
+          << " N=" << kTestSamples[sidx];
+        EXPECT_NEAR(kStdDev, stddev, 0.1 * kStdDev)
+          << "M=" << kTestMean[midx]
+          << " SD=" << kStdDev
+          << " N=" << kTestSamples[sidx];
+        delete f;
+      }
+    }
+  }
+}
diff --git a/base/virtualsocketserver.cc b/base/virtualsocketserver.cc
new file mode 100644
index 0000000..a7e2d30
--- /dev/null
+++ b/base/virtualsocketserver.cc
@@ -0,0 +1,1213 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/virtualsocketserver.h"
+
+#include <errno.h>
+#include <math.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/fakeclock.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/socketaddresspair.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/timeutils.h"
+
+namespace rtc {
+#if defined(WEBRTC_WIN)
+const in_addr kInitialNextIPv4 = { { { 0x01, 0, 0, 0 } } };
+#else
+// This value is entirely arbitrary, hence the lack of concern about endianness.
+const in_addr kInitialNextIPv4 = { 0x01000000 };
+#endif
+// Starts at ::2 so as to not cause confusion with ::1.
+const in6_addr kInitialNextIPv6 = { { {
+      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2
+    } } };
+
+const uint16_t kFirstEphemeralPort = 49152;
+const uint16_t kLastEphemeralPort = 65535;
+const uint16_t kEphemeralPortCount =
+    kLastEphemeralPort - kFirstEphemeralPort + 1;
+const uint32_t kDefaultNetworkCapacity = 64 * 1024;
+const uint32_t kDefaultTcpBufferSize = 32 * 1024;
+
+const uint32_t UDP_HEADER_SIZE = 28;  // IP + UDP headers
+const uint32_t TCP_HEADER_SIZE = 40;  // IP + TCP headers
+const uint32_t TCP_MSS = 1400;        // Maximum segment size
+
+// Note: The current algorithm doesn't work for sample sizes smaller than this.
+const int NUM_SAMPLES = 1000;
+
+enum {
+  MSG_ID_PACKET,
+  MSG_ID_ADDRESS_BOUND,
+  MSG_ID_CONNECT,
+  MSG_ID_DISCONNECT,
+  MSG_ID_SIGNALREADEVENT,
+};
+
+// Packets are passed between sockets as messages.  We copy the data just like
+// the kernel does.
+class Packet : public MessageData {
+ public:
+  Packet(const char* data, size_t size, const SocketAddress& from)
+        : size_(size), consumed_(0), from_(from) {
+    RTC_DCHECK(nullptr != data);
+    data_ = new char[size_];
+    memcpy(data_, data, size_);
+  }
+
+  ~Packet() override {
+    delete[] data_;
+  }
+
+  const char* data() const { return data_ + consumed_; }
+  size_t size() const { return size_ - consumed_; }
+  const SocketAddress& from() const { return from_; }
+
+  // Remove the first size bytes from the data.
+  void Consume(size_t size) {
+    RTC_DCHECK(size + consumed_ < size_);
+    consumed_ += size;
+  }
+
+ private:
+  char* data_;
+  size_t size_, consumed_;
+  SocketAddress from_;
+};
+
+struct MessageAddress : public MessageData {
+  explicit MessageAddress(const SocketAddress& a) : addr(a) { }
+  SocketAddress addr;
+};
+
+VirtualSocket::VirtualSocket(VirtualSocketServer* server,
+                             int family,
+                             int type,
+                             bool async)
+    : server_(server),
+      type_(type),
+      async_(async),
+      state_(CS_CLOSED),
+      error_(0),
+      listen_queue_(nullptr),
+      network_size_(0),
+      recv_buffer_size_(0),
+      bound_(false),
+      was_any_(false) {
+  RTC_DCHECK((type_ == SOCK_DGRAM) || (type_ == SOCK_STREAM));
+  RTC_DCHECK(async_ ||
+             (type_ != SOCK_STREAM));  // We only support async streams
+  server->SignalReadyToSend.connect(this,
+                                    &VirtualSocket::OnSocketServerReadyToSend);
+}
+
+VirtualSocket::~VirtualSocket() {
+  Close();
+
+  for (RecvBuffer::iterator it = recv_buffer_.begin(); it != recv_buffer_.end();
+       ++it) {
+    delete *it;
+  }
+}
+
+SocketAddress VirtualSocket::GetLocalAddress() const {
+  if (!alternative_local_addr_.IsNil())
+    return alternative_local_addr_;
+  return local_addr_;
+}
+
+SocketAddress VirtualSocket::GetRemoteAddress() const {
+  return remote_addr_;
+}
+
+void VirtualSocket::SetLocalAddress(const SocketAddress& addr) {
+  local_addr_ = addr;
+}
+
+void VirtualSocket::SetAlternativeLocalAddress(const SocketAddress& addr) {
+  alternative_local_addr_ = addr;
+}
+
+int VirtualSocket::Bind(const SocketAddress& addr) {
+  if (!local_addr_.IsNil()) {
+    error_ = EINVAL;
+    return -1;
+  }
+  local_addr_ = addr;
+  int result = server_->Bind(this, &local_addr_);
+  if (result != 0) {
+    local_addr_.Clear();
+    error_ = EADDRINUSE;
+  } else {
+    bound_ = true;
+    was_any_ = addr.IsAnyIP();
+    // Post a message here such that test case could have chance to
+    // process the local address. (i.e. SetAlternativeLocalAddress).
+    server_->msg_queue_->Post(RTC_FROM_HERE, this, MSG_ID_ADDRESS_BOUND);
+  }
+  return result;
+}
+
+int VirtualSocket::Connect(const SocketAddress& addr) {
+  return InitiateConnect(addr, true);
+}
+
+int VirtualSocket::Close() {
+  if (!local_addr_.IsNil() && bound_) {
+    // Remove from the binding table.
+    server_->Unbind(local_addr_, this);
+    bound_ = false;
+  }
+
+  if (SOCK_STREAM == type_) {
+    // Cancel pending sockets
+    if (listen_queue_) {
+      while (!listen_queue_->empty()) {
+        SocketAddress addr = listen_queue_->front();
+
+        // Disconnect listening socket.
+        server_->Disconnect(server_->LookupBinding(addr));
+        listen_queue_->pop_front();
+      }
+      delete listen_queue_;
+      listen_queue_ = nullptr;
+    }
+    // Disconnect stream sockets
+    if (CS_CONNECTED == state_) {
+      // Disconnect remote socket, check if it is a child of a server socket.
+      VirtualSocket* socket =
+          server_->LookupConnection(local_addr_, remote_addr_);
+      if (!socket) {
+        // Not a server socket child, then see if it is bound.
+        // TODO(tbd): If this is indeed a server socket that has no
+        // children this will cause the server socket to be
+        // closed. This might lead to unexpected results, how to fix this?
+        socket = server_->LookupBinding(remote_addr_);
+      }
+      server_->Disconnect(socket);
+
+      // Remove mapping for both directions.
+      server_->RemoveConnection(remote_addr_, local_addr_);
+      server_->RemoveConnection(local_addr_, remote_addr_);
+    }
+    // Cancel potential connects
+    MessageList msgs;
+    if (server_->msg_queue_) {
+      server_->msg_queue_->Clear(this, MSG_ID_CONNECT, &msgs);
+    }
+    for (MessageList::iterator it = msgs.begin(); it != msgs.end(); ++it) {
+      RTC_DCHECK(nullptr != it->pdata);
+      MessageAddress* data = static_cast<MessageAddress*>(it->pdata);
+
+      // Lookup remote side.
+      VirtualSocket* socket =
+          server_->LookupConnection(local_addr_, data->addr);
+      if (socket) {
+        // Server socket, remote side is a socket retreived by
+        // accept. Accepted sockets are not bound so we will not
+        // find it by looking in the bindings table.
+        server_->Disconnect(socket);
+        server_->RemoveConnection(local_addr_, data->addr);
+      } else {
+        server_->Disconnect(server_->LookupBinding(data->addr));
+      }
+      delete data;
+    }
+    // Clear incoming packets and disconnect messages
+    if (server_->msg_queue_) {
+      server_->msg_queue_->Clear(this);
+    }
+  }
+
+  state_ = CS_CLOSED;
+  local_addr_.Clear();
+  remote_addr_.Clear();
+  return 0;
+}
+
+int VirtualSocket::Send(const void* pv, size_t cb) {
+    if (CS_CONNECTED != state_) {
+      error_ = ENOTCONN;
+      return -1;
+    }
+    if (SOCK_DGRAM == type_) {
+      return SendUdp(pv, cb, remote_addr_);
+    } else {
+      return SendTcp(pv, cb);
+    }
+}
+
+int VirtualSocket::SendTo(const void* pv,
+                          size_t cb,
+                          const SocketAddress& addr) {
+  if (SOCK_DGRAM == type_) {
+    return SendUdp(pv, cb, addr);
+  } else {
+    if (CS_CONNECTED != state_) {
+      error_ = ENOTCONN;
+      return -1;
+    }
+    return SendTcp(pv, cb);
+  }
+}
+
+int VirtualSocket::Recv(void* pv, size_t cb, int64_t* timestamp) {
+  SocketAddress addr;
+  return RecvFrom(pv, cb, &addr, timestamp);
+}
+
+int VirtualSocket::RecvFrom(void* pv,
+                            size_t cb,
+                            SocketAddress* paddr,
+                            int64_t* timestamp) {
+  if (timestamp) {
+    *timestamp = -1;
+  }
+  // If we don't have a packet, then either error or wait for one to arrive.
+  if (recv_buffer_.empty()) {
+    if (async_) {
+      error_ = EAGAIN;
+      return -1;
+    }
+    while (recv_buffer_.empty()) {
+      Message msg;
+      server_->msg_queue_->Get(&msg);
+      server_->msg_queue_->Dispatch(&msg);
+    }
+  }
+
+  // Return the packet at the front of the queue.
+  Packet* packet = recv_buffer_.front();
+  size_t data_read = std::min(cb, packet->size());
+  memcpy(pv, packet->data(), data_read);
+  *paddr = packet->from();
+
+  if (data_read < packet->size()) {
+    packet->Consume(data_read);
+  } else {
+    recv_buffer_.pop_front();
+    delete packet;
+  }
+
+  // To behave like a real socket, SignalReadEvent should fire in the next
+  // message loop pass if there's still data buffered.
+  if (!recv_buffer_.empty()) {
+    // Clear the message so it doesn't end up posted multiple times.
+    server_->msg_queue_->Clear(this, MSG_ID_SIGNALREADEVENT);
+    server_->msg_queue_->Post(RTC_FROM_HERE, this, MSG_ID_SIGNALREADEVENT);
+  }
+
+  if (SOCK_STREAM == type_) {
+    bool was_full = (recv_buffer_size_ == server_->recv_buffer_capacity_);
+    recv_buffer_size_ -= data_read;
+    if (was_full) {
+      VirtualSocket* sender = server_->LookupBinding(remote_addr_);
+      RTC_DCHECK(nullptr != sender);
+      server_->SendTcp(sender);
+    }
+  }
+
+  return static_cast<int>(data_read);
+}
+
+int VirtualSocket::Listen(int backlog) {
+  RTC_DCHECK(SOCK_STREAM == type_);
+  RTC_DCHECK(CS_CLOSED == state_);
+  if (local_addr_.IsNil()) {
+    error_ = EINVAL;
+    return -1;
+  }
+  RTC_DCHECK(nullptr == listen_queue_);
+  listen_queue_ = new ListenQueue;
+  state_ = CS_CONNECTING;
+  return 0;
+}
+
+VirtualSocket* VirtualSocket::Accept(SocketAddress* paddr) {
+  if (nullptr == listen_queue_) {
+    error_ = EINVAL;
+    return nullptr;
+  }
+  while (!listen_queue_->empty()) {
+    VirtualSocket* socket = new VirtualSocket(server_, AF_INET, type_, async_);
+
+    // Set the new local address to the same as this server socket.
+    socket->SetLocalAddress(local_addr_);
+    // Sockets made from a socket that 'was Any' need to inherit that.
+    socket->set_was_any(was_any_);
+    SocketAddress remote_addr(listen_queue_->front());
+    int result = socket->InitiateConnect(remote_addr, false);
+    listen_queue_->pop_front();
+    if (result != 0) {
+      delete socket;
+      continue;
+    }
+    socket->CompleteConnect(remote_addr, false);
+    if (paddr) {
+      *paddr = remote_addr;
+    }
+    return socket;
+  }
+  error_ = EWOULDBLOCK;
+  return nullptr;
+}
+
+int VirtualSocket::GetError() const {
+  return error_;
+}
+
+void VirtualSocket::SetError(int error) {
+  error_ = error;
+}
+
+Socket::ConnState VirtualSocket::GetState() const {
+  return state_;
+}
+
+int VirtualSocket::GetOption(Option opt, int* value) {
+  OptionsMap::const_iterator it = options_map_.find(opt);
+  if (it == options_map_.end()) {
+    return -1;
+  }
+  *value = it->second;
+  return 0;  // 0 is success to emulate getsockopt()
+}
+
+int VirtualSocket::SetOption(Option opt, int value) {
+  options_map_[opt] = value;
+  return 0;  // 0 is success to emulate setsockopt()
+}
+
+void VirtualSocket::OnMessage(Message* pmsg) {
+  if (pmsg->message_id == MSG_ID_PACKET) {
+    RTC_DCHECK(nullptr != pmsg->pdata);
+    Packet* packet = static_cast<Packet*>(pmsg->pdata);
+
+    recv_buffer_.push_back(packet);
+
+    if (async_) {
+      SignalReadEvent(this);
+    }
+  } else if (pmsg->message_id == MSG_ID_CONNECT) {
+    RTC_DCHECK(nullptr != pmsg->pdata);
+    MessageAddress* data = static_cast<MessageAddress*>(pmsg->pdata);
+    if (listen_queue_ != nullptr) {
+      listen_queue_->push_back(data->addr);
+      if (async_) {
+        SignalReadEvent(this);
+      }
+    } else if ((SOCK_STREAM == type_) && (CS_CONNECTING == state_)) {
+      CompleteConnect(data->addr, true);
+    } else {
+      LOG(LS_VERBOSE) << "Socket at " << local_addr_ << " is not listening";
+      server_->Disconnect(server_->LookupBinding(data->addr));
+    }
+    delete data;
+  } else if (pmsg->message_id == MSG_ID_DISCONNECT) {
+    RTC_DCHECK(SOCK_STREAM == type_);
+    if (CS_CLOSED != state_) {
+      int error = (CS_CONNECTING == state_) ? ECONNREFUSED : 0;
+      state_ = CS_CLOSED;
+      remote_addr_.Clear();
+      if (async_) {
+        SignalCloseEvent(this, error);
+      }
+    }
+  } else if (pmsg->message_id == MSG_ID_ADDRESS_BOUND) {
+    SignalAddressReady(this, GetLocalAddress());
+  } else if (pmsg->message_id == MSG_ID_SIGNALREADEVENT) {
+    if (!recv_buffer_.empty()) {
+      SignalReadEvent(this);
+    }
+  } else {
+    RTC_NOTREACHED();
+  }
+}
+
+int VirtualSocket::InitiateConnect(const SocketAddress& addr, bool use_delay) {
+  if (!remote_addr_.IsNil()) {
+    error_ = (CS_CONNECTED == state_) ? EISCONN : EINPROGRESS;
+    return -1;
+  }
+  if (local_addr_.IsNil()) {
+    // If there's no local address set, grab a random one in the correct AF.
+    int result = 0;
+    if (addr.ipaddr().family() == AF_INET) {
+      result = Bind(SocketAddress("0.0.0.0", 0));
+    } else if (addr.ipaddr().family() == AF_INET6) {
+      result = Bind(SocketAddress("::", 0));
+    }
+    if (result != 0) {
+      return result;
+    }
+  }
+  if (type_ == SOCK_DGRAM) {
+    remote_addr_ = addr;
+    state_ = CS_CONNECTED;
+  } else {
+    int result = server_->Connect(this, addr, use_delay);
+    if (result != 0) {
+      error_ = EHOSTUNREACH;
+      return -1;
+    }
+    state_ = CS_CONNECTING;
+  }
+  return 0;
+}
+
+void VirtualSocket::CompleteConnect(const SocketAddress& addr, bool notify) {
+  RTC_DCHECK(CS_CONNECTING == state_);
+  remote_addr_ = addr;
+  state_ = CS_CONNECTED;
+  server_->AddConnection(remote_addr_, local_addr_, this);
+  if (async_ && notify) {
+    SignalConnectEvent(this);
+  }
+}
+
+int VirtualSocket::SendUdp(const void* pv,
+                           size_t cb,
+                           const SocketAddress& addr) {
+  // If we have not been assigned a local port, then get one.
+  if (local_addr_.IsNil()) {
+    local_addr_ = EmptySocketAddressWithFamily(addr.ipaddr().family());
+    int result = server_->Bind(this, &local_addr_);
+    if (result != 0) {
+      local_addr_.Clear();
+      error_ = EADDRINUSE;
+      return result;
+    }
+  }
+
+  // Send the data in a message to the appropriate socket.
+  return server_->SendUdp(this, static_cast<const char*>(pv), cb, addr);
+}
+
+int VirtualSocket::SendTcp(const void* pv, size_t cb) {
+  size_t capacity = server_->send_buffer_capacity_ - send_buffer_.size();
+  if (0 == capacity) {
+    ready_to_send_ = false;
+    error_ = EWOULDBLOCK;
+    return -1;
+  }
+  size_t consumed = std::min(cb, capacity);
+  const char* cpv = static_cast<const char*>(pv);
+  send_buffer_.insert(send_buffer_.end(), cpv, cpv + consumed);
+  server_->SendTcp(this);
+  return static_cast<int>(consumed);
+}
+
+void VirtualSocket::OnSocketServerReadyToSend() {
+  if (ready_to_send_) {
+    // This socket didn't encounter EWOULDBLOCK, so there's nothing to do.
+    return;
+  }
+  if (type_ == SOCK_DGRAM) {
+    ready_to_send_ = true;
+    SignalWriteEvent(this);
+  } else {
+    RTC_DCHECK(type_ == SOCK_STREAM);
+    // This will attempt to empty the full send buffer, and will fire
+    // SignalWriteEvent if successful.
+    server_->SendTcp(this);
+  }
+}
+
+VirtualSocketServer::VirtualSocketServer() : VirtualSocketServer(nullptr) {}
+
+VirtualSocketServer::VirtualSocketServer(FakeClock* fake_clock)
+    : fake_clock_(fake_clock),
+      wakeup_(/*manual_reset=*/false, /*initially_signaled=*/false),
+      msg_queue_(nullptr),
+      stop_on_idle_(false),
+      next_ipv4_(kInitialNextIPv4),
+      next_ipv6_(kInitialNextIPv6),
+      next_port_(kFirstEphemeralPort),
+      bindings_(new AddressMap()),
+      connections_(new ConnectionMap()),
+      bandwidth_(0),
+      network_capacity_(kDefaultNetworkCapacity),
+      send_buffer_capacity_(kDefaultTcpBufferSize),
+      recv_buffer_capacity_(kDefaultTcpBufferSize),
+      delay_mean_(0),
+      delay_stddev_(0),
+      delay_samples_(NUM_SAMPLES),
+      drop_prob_(0.0) {
+  UpdateDelayDistribution();
+}
+
+VirtualSocketServer::~VirtualSocketServer() {
+  delete bindings_;
+  delete connections_;
+}
+
+IPAddress VirtualSocketServer::GetNextIP(int family) {
+  if (family == AF_INET) {
+    IPAddress next_ip(next_ipv4_);
+    next_ipv4_.s_addr =
+        HostToNetwork32(NetworkToHost32(next_ipv4_.s_addr) + 1);
+    return next_ip;
+  } else if (family == AF_INET6) {
+    IPAddress next_ip(next_ipv6_);
+    uint32_t* as_ints = reinterpret_cast<uint32_t*>(&next_ipv6_.s6_addr);
+    as_ints[3] += 1;
+    return next_ip;
+  }
+  return IPAddress();
+}
+
+uint16_t VirtualSocketServer::GetNextPort() {
+  uint16_t port = next_port_;
+  if (next_port_ < kLastEphemeralPort) {
+    ++next_port_;
+  } else {
+    next_port_ = kFirstEphemeralPort;
+  }
+  return port;
+}
+
+void VirtualSocketServer::SetSendingBlocked(bool blocked) {
+  if (blocked == sending_blocked_) {
+    // Unchanged; nothing to do.
+    return;
+  }
+  sending_blocked_ = blocked;
+  if (!sending_blocked_) {
+    // Sending was blocked, but is now unblocked. This signal gives sockets a
+    // chance to fire SignalWriteEvent, and for TCP, send buffered data.
+    SignalReadyToSend();
+  }
+}
+
+Socket* VirtualSocketServer::CreateSocket(int type) {
+  return CreateSocket(AF_INET, type);
+}
+
+Socket* VirtualSocketServer::CreateSocket(int family, int type) {
+  return CreateSocketInternal(family, type);
+}
+
+AsyncSocket* VirtualSocketServer::CreateAsyncSocket(int type) {
+  return CreateAsyncSocket(AF_INET, type);
+}
+
+AsyncSocket* VirtualSocketServer::CreateAsyncSocket(int family, int type) {
+  return CreateSocketInternal(family, type);
+}
+
+VirtualSocket* VirtualSocketServer::CreateSocketInternal(int family, int type) {
+  VirtualSocket* socket = new VirtualSocket(this, family, type, true);
+  SignalSocketCreated(socket);
+  return socket;
+}
+
+void VirtualSocketServer::SetMessageQueue(MessageQueue* msg_queue) {
+  msg_queue_ = msg_queue;
+  if (msg_queue_) {
+    msg_queue_->SignalQueueDestroyed.connect(this,
+        &VirtualSocketServer::OnMessageQueueDestroyed);
+  }
+}
+
+bool VirtualSocketServer::Wait(int cmsWait, bool process_io) {
+  RTC_DCHECK(msg_queue_ == Thread::Current());
+  if (stop_on_idle_ && Thread::Current()->empty()) {
+    return false;
+  }
+  // Note: we don't need to do anything with |process_io| since we don't have
+  // any real I/O. Received packets come in the form of queued messages, so
+  // MessageQueue will ensure WakeUp is called if another thread sends a
+  // packet.
+  wakeup_.Wait(cmsWait);
+  return true;
+}
+
+void VirtualSocketServer::WakeUp() {
+  wakeup_.Set();
+}
+
+bool VirtualSocketServer::ProcessMessagesUntilIdle() {
+  RTC_DCHECK(msg_queue_ == Thread::Current());
+  stop_on_idle_ = true;
+  while (!msg_queue_->empty()) {
+    if (fake_clock_) {
+      // If using a fake clock, advance it in millisecond increments until the
+      // queue is empty.
+      fake_clock_->AdvanceTime(rtc::TimeDelta::FromMilliseconds(1));
+    } else {
+      // Otherwise, run a normal message loop.
+      Message msg;
+      if (msg_queue_->Get(&msg, Thread::kForever)) {
+        msg_queue_->Dispatch(&msg);
+      }
+    }
+  }
+  stop_on_idle_ = false;
+  return !msg_queue_->IsQuitting();
+}
+
+void VirtualSocketServer::SetNextPortForTesting(uint16_t port) {
+  next_port_ = port;
+}
+
+bool VirtualSocketServer::CloseTcpConnections(
+    const SocketAddress& addr_local,
+    const SocketAddress& addr_remote) {
+  VirtualSocket* socket = LookupConnection(addr_local, addr_remote);
+  if (!socket) {
+    return false;
+  }
+  // Signal the close event on the local connection first.
+  socket->SignalCloseEvent(socket, 0);
+
+  // Trigger the remote connection's close event.
+  socket->Close();
+
+  return true;
+}
+
+int VirtualSocketServer::Bind(VirtualSocket* socket,
+                              const SocketAddress& addr) {
+  RTC_DCHECK(nullptr != socket);
+  // Address must be completely specified at this point
+  RTC_DCHECK(!IPIsUnspec(addr.ipaddr()));
+  RTC_DCHECK(addr.port() != 0);
+
+  // Normalize the address (turns v6-mapped addresses into v4-addresses).
+  SocketAddress normalized(addr.ipaddr().Normalized(), addr.port());
+
+  AddressMap::value_type entry(normalized, socket);
+  return bindings_->insert(entry).second ? 0 : -1;
+}
+
+int VirtualSocketServer::Bind(VirtualSocket* socket, SocketAddress* addr) {
+  RTC_DCHECK(nullptr != socket);
+
+  if (!IPIsUnspec(addr->ipaddr())) {
+    addr->SetIP(addr->ipaddr().Normalized());
+  } else {
+    RTC_NOTREACHED();
+  }
+
+  if (addr->port() == 0) {
+    for (int i = 0; i < kEphemeralPortCount; ++i) {
+      addr->SetPort(GetNextPort());
+      if (bindings_->find(*addr) == bindings_->end()) {
+        break;
+      }
+    }
+  }
+
+  return Bind(socket, *addr);
+}
+
+VirtualSocket* VirtualSocketServer::LookupBinding(const SocketAddress& addr) {
+  SocketAddress normalized(addr.ipaddr().Normalized(),
+                           addr.port());
+  AddressMap::iterator it = bindings_->find(normalized);
+  if (it != bindings_->end()) {
+    return it->second;
+  }
+
+  IPAddress default_ip = GetDefaultRoute(addr.ipaddr().family());
+  if (!IPIsUnspec(default_ip) && addr.ipaddr() == default_ip) {
+    // If we can't find a binding for the packet which is sent to the interface
+    // corresponding to the default route, it should match a binding with the
+    // correct port to the any address.
+    SocketAddress sock_addr =
+        EmptySocketAddressWithFamily(addr.ipaddr().family());
+    sock_addr.SetPort(addr.port());
+    return LookupBinding(sock_addr);
+  }
+
+  return nullptr;
+}
+
+int VirtualSocketServer::Unbind(const SocketAddress& addr,
+                                VirtualSocket* socket) {
+  SocketAddress normalized(addr.ipaddr().Normalized(),
+                           addr.port());
+  RTC_DCHECK((*bindings_)[normalized] == socket);
+  bindings_->erase(bindings_->find(normalized));
+  return 0;
+}
+
+void VirtualSocketServer::AddConnection(const SocketAddress& local,
+                                        const SocketAddress& remote,
+                                        VirtualSocket* remote_socket) {
+  // Add this socket pair to our routing table. This will allow
+  // multiple clients to connect to the same server address.
+  SocketAddress local_normalized(local.ipaddr().Normalized(),
+                                 local.port());
+  SocketAddress remote_normalized(remote.ipaddr().Normalized(),
+                                  remote.port());
+  SocketAddressPair address_pair(local_normalized, remote_normalized);
+  connections_->insert(std::pair<SocketAddressPair,
+                       VirtualSocket*>(address_pair, remote_socket));
+}
+
+VirtualSocket* VirtualSocketServer::LookupConnection(
+    const SocketAddress& local,
+    const SocketAddress& remote) {
+  SocketAddress local_normalized(local.ipaddr().Normalized(),
+                                 local.port());
+  SocketAddress remote_normalized(remote.ipaddr().Normalized(),
+                                  remote.port());
+  SocketAddressPair address_pair(local_normalized, remote_normalized);
+  ConnectionMap::iterator it = connections_->find(address_pair);
+  return (connections_->end() != it) ? it->second : nullptr;
+}
+
+void VirtualSocketServer::RemoveConnection(const SocketAddress& local,
+                                           const SocketAddress& remote) {
+  SocketAddress local_normalized(local.ipaddr().Normalized(),
+                                local.port());
+  SocketAddress remote_normalized(remote.ipaddr().Normalized(),
+                                 remote.port());
+  SocketAddressPair address_pair(local_normalized, remote_normalized);
+  connections_->erase(address_pair);
+}
+
+static double Random() {
+  return static_cast<double>(rand()) / RAND_MAX;
+}
+
+int VirtualSocketServer::Connect(VirtualSocket* socket,
+                                 const SocketAddress& remote_addr,
+                                 bool use_delay) {
+  uint32_t delay = use_delay ? GetTransitDelay(socket) : 0;
+  VirtualSocket* remote = LookupBinding(remote_addr);
+  if (!CanInteractWith(socket, remote)) {
+    LOG(LS_INFO) << "Address family mismatch between "
+                 << socket->GetLocalAddress() << " and " << remote_addr;
+    return -1;
+  }
+  if (remote != nullptr) {
+    SocketAddress addr = socket->GetLocalAddress();
+    msg_queue_->PostDelayed(RTC_FROM_HERE, delay, remote, MSG_ID_CONNECT,
+                            new MessageAddress(addr));
+  } else {
+    LOG(LS_INFO) << "No one listening at " << remote_addr;
+    msg_queue_->PostDelayed(RTC_FROM_HERE, delay, socket, MSG_ID_DISCONNECT);
+  }
+  return 0;
+}
+
+bool VirtualSocketServer::Disconnect(VirtualSocket* socket) {
+  if (socket) {
+    // If we simulate packets being delayed, we should simulate the
+    // equivalent of a FIN being delayed as well.
+    uint32_t delay = GetTransitDelay(socket);
+    // Remove the mapping.
+    msg_queue_->PostDelayed(RTC_FROM_HERE, delay, socket, MSG_ID_DISCONNECT);
+    return true;
+  }
+  return false;
+}
+
+int VirtualSocketServer::SendUdp(VirtualSocket* socket,
+                                 const char* data, size_t data_size,
+                                 const SocketAddress& remote_addr) {
+  if (sending_blocked_) {
+    CritScope cs(&socket->crit_);
+    socket->ready_to_send_ = false;
+    socket->error_ = EWOULDBLOCK;
+    return -1;
+  }
+
+  // See if we want to drop this packet.
+  if (Random() < drop_prob_) {
+    LOG(LS_VERBOSE) << "Dropping packet: bad luck";
+    return static_cast<int>(data_size);
+  }
+
+  VirtualSocket* recipient = LookupBinding(remote_addr);
+  if (!recipient) {
+    // Make a fake recipient for address family checking.
+    std::unique_ptr<VirtualSocket> dummy_socket(
+        CreateSocketInternal(AF_INET, SOCK_DGRAM));
+    dummy_socket->SetLocalAddress(remote_addr);
+    if (!CanInteractWith(socket, dummy_socket.get())) {
+      LOG(LS_VERBOSE) << "Incompatible address families: "
+                      << socket->GetLocalAddress() << " and " << remote_addr;
+      return -1;
+    }
+    LOG(LS_VERBOSE) << "No one listening at " << remote_addr;
+    return static_cast<int>(data_size);
+  }
+
+  if (!CanInteractWith(socket, recipient)) {
+    LOG(LS_VERBOSE) << "Incompatible address families: "
+                    << socket->GetLocalAddress() << " and " << remote_addr;
+    return -1;
+  }
+
+  {
+    CritScope cs(&socket->crit_);
+
+    int64_t cur_time = TimeMillis();
+    PurgeNetworkPackets(socket, cur_time);
+
+    // Determine whether we have enough bandwidth to accept this packet.  To do
+    // this, we need to update the send queue.  Once we know it's current size,
+    // we know whether we can fit this packet.
+    //
+    // NOTE: There are better algorithms for maintaining such a queue (such as
+    // "Derivative Random Drop"); however, this algorithm is a more accurate
+    // simulation of what a normal network would do.
+
+    size_t packet_size = data_size + UDP_HEADER_SIZE;
+    if (socket->network_size_ + packet_size > network_capacity_) {
+      LOG(LS_VERBOSE) << "Dropping packet: network capacity exceeded";
+      return static_cast<int>(data_size);
+    }
+
+    AddPacketToNetwork(socket, recipient, cur_time, data, data_size,
+                       UDP_HEADER_SIZE, false);
+
+    return static_cast<int>(data_size);
+  }
+}
+
+void VirtualSocketServer::SendTcp(VirtualSocket* socket) {
+  if (sending_blocked_) {
+    // Eventually the socket's buffer will fill and VirtualSocket::SendTcp will
+    // set EWOULDBLOCK.
+    return;
+  }
+
+  // TCP can't send more data than will fill up the receiver's buffer.
+  // We track the data that is in the buffer plus data in flight using the
+  // recipient's recv_buffer_size_.  Anything beyond that must be stored in the
+  // sender's buffer.  We will trigger the buffered data to be sent when data
+  // is read from the recv_buffer.
+
+  // Lookup the local/remote pair in the connections table.
+  VirtualSocket* recipient = LookupConnection(socket->local_addr_,
+                                              socket->remote_addr_);
+  if (!recipient) {
+    LOG(LS_VERBOSE) << "Sending data to no one.";
+    return;
+  }
+
+  CritScope cs(&socket->crit_);
+
+  int64_t cur_time = TimeMillis();
+  PurgeNetworkPackets(socket, cur_time);
+
+  while (true) {
+    size_t available = recv_buffer_capacity_ - recipient->recv_buffer_size_;
+    size_t max_data_size =
+        std::min<size_t>(available, TCP_MSS - TCP_HEADER_SIZE);
+    size_t data_size = std::min(socket->send_buffer_.size(), max_data_size);
+    if (0 == data_size)
+      break;
+
+    AddPacketToNetwork(socket, recipient, cur_time, &socket->send_buffer_[0],
+                       data_size, TCP_HEADER_SIZE, true);
+    recipient->recv_buffer_size_ += data_size;
+
+    size_t new_buffer_size = socket->send_buffer_.size() - data_size;
+    // Avoid undefined access beyond the last element of the vector.
+    // This only happens when new_buffer_size is 0.
+    if (data_size < socket->send_buffer_.size()) {
+      // memmove is required for potentially overlapping source/destination.
+      memmove(&socket->send_buffer_[0], &socket->send_buffer_[data_size],
+              new_buffer_size);
+    }
+    socket->send_buffer_.resize(new_buffer_size);
+  }
+
+  if (!socket->ready_to_send_ &&
+      (socket->send_buffer_.size() < send_buffer_capacity_)) {
+    socket->ready_to_send_ = true;
+    socket->SignalWriteEvent(socket);
+  }
+}
+
+void VirtualSocketServer::AddPacketToNetwork(VirtualSocket* sender,
+                                             VirtualSocket* recipient,
+                                             int64_t cur_time,
+                                             const char* data,
+                                             size_t data_size,
+                                             size_t header_size,
+                                             bool ordered) {
+  VirtualSocket::NetworkEntry entry;
+  entry.size = data_size + header_size;
+
+  sender->network_size_ += entry.size;
+  uint32_t send_delay = SendDelay(static_cast<uint32_t>(sender->network_size_));
+  entry.done_time = cur_time + send_delay;
+  sender->network_.push_back(entry);
+
+  // Find the delay for crossing the many virtual hops of the network.
+  uint32_t transit_delay = GetTransitDelay(sender);
+
+  // When the incoming packet is from a binding of the any address, translate it
+  // to the default route here such that the recipient will see the default
+  // route.
+  SocketAddress sender_addr = sender->local_addr_;
+  IPAddress default_ip = GetDefaultRoute(sender_addr.ipaddr().family());
+  if (sender_addr.IsAnyIP() && !IPIsUnspec(default_ip)) {
+    sender_addr.SetIP(default_ip);
+  }
+
+  // Post the packet as a message to be delivered (on our own thread)
+  Packet* p = new Packet(data, data_size, sender_addr);
+
+  int64_t ts = TimeAfter(send_delay + transit_delay);
+  if (ordered) {
+    // Ensure that new packets arrive after previous ones
+    ts = std::max(ts, sender->last_delivery_time_);
+    // A socket should not have both ordered and unordered delivery, so its last
+    // delivery time only needs to be updated when it has ordered delivery.
+    sender->last_delivery_time_ = ts;
+  }
+  msg_queue_->PostAt(RTC_FROM_HERE, ts, recipient, MSG_ID_PACKET, p);
+}
+
+void VirtualSocketServer::PurgeNetworkPackets(VirtualSocket* socket,
+                                              int64_t cur_time) {
+  while (!socket->network_.empty() &&
+         (socket->network_.front().done_time <= cur_time)) {
+    RTC_DCHECK(socket->network_size_ >= socket->network_.front().size);
+    socket->network_size_ -= socket->network_.front().size;
+    socket->network_.pop_front();
+  }
+}
+
+uint32_t VirtualSocketServer::SendDelay(uint32_t size) {
+  if (bandwidth_ == 0)
+    return 0;
+  else
+    return 1000 * size / bandwidth_;
+}
+
+#if 0
+void PrintFunction(std::vector<std::pair<double, double> >* f) {
+  return;
+  double sum = 0;
+  for (uint32_t i = 0; i < f->size(); ++i) {
+    std::cout << (*f)[i].first << '\t' << (*f)[i].second << std::endl;
+    sum += (*f)[i].second;
+  }
+  if (!f->empty()) {
+    const double mean = sum / f->size();
+    double sum_sq_dev = 0;
+    for (uint32_t i = 0; i < f->size(); ++i) {
+      double dev = (*f)[i].second - mean;
+      sum_sq_dev += dev * dev;
+    }
+    std::cout << "Mean = " << mean << " StdDev = "
+              << sqrt(sum_sq_dev / f->size()) << std::endl;
+  }
+}
+#endif  // <unused>
+
+void VirtualSocketServer::UpdateDelayDistribution() {
+  Function* dist = CreateDistribution(delay_mean_, delay_stddev_,
+                                      delay_samples_);
+  // We take a lock just to make sure we don't leak memory.
+  {
+    CritScope cs(&delay_crit_);
+    delay_dist_.reset(dist);
+  }
+}
+
+static double PI = 4 * atan(1.0);
+
+static double Normal(double x, double mean, double stddev) {
+  double a = (x - mean) * (x - mean) / (2 * stddev * stddev);
+  return exp(-a) / (stddev * sqrt(2 * PI));
+}
+
+#if 0  // static unused gives a warning
+static double Pareto(double x, double min, double k) {
+  if (x < min)
+    return 0;
+  else
+    return k * std::pow(min, k) / std::pow(x, k+1);
+}
+#endif
+
+VirtualSocketServer::Function* VirtualSocketServer::CreateDistribution(
+    uint32_t mean,
+    uint32_t stddev,
+    uint32_t samples) {
+  Function* f = new Function();
+
+  if (0 == stddev) {
+    f->push_back(Point(mean, 1.0));
+  } else {
+    double start = 0;
+    if (mean >= 4 * static_cast<double>(stddev))
+      start = mean - 4 * static_cast<double>(stddev);
+    double end = mean + 4 * static_cast<double>(stddev);
+
+    for (uint32_t i = 0; i < samples; i++) {
+      double x = start + (end - start) * i / (samples - 1);
+      double y = Normal(x, mean, stddev);
+      f->push_back(Point(x, y));
+    }
+  }
+  return Resample(Invert(Accumulate(f)), 0, 1, samples);
+}
+
+uint32_t VirtualSocketServer::GetTransitDelay(Socket* socket) {
+  // Use the delay based on the address if it is set.
+  auto iter = delay_by_ip_.find(socket->GetLocalAddress().ipaddr());
+  if (iter != delay_by_ip_.end()) {
+    return static_cast<uint32_t>(iter->second);
+  }
+  // Otherwise, use the delay from the distribution distribution.
+  size_t index = rand() % delay_dist_->size();
+  double delay = (*delay_dist_)[index].second;
+  // LOG_F(LS_INFO) << "random[" << index << "] = " << delay;
+  return static_cast<uint32_t>(delay);
+}
+
+struct FunctionDomainCmp {
+  bool operator()(const VirtualSocketServer::Point& p1,
+                   const VirtualSocketServer::Point& p2) {
+    return p1.first < p2.first;
+  }
+  bool operator()(double v1, const VirtualSocketServer::Point& p2) {
+    return v1 < p2.first;
+  }
+  bool operator()(const VirtualSocketServer::Point& p1, double v2) {
+    return p1.first < v2;
+  }
+};
+
+VirtualSocketServer::Function* VirtualSocketServer::Accumulate(Function* f) {
+  RTC_DCHECK(f->size() >= 1);
+  double v = 0;
+  for (Function::size_type i = 0; i < f->size() - 1; ++i) {
+    double dx = (*f)[i + 1].first - (*f)[i].first;
+    double avgy = ((*f)[i + 1].second + (*f)[i].second) / 2;
+    (*f)[i].second = v;
+    v = v + dx * avgy;
+  }
+  (*f)[f->size()-1].second = v;
+  return f;
+}
+
+VirtualSocketServer::Function* VirtualSocketServer::Invert(Function* f) {
+  for (Function::size_type i = 0; i < f->size(); ++i)
+    std::swap((*f)[i].first, (*f)[i].second);
+
+  std::sort(f->begin(), f->end(), FunctionDomainCmp());
+  return f;
+}
+
+VirtualSocketServer::Function* VirtualSocketServer::Resample(Function* f,
+                                                             double x1,
+                                                             double x2,
+                                                             uint32_t samples) {
+  Function* g = new Function();
+
+  for (size_t i = 0; i < samples; i++) {
+    double x = x1 + (x2 - x1) * i / (samples - 1);
+    double y = Evaluate(f, x);
+    g->push_back(Point(x, y));
+  }
+
+  delete f;
+  return g;
+}
+
+double VirtualSocketServer::Evaluate(Function* f, double x) {
+  Function::iterator iter =
+      std::lower_bound(f->begin(), f->end(), x, FunctionDomainCmp());
+  if (iter == f->begin()) {
+    return (*f)[0].second;
+  } else if (iter == f->end()) {
+    RTC_DCHECK(f->size() >= 1);
+    return (*f)[f->size() - 1].second;
+  } else if (iter->first == x) {
+    return iter->second;
+  } else {
+    double x1 = (iter - 1)->first;
+    double y1 = (iter - 1)->second;
+    double x2 = iter->first;
+    double y2 = iter->second;
+    return y1 + (y2 - y1) * (x - x1) / (x2 - x1);
+  }
+}
+
+bool VirtualSocketServer::CanInteractWith(VirtualSocket* local,
+                                          VirtualSocket* remote) {
+  if (!local || !remote) {
+    return false;
+  }
+  IPAddress local_ip = local->GetLocalAddress().ipaddr();
+  IPAddress remote_ip = remote->GetLocalAddress().ipaddr();
+  IPAddress local_normalized = local_ip.Normalized();
+  IPAddress remote_normalized = remote_ip.Normalized();
+  // Check if the addresses are the same family after Normalization (turns
+  // mapped IPv6 address into IPv4 addresses).
+  // This will stop unmapped V6 addresses from talking to mapped V6 addresses.
+  if (local_normalized.family() == remote_normalized.family()) {
+    return true;
+  }
+
+  // If ip1 is IPv4 and ip2 is :: and ip2 is not IPV6_V6ONLY.
+  int remote_v6_only = 0;
+  remote->GetOption(Socket::OPT_IPV6_V6ONLY, &remote_v6_only);
+  if (local_ip.family() == AF_INET && !remote_v6_only && IPIsAny(remote_ip)) {
+    return true;
+  }
+  // Same check, backwards.
+  int local_v6_only = 0;
+  local->GetOption(Socket::OPT_IPV6_V6ONLY, &local_v6_only);
+  if (remote_ip.family() == AF_INET && !local_v6_only && IPIsAny(local_ip)) {
+    return true;
+  }
+
+  // Check to see if either socket was explicitly bound to IPv6-any.
+  // These sockets can talk with anyone.
+  if (local_ip.family() == AF_INET6 && local->was_any()) {
+    return true;
+  }
+  if (remote_ip.family() == AF_INET6 && remote->was_any()) {
+    return true;
+  }
+
+  return false;
+}
+
+IPAddress VirtualSocketServer::GetDefaultRoute(int family) {
+  if (family == AF_INET) {
+    return default_route_v4_;
+  }
+  if (family == AF_INET6) {
+    return default_route_v6_;
+  }
+  return IPAddress();
+}
+void VirtualSocketServer::SetDefaultRoute(const IPAddress& from_addr) {
+  RTC_DCHECK(!IPIsAny(from_addr));
+  if (from_addr.family() == AF_INET) {
+    default_route_v4_ = from_addr;
+  } else if (from_addr.family() == AF_INET6) {
+    default_route_v6_ = from_addr;
+  }
+}
+
+}  // namespace rtc
diff --git a/base/virtualsocketserver.h b/base/virtualsocketserver.h
index 31ce96d..0ab2af2 100644
--- a/base/virtualsocketserver.h
+++ b/base/virtualsocketserver.h
@@ -11,9 +11,390 @@
 #ifndef WEBRTC_BASE_VIRTUALSOCKETSERVER_H_
 #define WEBRTC_BASE_VIRTUALSOCKETSERVER_H_
 
+#include <deque>
+#include <map>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/virtualsocketserver.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/fakeclock.h"
+#include "webrtc/base/messagequeue.h"
+#include "webrtc/base/socketserver.h"
+
+namespace rtc {
+
+class Packet;
+class VirtualSocket;
+class SocketAddressPair;
+
+// Simulates a network in the same manner as a loopback interface.  The
+// interface can create as many addresses as you want.  All of the sockets
+// created by this network will be able to communicate with one another, unless
+// they are bound to addresses from incompatible families.
+class VirtualSocketServer : public SocketServer, public sigslot::has_slots<> {
+ public:
+  VirtualSocketServer();
+  // This constructor needs to be used if the test uses a fake clock and
+  // ProcessMessagesUntilIdle, since ProcessMessagesUntilIdle needs a way of
+  // advancing time.
+  explicit VirtualSocketServer(FakeClock* fake_clock);
+  ~VirtualSocketServer() override;
+
+  // The default route indicates which local address to use when a socket is
+  // bound to the 'any' address, e.g. 0.0.0.0.
+  IPAddress GetDefaultRoute(int family);
+  void SetDefaultRoute(const IPAddress& from_addr);
+
+  // Limits the network bandwidth (maximum bytes per second).  Zero means that
+  // all sends occur instantly.  Defaults to 0.
+  uint32_t bandwidth() const { return bandwidth_; }
+  void set_bandwidth(uint32_t bandwidth) { bandwidth_ = bandwidth; }
+
+  // Limits the amount of data which can be in flight on the network without
+  // packet loss (on a per sender basis).  Defaults to 64 KB.
+  uint32_t network_capacity() const { return network_capacity_; }
+  void set_network_capacity(uint32_t capacity) { network_capacity_ = capacity; }
+
+  // The amount of data which can be buffered by tcp on the sender's side
+  uint32_t send_buffer_capacity() const { return send_buffer_capacity_; }
+  void set_send_buffer_capacity(uint32_t capacity) {
+    send_buffer_capacity_ = capacity;
+  }
+
+  // The amount of data which can be buffered by tcp on the receiver's side
+  uint32_t recv_buffer_capacity() const { return recv_buffer_capacity_; }
+  void set_recv_buffer_capacity(uint32_t capacity) {
+    recv_buffer_capacity_ = capacity;
+  }
+
+  // Controls the (transit) delay for packets sent in the network.  This does
+  // not inclue the time required to sit in the send queue.  Both of these
+  // values are measured in milliseconds.  Defaults to no delay.
+  uint32_t delay_mean() const { return delay_mean_; }
+  uint32_t delay_stddev() const { return delay_stddev_; }
+  uint32_t delay_samples() const { return delay_samples_; }
+  void set_delay_mean(uint32_t delay_mean) { delay_mean_ = delay_mean; }
+  void set_delay_stddev(uint32_t delay_stddev) { delay_stddev_ = delay_stddev; }
+  void set_delay_samples(uint32_t delay_samples) {
+    delay_samples_ = delay_samples;
+  }
+
+  // If the (transit) delay parameters are modified, this method should be
+  // called to recompute the new distribution.
+  void UpdateDelayDistribution();
+
+  // Controls the (uniform) probability that any sent packet is dropped.  This
+  // is separate from calculations to drop based on queue size.
+  double drop_probability() { return drop_prob_; }
+  void set_drop_probability(double drop_prob) {
+    RTC_DCHECK_GE(drop_prob, 0.0);
+    RTC_DCHECK_LE(drop_prob, 1.0);
+    drop_prob_ = drop_prob;
+  }
+
+  // If |blocked| is true, subsequent attempts to send will result in -1 being
+  // returned, with the socket error set to EWOULDBLOCK.
+  //
+  // If this method is later called with |blocked| set to false, any sockets
+  // that previously failed to send with EWOULDBLOCK will emit SignalWriteEvent.
+  //
+  // This can be used to simulate the send buffer on a network interface being
+  // full, and test functionality related to EWOULDBLOCK/SignalWriteEvent.
+  void SetSendingBlocked(bool blocked);
+
+  // SocketFactory:
+  Socket* CreateSocket(int type) override;
+  Socket* CreateSocket(int family, int type) override;
+
+  AsyncSocket* CreateAsyncSocket(int type) override;
+  AsyncSocket* CreateAsyncSocket(int family, int type) override;
+
+  // SocketServer:
+  void SetMessageQueue(MessageQueue* queue) override;
+  bool Wait(int cms, bool process_io) override;
+  void WakeUp() override;
+
+  void SetDelayOnAddress(const rtc::SocketAddress& address, int delay_ms) {
+    delay_by_ip_[address.ipaddr()] = delay_ms;
+  }
+
+  typedef std::pair<double, double> Point;
+  typedef std::vector<Point> Function;
+
+  static Function* CreateDistribution(uint32_t mean,
+                                      uint32_t stddev,
+                                      uint32_t samples);
+
+  // Similar to Thread::ProcessMessages, but it only processes messages until
+  // there are no immediate messages or pending network traffic.  Returns false
+  // if Thread::Stop() was called.
+  bool ProcessMessagesUntilIdle();
+
+  // Sets the next port number to use for testing.
+  void SetNextPortForTesting(uint16_t port);
+
+  // Close a pair of Tcp connections by addresses. Both connections will have
+  // its own OnClose invoked.
+  bool CloseTcpConnections(const SocketAddress& addr_local,
+                           const SocketAddress& addr_remote);
+
+  // For testing purpose only. Fired when a client socket is created.
+  sigslot::signal1<VirtualSocket*> SignalSocketCreated;
+
+ protected:
+  // Returns a new IP not used before in this network.
+  IPAddress GetNextIP(int family);
+  uint16_t GetNextPort();
+
+  VirtualSocket* CreateSocketInternal(int family, int type);
+
+  // Binds the given socket to addr, assigning and IP and Port if necessary
+  int Bind(VirtualSocket* socket, SocketAddress* addr);
+
+  // Binds the given socket to the given (fully-defined) address.
+  int Bind(VirtualSocket* socket, const SocketAddress& addr);
+
+  // Find the socket bound to the given address
+  VirtualSocket* LookupBinding(const SocketAddress& addr);
+
+  int Unbind(const SocketAddress& addr, VirtualSocket* socket);
+
+  // Adds a mapping between this socket pair and the socket.
+  void AddConnection(const SocketAddress& client,
+                     const SocketAddress& server,
+                     VirtualSocket* socket);
+
+  // Find the socket pair corresponding to this server address.
+  VirtualSocket* LookupConnection(const SocketAddress& client,
+                                  const SocketAddress& server);
+
+  void RemoveConnection(const SocketAddress& client,
+                        const SocketAddress& server);
+
+  // Connects the given socket to the socket at the given address
+  int Connect(VirtualSocket* socket, const SocketAddress& remote_addr,
+              bool use_delay);
+
+  // Sends a disconnect message to the socket at the given address
+  bool Disconnect(VirtualSocket* socket);
+
+  // Sends the given packet to the socket at the given address (if one exists).
+  int SendUdp(VirtualSocket* socket, const char* data, size_t data_size,
+              const SocketAddress& remote_addr);
+
+  // Moves as much data as possible from the sender's buffer to the network
+  void SendTcp(VirtualSocket* socket);
+
+  // Places a packet on the network.
+  void AddPacketToNetwork(VirtualSocket* socket,
+                          VirtualSocket* recipient,
+                          int64_t cur_time,
+                          const char* data,
+                          size_t data_size,
+                          size_t header_size,
+                          bool ordered);
+
+  // Removes stale packets from the network
+  void PurgeNetworkPackets(VirtualSocket* socket, int64_t cur_time);
+
+  // Computes the number of milliseconds required to send a packet of this size.
+  uint32_t SendDelay(uint32_t size);
+
+  // If the delay has been set for the address of the socket, returns the set
+  // delay. Otherwise, returns a random transit delay chosen from the
+  // appropriate distribution.
+  uint32_t GetTransitDelay(Socket* socket);
+
+  // Basic operations on functions.  Those that return a function also take
+  // ownership of the function given (and hence, may modify or delete it).
+  static Function* Accumulate(Function* f);
+  static Function* Invert(Function* f);
+  static Function* Resample(Function* f,
+                            double x1,
+                            double x2,
+                            uint32_t samples);
+  static double Evaluate(Function* f, double x);
+
+  // Null out our message queue if it goes away. Necessary in the case where
+  // our lifetime is greater than that of the thread we are using, since we
+  // try to send Close messages for all connected sockets when we shutdown.
+  void OnMessageQueueDestroyed() { msg_queue_ = nullptr; }
+
+  // Determine if two sockets should be able to communicate.
+  // We don't (currently) specify an address family for sockets; instead,
+  // the currently bound address is used to infer the address family.
+  // Any socket that is not explicitly bound to an IPv4 address is assumed to be
+  // dual-stack capable.
+  // This function tests if two addresses can communicate, as well as the
+  // sockets to which they may be bound (the addresses may or may not yet be
+  // bound to the sockets).
+  // First the addresses are tested (after normalization):
+  // If both have the same family, then communication is OK.
+  // If only one is IPv4 then false, unless the other is bound to ::.
+  // This applies even if the IPv4 address is 0.0.0.0.
+  // The socket arguments are optional; the sockets are checked to see if they
+  // were explicitly bound to IPv6-any ('::'), and if so communication is
+  // permitted.
+  // NB: This scheme doesn't permit non-dualstack IPv6 sockets.
+  static bool CanInteractWith(VirtualSocket* local, VirtualSocket* remote);
+
+ private:
+  friend class VirtualSocket;
+
+  // Sending was previously blocked, but now isn't.
+  sigslot::signal0<> SignalReadyToSend;
+
+  typedef std::map<SocketAddress, VirtualSocket*> AddressMap;
+  typedef std::map<SocketAddressPair, VirtualSocket*> ConnectionMap;
+
+  // May be null if the test doesn't use a fake clock, or it does but doesn't
+  // use ProcessMessagesUntilIdle.
+  FakeClock* fake_clock_ = nullptr;
+
+  // Used to implement Wait/WakeUp.
+  Event wakeup_;
+  MessageQueue* msg_queue_;
+  bool stop_on_idle_;
+  in_addr next_ipv4_;
+  in6_addr next_ipv6_;
+  uint16_t next_port_;
+  AddressMap* bindings_;
+  ConnectionMap* connections_;
+
+  IPAddress default_route_v4_;
+  IPAddress default_route_v6_;
+
+  uint32_t bandwidth_;
+  uint32_t network_capacity_;
+  uint32_t send_buffer_capacity_;
+  uint32_t recv_buffer_capacity_;
+  uint32_t delay_mean_;
+  uint32_t delay_stddev_;
+  uint32_t delay_samples_;
+
+  std::map<rtc::IPAddress, int> delay_by_ip_;
+  std::unique_ptr<Function> delay_dist_;
+
+  CriticalSection delay_crit_;
+
+  double drop_prob_;
+  bool sending_blocked_ = false;
+  RTC_DISALLOW_COPY_AND_ASSIGN(VirtualSocketServer);
+};
+
+// Implements the socket interface using the virtual network.  Packets are
+// passed as messages using the message queue of the socket server.
+class VirtualSocket : public AsyncSocket,
+                      public MessageHandler,
+                      public sigslot::has_slots<> {
+ public:
+  VirtualSocket(VirtualSocketServer* server, int family, int type, bool async);
+  ~VirtualSocket() override;
+
+  SocketAddress GetLocalAddress() const override;
+  SocketAddress GetRemoteAddress() const override;
+
+  // Used by TurnPortTest to mimic a case where proxy returns local host address
+  // instead of the original one TurnPort was bound against. Please see WebRTC
+  // issue 3927 for more detail.
+  void SetAlternativeLocalAddress(const SocketAddress& addr);
+
+  int Bind(const SocketAddress& addr) override;
+  int Connect(const SocketAddress& addr) override;
+  int Close() override;
+  int Send(const void* pv, size_t cb) override;
+  int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override;
+  int Recv(void* pv, size_t cb, int64_t* timestamp) override;
+  int RecvFrom(void* pv,
+               size_t cb,
+               SocketAddress* paddr,
+               int64_t* timestamp) override;
+  int Listen(int backlog) override;
+  VirtualSocket* Accept(SocketAddress* paddr) override;
+
+  int GetError() const override;
+  void SetError(int error) override;
+  ConnState GetState() const override;
+  int GetOption(Option opt, int* value) override;
+  int SetOption(Option opt, int value) override;
+  void OnMessage(Message* pmsg) override;
+
+  bool was_any() { return was_any_; }
+  void set_was_any(bool was_any) { was_any_ = was_any; }
+
+  // For testing purpose only. Fired when client socket is bound to an address.
+  sigslot::signal2<VirtualSocket*, const SocketAddress&> SignalAddressReady;
+
+ private:
+  struct NetworkEntry {
+    size_t size;
+    int64_t done_time;
+  };
+
+  typedef std::deque<SocketAddress> ListenQueue;
+  typedef std::deque<NetworkEntry> NetworkQueue;
+  typedef std::vector<char> SendBuffer;
+  typedef std::list<Packet*> RecvBuffer;
+  typedef std::map<Option, int> OptionsMap;
+
+  int InitiateConnect(const SocketAddress& addr, bool use_delay);
+  void CompleteConnect(const SocketAddress& addr, bool notify);
+  int SendUdp(const void* pv, size_t cb, const SocketAddress& addr);
+  int SendTcp(const void* pv, size_t cb);
+
+  // Used by server sockets to set the local address without binding.
+  void SetLocalAddress(const SocketAddress& addr);
+
+  void OnSocketServerReadyToSend();
+
+  VirtualSocketServer* server_;
+  int type_;
+  bool async_;
+  ConnState state_;
+  int error_;
+  SocketAddress local_addr_;
+  SocketAddress alternative_local_addr_;
+  SocketAddress remote_addr_;
+
+  // Pending sockets which can be Accepted
+  ListenQueue* listen_queue_;
+
+  // Data which tcp has buffered for sending
+  SendBuffer send_buffer_;
+  // Set to false if the last attempt to send resulted in EWOULDBLOCK.
+  // Set back to true when the socket can send again.
+  bool ready_to_send_ = true;
+
+  // Critical section to protect the recv_buffer and queue_
+  CriticalSection crit_;
+
+  // Network model that enforces bandwidth and capacity constraints
+  NetworkQueue network_;
+  size_t network_size_;
+  // The scheduled delivery time of the last packet sent on this socket.
+  // It is used to ensure ordered delivery of packets sent on this socket.
+  int64_t last_delivery_time_ = 0;
+
+  // Data which has been received from the network
+  RecvBuffer recv_buffer_;
+  // The amount of data which is in flight or in recv_buffer_
+  size_t recv_buffer_size_;
+
+  // Is this socket bound?
+  bool bound_;
+
+  // When we bind a socket to Any, VSS's Bind gives it another address. For
+  // dual-stack sockets, we want to distinguish between sockets that were
+  // explicitly given a particular address and sockets that had one picked
+  // for them by VSS.
+  bool was_any_;
+
+  // Store the options that are set
+  OptionsMap options_map_;
+
+  friend class VirtualSocketServer;
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_VIRTUALSOCKETSERVER_H_
diff --git a/base/weak_ptr.cc b/base/weak_ptr.cc
new file mode 100644
index 0000000..8a07a87
--- /dev/null
+++ b/base/weak_ptr.cc
@@ -0,0 +1,87 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/weak_ptr.h"
+
+// The implementation is borrowed from chromium except that it does not
+// implement SupportsWeakPtr.
+
+namespace rtc {
+namespace internal {
+
+WeakReference::Flag::Flag() : is_valid_(true) {
+  // Flags only become bound when checked for validity, or invalidated,
+  // so that we can check that later validity/invalidation operations on
+  // the same Flag take place on the same sequence.
+  checker_.Detach();
+}
+
+void WeakReference::Flag::Invalidate() {
+  RTC_DCHECK(checker_.CalledSequentially())
+      << "WeakPtrs must be invalidated on the same sequence.";
+  is_valid_ = false;
+}
+
+bool WeakReference::Flag::IsValid() const {
+  RTC_DCHECK(checker_.CalledSequentially())
+      << "WeakPtrs must be checked on the same sequence.";
+  return is_valid_;
+}
+
+WeakReference::Flag::~Flag() {}
+
+WeakReference::WeakReference() {}
+
+WeakReference::WeakReference(const Flag* flag) : flag_(flag) {}
+
+WeakReference::~WeakReference() {}
+
+WeakReference::WeakReference(WeakReference&& other) = default;
+
+WeakReference::WeakReference(const WeakReference& other) = default;
+
+bool WeakReference::is_valid() const {
+  return flag_.get() && flag_->IsValid();
+}
+
+WeakReferenceOwner::WeakReferenceOwner() {
+  checker_.Detach();
+}
+
+WeakReferenceOwner::~WeakReferenceOwner() {
+  RTC_DCHECK(checker_.CalledSequentially());
+  Invalidate();
+}
+
+WeakReference WeakReferenceOwner::GetRef() const {
+  RTC_DCHECK(checker_.CalledSequentially());
+  // If we hold the last reference to the Flag then create a new one.
+  if (!HasRefs())
+    flag_ = new RefCountedObject<WeakReference::Flag>();
+
+  return WeakReference(flag_.get());
+}
+
+void WeakReferenceOwner::Invalidate() {
+  RTC_DCHECK(checker_.CalledSequentially());
+  if (flag_.get()) {
+    flag_->Invalidate();
+    flag_ = nullptr;
+  }
+}
+
+WeakPtrBase::WeakPtrBase() {}
+
+WeakPtrBase::~WeakPtrBase() {}
+
+WeakPtrBase::WeakPtrBase(const WeakReference& ref) : ref_(ref) {}
+
+}  // namespace internal
+}  // namespace rtc
diff --git a/base/weak_ptr.h b/base/weak_ptr.h
index 282a551..28789d0 100644
--- a/base/weak_ptr.h
+++ b/base/weak_ptr.h
@@ -11,9 +11,262 @@
 #ifndef WEBRTC_BASE_WEAK_PTR_H_
 #define WEBRTC_BASE_WEAK_PTR_H_
 
+#include <memory>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/weak_ptr.h"
+#include <utility>
+
+#include "webrtc/base/refcount.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+#include "webrtc/base/sequenced_task_checker.h"
+
+// The implementation is borrowed from chromium except that it does not
+// implement SupportsWeakPtr.
+
+// Weak pointers are pointers to an object that do not affect its lifetime,
+// and which may be invalidated (i.e. reset to nullptr) by the object, or its
+// owner, at any time, most commonly when the object is about to be deleted.
+
+// Weak pointers are useful when an object needs to be accessed safely by one
+// or more objects other than its owner, and those callers can cope with the
+// object vanishing and e.g. tasks posted to it being silently dropped.
+// Reference-counting such an object would complicate the ownership graph and
+// make it harder to reason about the object's lifetime.
+
+// EXAMPLE:
+//
+//  class Controller {
+//   public:
+//    Controller() : weak_factory_(this) {}
+//    void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
+//    void WorkComplete(const Result& result) { ... }
+//   private:
+//    // Member variables should appear before the WeakPtrFactory, to ensure
+//    // that any WeakPtrs to Controller are invalidated before its members
+//    // variable's destructors are executed, rendering them invalid.
+//    WeakPtrFactory<Controller> weak_factory_;
+//  };
+//
+//  class Worker {
+//   public:
+//    static void StartNew(const WeakPtr<Controller>& controller) {
+//      Worker* worker = new Worker(controller);
+//      // Kick off asynchronous processing...
+//    }
+//   private:
+//    Worker(const WeakPtr<Controller>& controller)
+//        : controller_(controller) {}
+//    void DidCompleteAsynchronousProcessing(const Result& result) {
+//      if (controller_)
+//        controller_->WorkComplete(result);
+//    }
+//    WeakPtr<Controller> controller_;
+//  };
+//
+// With this implementation a caller may use SpawnWorker() to dispatch multiple
+// Workers and subsequently delete the Controller, without waiting for all
+// Workers to have completed.
+
+// ------------------------- IMPORTANT: Thread-safety -------------------------
+
+// Weak pointers may be passed safely between threads, but must always be
+// dereferenced and invalidated on the same TaskQueue or thread, otherwise
+// checking the pointer would be racey.
+//
+// To ensure correct use, the first time a WeakPtr issued by a WeakPtrFactory
+// is dereferenced, the factory and its WeakPtrs become bound to the calling
+// TaskQueue/thread, and cannot be dereferenced or
+// invalidated on any other TaskQueue/thread. Bound WeakPtrs can still be handed
+// off to other TaskQueues, e.g. to use to post tasks back to object on the
+// bound sequence.
+//
+// Thus, at least one WeakPtr object must exist and have been dereferenced on
+// the correct thread to enforce that other WeakPtr objects will enforce they
+// are used on the desired thread.
+
+namespace rtc {
+
+namespace internal {
+
+class WeakReference {
+ public:
+  // Although Flag is bound to a specific sequence, it may be
+  // deleted from another via base::WeakPtr::~WeakPtr().
+  class Flag : public RefCountInterface {
+   public:
+    Flag();
+
+    void Invalidate();
+    bool IsValid() const;
+
+   private:
+    friend class RefCountedObject<Flag>;
+
+    ~Flag() override;
+
+    SequencedTaskChecker checker_;
+    bool is_valid_;
+  };
+
+  WeakReference();
+  explicit WeakReference(const Flag* flag);
+  ~WeakReference();
+
+  WeakReference(WeakReference&& other);
+  WeakReference(const WeakReference& other);
+  WeakReference& operator=(WeakReference&& other) = default;
+  WeakReference& operator=(const WeakReference& other) = default;
+
+  bool is_valid() const;
+
+ private:
+  scoped_refptr<const Flag> flag_;
+};
+
+class WeakReferenceOwner {
+ public:
+  WeakReferenceOwner();
+  ~WeakReferenceOwner();
+
+  WeakReference GetRef() const;
+
+  bool HasRefs() const { return flag_.get() && !flag_->HasOneRef(); }
+
+  void Invalidate();
+
+ private:
+  SequencedTaskChecker checker_;
+  mutable scoped_refptr<RefCountedObject<WeakReference::Flag>> flag_;
+};
+
+// This class simplifies the implementation of WeakPtr's type conversion
+// constructor by avoiding the need for a public accessor for ref_.  A
+// WeakPtr<T> cannot access the private members of WeakPtr<U>, so this
+// base class gives us a way to access ref_ in a protected fashion.
+class WeakPtrBase {
+ public:
+  WeakPtrBase();
+  ~WeakPtrBase();
+
+  WeakPtrBase(const WeakPtrBase& other) = default;
+  WeakPtrBase(WeakPtrBase&& other) = default;
+  WeakPtrBase& operator=(const WeakPtrBase& other) = default;
+  WeakPtrBase& operator=(WeakPtrBase&& other) = default;
+
+ protected:
+  explicit WeakPtrBase(const WeakReference& ref);
+
+  WeakReference ref_;
+};
+
+}  // namespace internal
+
+template <typename T>
+class WeakPtrFactory;
+
+template <typename T>
+class WeakPtr : public internal::WeakPtrBase {
+ public:
+  WeakPtr() : ptr_(nullptr) {}
+
+  // Allow conversion from U to T provided U "is a" T. Note that this
+  // is separate from the (implicit) copy and move constructors.
+  template <typename U>
+  WeakPtr(const WeakPtr<U>& other)
+      : internal::WeakPtrBase(other), ptr_(other.ptr_) {}
+  template <typename U>
+  WeakPtr(WeakPtr<U>&& other)
+      : internal::WeakPtrBase(std::move(other)), ptr_(other.ptr_) {}
+
+  T* get() const { return ref_.is_valid() ? ptr_ : nullptr; }
+
+  T& operator*() const {
+    RTC_DCHECK(get() != nullptr);
+    return *get();
+  }
+  T* operator->() const {
+    RTC_DCHECK(get() != nullptr);
+    return get();
+  }
+
+  void reset() {
+    ref_ = internal::WeakReference();
+    ptr_ = nullptr;
+  }
+
+  // Allow conditionals to test validity, e.g. if (weak_ptr) {...};
+  explicit operator bool() const { return get() != nullptr; }
+
+ private:
+  template <typename U>
+  friend class WeakPtr;
+  friend class WeakPtrFactory<T>;
+
+  WeakPtr(const internal::WeakReference& ref, T* ptr)
+      : internal::WeakPtrBase(ref), ptr_(ptr) {}
+
+  // This pointer is only valid when ref_.is_valid() is true.  Otherwise, its
+  // value is undefined (as opposed to nullptr).
+  T* ptr_;
+};
+
+// Allow callers to compare WeakPtrs against nullptr to test validity.
+template <class T>
+bool operator!=(const WeakPtr<T>& weak_ptr, std::nullptr_t) {
+  return !(weak_ptr == nullptr);
+}
+template <class T>
+bool operator!=(std::nullptr_t, const WeakPtr<T>& weak_ptr) {
+  return weak_ptr != nullptr;
+}
+template <class T>
+bool operator==(const WeakPtr<T>& weak_ptr, std::nullptr_t) {
+  return weak_ptr.get() == nullptr;
+}
+template <class T>
+bool operator==(std::nullptr_t, const WeakPtr<T>& weak_ptr) {
+  return weak_ptr == nullptr;
+}
+
+// A class may be composed of a WeakPtrFactory and thereby
+// control how it exposes weak pointers to itself.  This is helpful if you only
+// need weak pointers within the implementation of a class.  This class is also
+// useful when working with primitive types.  For example, you could have a
+// WeakPtrFactory<bool> that is used to pass around a weak reference to a bool.
+
+// Note that GetWeakPtr must be called on one and only one TaskQueue or thread
+// and the WeakPtr must only be dereferenced and invalidated on that same
+// TaskQueue/thread. A WeakPtr instance can be copied and posted to other
+// sequences though as long as it is not dereferenced (WeakPtr<T>::get()).
+template <class T>
+class WeakPtrFactory {
+ public:
+  explicit WeakPtrFactory(T* ptr) : ptr_(ptr) {}
+
+  ~WeakPtrFactory() { ptr_ = nullptr; }
+
+  WeakPtr<T> GetWeakPtr() {
+    RTC_DCHECK(ptr_);
+    return WeakPtr<T>(weak_reference_owner_.GetRef(), ptr_);
+  }
+
+  // Call this method to invalidate all existing weak pointers.
+  void InvalidateWeakPtrs() {
+    RTC_DCHECK(ptr_);
+    weak_reference_owner_.Invalidate();
+  }
+
+  // Call this method to determine if any weak pointers exist.
+  bool HasWeakPtrs() const {
+    RTC_DCHECK(ptr_);
+    return weak_reference_owner_.HasRefs();
+  }
+
+ private:
+  internal::WeakReferenceOwner weak_reference_owner_;
+  T* ptr_;
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(WeakPtrFactory);
+};
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_WEAK_PTR_H_
diff --git a/base/weak_ptr_unittest.cc b/base/weak_ptr_unittest.cc
new file mode 100644
index 0000000..a3cd1f8
--- /dev/null
+++ b/base/weak_ptr_unittest.cc
@@ -0,0 +1,233 @@
+/*
+ *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/task_queue.h"
+#include "webrtc/base/weak_ptr.h"
+
+namespace rtc {
+
+namespace {
+
+struct Base {
+  std::string member;
+};
+struct Derived : public Base {};
+
+struct Target {};
+
+struct Arrow {
+  WeakPtr<Target> target;
+};
+
+struct TargetWithFactory : public Target {
+  TargetWithFactory() : factory(this) {}
+  WeakPtrFactory<Target> factory;
+};
+
+}  // namespace
+
+TEST(WeakPtrFactoryTest, Basic) {
+  int data;
+  WeakPtrFactory<int> factory(&data);
+  WeakPtr<int> ptr = factory.GetWeakPtr();
+  EXPECT_EQ(&data, ptr.get());
+}
+
+TEST(WeakPtrFactoryTest, Comparison) {
+  int data;
+  WeakPtrFactory<int> factory(&data);
+  WeakPtr<int> ptr = factory.GetWeakPtr();
+  WeakPtr<int> ptr2 = ptr;
+  EXPECT_EQ(ptr.get(), ptr2.get());
+}
+
+TEST(WeakPtrFactoryTest, Move) {
+  int data;
+  WeakPtrFactory<int> factory(&data);
+  WeakPtr<int> ptr = factory.GetWeakPtr();
+  WeakPtr<int> ptr2 = factory.GetWeakPtr();
+  WeakPtr<int> ptr3 = std::move(ptr2);
+  EXPECT_NE(ptr.get(), ptr2.get());
+  EXPECT_EQ(ptr.get(), ptr3.get());
+}
+
+TEST(WeakPtrFactoryTest, OutOfScope) {
+  WeakPtr<int> ptr;
+  EXPECT_EQ(nullptr, ptr.get());
+  {
+    int data;
+    WeakPtrFactory<int> factory(&data);
+    ptr = factory.GetWeakPtr();
+    EXPECT_EQ(&data, ptr.get());
+  }
+  EXPECT_EQ(nullptr, ptr.get());
+}
+
+TEST(WeakPtrFactoryTest, Multiple) {
+  WeakPtr<int> a, b;
+  {
+    int data;
+    WeakPtrFactory<int> factory(&data);
+    a = factory.GetWeakPtr();
+    b = factory.GetWeakPtr();
+    EXPECT_EQ(&data, a.get());
+    EXPECT_EQ(&data, b.get());
+  }
+  EXPECT_EQ(nullptr, a.get());
+  EXPECT_EQ(nullptr, b.get());
+}
+
+TEST(WeakPtrFactoryTest, MultipleStaged) {
+  WeakPtr<int> a;
+  {
+    int data;
+    WeakPtrFactory<int> factory(&data);
+    a = factory.GetWeakPtr();
+    { WeakPtr<int> b = factory.GetWeakPtr(); }
+    EXPECT_NE(nullptr, a.get());
+  }
+  EXPECT_EQ(nullptr, a.get());
+}
+
+TEST(WeakPtrFactoryTest, Dereference) {
+  Base data;
+  data.member = "123456";
+  WeakPtrFactory<Base> factory(&data);
+  WeakPtr<Base> ptr = factory.GetWeakPtr();
+  EXPECT_EQ(&data, ptr.get());
+  EXPECT_EQ(data.member, (*ptr).member);
+  EXPECT_EQ(data.member, ptr->member);
+}
+
+TEST(WeakPtrFactoryTest, UpCast) {
+  Derived data;
+  WeakPtrFactory<Derived> factory(&data);
+  WeakPtr<Base> ptr = factory.GetWeakPtr();
+  ptr = factory.GetWeakPtr();
+  EXPECT_EQ(ptr.get(), &data);
+}
+
+TEST(WeakPtrTest, DefaultConstructor) {
+  WeakPtr<int> ptr;
+  EXPECT_EQ(nullptr, ptr.get());
+}
+
+TEST(WeakPtrFactoryTest, BooleanTesting) {
+  int data;
+  WeakPtrFactory<int> factory(&data);
+
+  WeakPtr<int> ptr_to_an_instance = factory.GetWeakPtr();
+  EXPECT_TRUE(ptr_to_an_instance);
+  EXPECT_FALSE(!ptr_to_an_instance);
+
+  if (ptr_to_an_instance) {
+  } else {
+    ADD_FAILURE() << "Pointer to an instance should result in true.";
+  }
+
+  if (!ptr_to_an_instance) {  // check for operator!().
+    ADD_FAILURE() << "Pointer to an instance should result in !x being false.";
+  }
+
+  WeakPtr<int> null_ptr;
+  EXPECT_FALSE(null_ptr);
+  EXPECT_TRUE(!null_ptr);
+
+  if (null_ptr) {
+    ADD_FAILURE() << "Null pointer should result in false.";
+  }
+
+  if (!null_ptr) {  // check for operator!().
+  } else {
+    ADD_FAILURE() << "Null pointer should result in !x being true.";
+  }
+}
+
+TEST(WeakPtrFactoryTest, ComparisonToNull) {
+  int data;
+  WeakPtrFactory<int> factory(&data);
+
+  WeakPtr<int> ptr_to_an_instance = factory.GetWeakPtr();
+  EXPECT_NE(nullptr, ptr_to_an_instance);
+  EXPECT_NE(ptr_to_an_instance, nullptr);
+
+  WeakPtr<int> null_ptr;
+  EXPECT_EQ(null_ptr, nullptr);
+  EXPECT_EQ(nullptr, null_ptr);
+}
+
+TEST(WeakPtrTest, InvalidateWeakPtrs) {
+  int data;
+  WeakPtrFactory<int> factory(&data);
+  WeakPtr<int> ptr = factory.GetWeakPtr();
+  EXPECT_EQ(&data, ptr.get());
+  EXPECT_TRUE(factory.HasWeakPtrs());
+  factory.InvalidateWeakPtrs();
+  EXPECT_EQ(nullptr, ptr.get());
+  EXPECT_FALSE(factory.HasWeakPtrs());
+
+  // Test that the factory can create new weak pointers after a
+  // InvalidateWeakPtrs call, and they remain valid until the next
+  // InvalidateWeakPtrs call.
+  WeakPtr<int> ptr2 = factory.GetWeakPtr();
+  EXPECT_EQ(&data, ptr2.get());
+  EXPECT_TRUE(factory.HasWeakPtrs());
+  factory.InvalidateWeakPtrs();
+  EXPECT_EQ(nullptr, ptr2.get());
+  EXPECT_FALSE(factory.HasWeakPtrs());
+}
+
+TEST(WeakPtrTest, HasWeakPtrs) {
+  int data;
+  WeakPtrFactory<int> factory(&data);
+  {
+    WeakPtr<int> ptr = factory.GetWeakPtr();
+    EXPECT_TRUE(factory.HasWeakPtrs());
+  }
+  EXPECT_FALSE(factory.HasWeakPtrs());
+}
+
+template <class T>
+std::unique_ptr<T> NewObjectCreatedOnTaskQueue() {
+  std::unique_ptr<T> obj;
+  TaskQueue queue("NewObjectCreatedOnTaskQueue");
+  Event event(false, false);
+  queue.PostTask([&event, &obj] {
+    obj.reset(new T());
+    event.Set();
+  });
+  EXPECT_TRUE(event.Wait(1000));
+  return obj;
+}
+
+TEST(WeakPtrTest, ObjectAndWeakPtrOnDifferentThreads) {
+  // Test that it is OK to create an object with a WeakPtrFactory one thread,
+  // but use it on another.  This tests that we do not trip runtime checks that
+  // ensure that a WeakPtr is not used by multiple threads.
+  std::unique_ptr<TargetWithFactory> target(
+      NewObjectCreatedOnTaskQueue<TargetWithFactory>());
+  WeakPtr<Target> weak_ptr = target->factory.GetWeakPtr();
+  EXPECT_EQ(target.get(), weak_ptr.get());
+}
+
+TEST(WeakPtrTest, WeakPtrInitiateAndUseOnDifferentThreads) {
+  // Test that it is OK to create an object that has a WeakPtr member on one
+  // thread, but use it on another.  This tests that we do not trip runtime
+  // checks that ensure that a WeakPtr is not used by multiple threads.
+  std::unique_ptr<Arrow> arrow(NewObjectCreatedOnTaskQueue<Arrow>());
+  TargetWithFactory target;
+  arrow->target = target.factory.GetWeakPtr();
+  EXPECT_EQ(&target, arrow->target.get());
+}
+
+}  // namespace rtc
diff --git a/base/win32.cc b/base/win32.cc
new file mode 100644
index 0000000..89970ec
--- /dev/null
+++ b/base/win32.cc
@@ -0,0 +1,461 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/win32.h"
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <algorithm>
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+
+namespace rtc {
+
+// Helper function declarations for inet_ntop/inet_pton.
+static const char* inet_ntop_v4(const void* src, char* dst, socklen_t size);
+static const char* inet_ntop_v6(const void* src, char* dst, socklen_t size);
+static int inet_pton_v4(const char* src, void* dst);
+static int inet_pton_v6(const char* src, void* dst);
+
+// Implementation of inet_ntop (create a printable representation of an
+// ip address). XP doesn't have its own inet_ntop, and
+// WSAAddressToString requires both IPv6 to be  installed and for Winsock
+// to be initialized.
+const char* win32_inet_ntop(int af, const void *src,
+                            char* dst, socklen_t size) {
+  if (!src || !dst) {
+    return nullptr;
+  }
+  switch (af) {
+    case AF_INET: {
+      return inet_ntop_v4(src, dst, size);
+    }
+    case AF_INET6: {
+      return inet_ntop_v6(src, dst, size);
+    }
+  }
+  return nullptr;
+}
+
+// As above, but for inet_pton. Implements inet_pton for v4 and v6.
+// Note that our inet_ntop will output normal 'dotted' v4 addresses only.
+int win32_inet_pton(int af, const char* src, void* dst) {
+  if (!src || !dst) {
+    return 0;
+  }
+  if (af == AF_INET) {
+    return inet_pton_v4(src, dst);
+  } else if (af == AF_INET6) {
+    return inet_pton_v6(src, dst);
+  }
+  return -1;
+}
+
+// Helper function for inet_ntop for IPv4 addresses.
+// Outputs "dotted-quad" decimal notation.
+const char* inet_ntop_v4(const void* src, char* dst, socklen_t size) {
+  if (size < INET_ADDRSTRLEN) {
+    return nullptr;
+  }
+  const struct in_addr* as_in_addr =
+      reinterpret_cast<const struct in_addr*>(src);
+  rtc::sprintfn(dst, size, "%d.%d.%d.%d",
+                      as_in_addr->S_un.S_un_b.s_b1,
+                      as_in_addr->S_un.S_un_b.s_b2,
+                      as_in_addr->S_un.S_un_b.s_b3,
+                      as_in_addr->S_un.S_un_b.s_b4);
+  return dst;
+}
+
+// Helper function for inet_ntop for IPv6 addresses.
+const char* inet_ntop_v6(const void* src, char* dst, socklen_t size) {
+  if (size < INET6_ADDRSTRLEN) {
+    return nullptr;
+  }
+  const uint16_t* as_shorts = reinterpret_cast<const uint16_t*>(src);
+  int runpos[8];
+  int current = 1;
+  int max = 0;
+  int maxpos = -1;
+  int run_array_size = arraysize(runpos);
+  // Run over the address marking runs of 0s.
+  for (int i = 0; i < run_array_size; ++i) {
+    if (as_shorts[i] == 0) {
+      runpos[i] = current;
+      if (current > max) {
+        maxpos = i;
+        max = current;
+      }
+      ++current;
+    } else {
+      runpos[i] = -1;
+      current = 1;
+    }
+  }
+
+  if (max > 0) {
+    int tmpmax = maxpos;
+    // Run back through, setting -1 for all but the longest run.
+    for (int i = run_array_size - 1; i >= 0; i--) {
+      if (i > tmpmax) {
+        runpos[i] = -1;
+      } else if (runpos[i] == -1) {
+        // We're less than maxpos, we hit a -1, so the 'good' run is done.
+        // Setting tmpmax -1 means all remaining positions get set to -1.
+        tmpmax = -1;
+      }
+    }
+  }
+
+  char* cursor = dst;
+  // Print IPv4 compatible and IPv4 mapped addresses using the IPv4 helper.
+  // These addresses have an initial run of either eight zero-bytes followed
+  // by 0xFFFF, or an initial run of ten zero-bytes.
+  if (runpos[0] == 1 && (maxpos == 5 ||
+                         (maxpos == 4 && as_shorts[5] == 0xFFFF))) {
+    *cursor++ = ':';
+    *cursor++ = ':';
+    if (maxpos == 4) {
+      cursor += rtc::sprintfn(cursor, INET6_ADDRSTRLEN - 2, "ffff:");
+    }
+    const struct in_addr* as_v4 =
+        reinterpret_cast<const struct in_addr*>(&(as_shorts[6]));
+    inet_ntop_v4(as_v4, cursor,
+                 static_cast<socklen_t>(INET6_ADDRSTRLEN - (cursor - dst)));
+  } else {
+    for (int i = 0; i < run_array_size; ++i) {
+      if (runpos[i] == -1) {
+        cursor += rtc::sprintfn(cursor,
+                                      INET6_ADDRSTRLEN - (cursor - dst),
+                                      "%x", NetworkToHost16(as_shorts[i]));
+        if (i != 7 && runpos[i + 1] != 1) {
+          *cursor++ = ':';
+        }
+      } else if (runpos[i] == 1) {
+        // Entered the run; print the colons and skip the run.
+        *cursor++ = ':';
+        *cursor++ = ':';
+        i += (max - 1);
+      }
+    }
+  }
+  return dst;
+}
+
+// Helper function for inet_pton for IPv4 addresses.
+// |src| points to a character string containing an IPv4 network address in
+// dotted-decimal format, "ddd.ddd.ddd.ddd", where ddd is a decimal number
+// of up to three digits in the range 0 to 255.
+// The address is converted and copied to dst,
+// which must be sizeof(struct in_addr) (4) bytes (32 bits) long.
+int inet_pton_v4(const char* src, void* dst) {
+  const int kIpv4AddressSize = 4;
+  int found = 0;
+  const char* src_pos = src;
+  unsigned char result[kIpv4AddressSize] = {0};
+
+  while (*src_pos != '\0') {
+    // strtol won't treat whitespace characters in the begining as an error,
+    // so check to ensure this is started with digit before passing to strtol.
+    if (!isdigit(*src_pos)) {
+      return 0;
+    }
+    char* end_pos;
+    long value = strtol(src_pos, &end_pos, 10);
+    if (value < 0 || value > 255 || src_pos == end_pos) {
+      return 0;
+    }
+    ++found;
+    if (found > kIpv4AddressSize) {
+      return 0;
+    }
+    result[found - 1] = static_cast<unsigned char>(value);
+    src_pos = end_pos;
+    if (*src_pos == '.') {
+      // There's more.
+      ++src_pos;
+    } else if (*src_pos != '\0') {
+      // If it's neither '.' nor '\0' then return fail.
+      return 0;
+    }
+  }
+  if (found != kIpv4AddressSize) {
+    return 0;
+  }
+  memcpy(dst, result, sizeof(result));
+  return 1;
+}
+
+// Helper function for inet_pton for IPv6 addresses.
+int inet_pton_v6(const char* src, void* dst) {
+  // sscanf will pick any other invalid chars up, but it parses 0xnnnn as hex.
+  // Check for literal x in the input string.
+  const char* readcursor = src;
+  char c = *readcursor++;
+  while (c) {
+    if (c == 'x') {
+      return 0;
+    }
+    c = *readcursor++;
+  }
+  readcursor = src;
+
+  struct in6_addr an_addr;
+  memset(&an_addr, 0, sizeof(an_addr));
+
+  uint16_t* addr_cursor = reinterpret_cast<uint16_t*>(&an_addr.s6_addr[0]);
+  uint16_t* addr_end = reinterpret_cast<uint16_t*>(&an_addr.s6_addr[16]);
+  bool seencompressed = false;
+
+  // Addresses that start with "::" (i.e., a run of initial zeros) or
+  // "::ffff:" can potentially be IPv4 mapped or compatibility addresses.
+  // These have dotted-style IPv4 addresses on the end (e.g. "::192.168.7.1").
+  if (*readcursor == ':' && *(readcursor+1) == ':' &&
+      *(readcursor + 2) != 0) {
+    // Check for periods, which we'll take as a sign of v4 addresses.
+    const char* addrstart = readcursor + 2;
+    if (rtc::strchr(addrstart, ".")) {
+      const char* colon = rtc::strchr(addrstart, "::");
+      if (colon) {
+        uint16_t a_short;
+        int bytesread = 0;
+        if (sscanf(addrstart, "%hx%n", &a_short, &bytesread) != 1 ||
+            a_short != 0xFFFF || bytesread != 4) {
+          // Colons + periods means has to be ::ffff:a.b.c.d. But it wasn't.
+          return 0;
+        } else {
+          an_addr.s6_addr[10] = 0xFF;
+          an_addr.s6_addr[11] = 0xFF;
+          addrstart = colon + 1;
+        }
+      }
+      struct in_addr v4;
+      if (inet_pton_v4(addrstart, &v4.s_addr)) {
+        memcpy(&an_addr.s6_addr[12], &v4, sizeof(v4));
+        memcpy(dst, &an_addr, sizeof(an_addr));
+        return 1;
+      } else {
+        // Invalid v4 address.
+        return 0;
+      }
+    }
+  }
+
+  // For addresses without a trailing IPv4 component ('normal' IPv6 addresses).
+  while (*readcursor != 0 && addr_cursor < addr_end) {
+    if (*readcursor == ':') {
+      if (*(readcursor + 1) == ':') {
+        if (seencompressed) {
+          // Can only have one compressed run of zeroes ("::") per address.
+          return 0;
+        }
+        // Hit a compressed run. Count colons to figure out how much of the
+        // address is skipped.
+        readcursor += 2;
+        const char* coloncounter = readcursor;
+        int coloncount = 0;
+        if (*coloncounter == 0) {
+          // Special case - trailing ::.
+          addr_cursor = addr_end;
+        } else {
+          while (*coloncounter) {
+            if (*coloncounter == ':') {
+              ++coloncount;
+            }
+            ++coloncounter;
+          }
+          // (coloncount + 1) is the number of shorts left in the address.
+          // If this number is greater than the number of available shorts, the
+          // address is malformed.
+          if (coloncount + 1 > addr_end - addr_cursor) {
+            return 0;
+          }
+          addr_cursor = addr_end - (coloncount + 1);
+          seencompressed = true;
+        }
+      } else {
+        ++readcursor;
+      }
+    } else {
+      uint16_t word;
+      int bytesread = 0;
+      if (sscanf(readcursor, "%4hx%n", &word, &bytesread) != 1) {
+        return 0;
+      } else {
+        *addr_cursor = HostToNetwork16(word);
+        ++addr_cursor;
+        readcursor += bytesread;
+        if (*readcursor != ':' && *readcursor != '\0') {
+          return 0;
+        }
+      }
+    }
+  }
+
+  if (*readcursor != '\0' || addr_cursor < addr_end) {
+    // Catches addresses too short or too long.
+    return 0;
+  }
+  memcpy(dst, &an_addr, sizeof(an_addr));
+  return 1;
+}
+
+//
+// Unix time is in seconds relative to 1/1/1970.  So we compute the windows
+// FILETIME of that time/date, then we add/subtract in appropriate units to
+// convert to/from unix time.
+// The units of FILETIME are 100ns intervals, so by multiplying by or dividing
+// by 10000000, we can convert to/from seconds.
+//
+// FileTime = UnixTime*10000000 + FileTime(1970)
+// UnixTime = (FileTime-FileTime(1970))/10000000
+//
+
+void FileTimeToUnixTime(const FILETIME& ft, time_t* ut) {
+  RTC_DCHECK(nullptr != ut);
+
+  // FILETIME has an earlier date base than time_t (1/1/1970), so subtract off
+  // the difference.
+  SYSTEMTIME base_st;
+  memset(&base_st, 0, sizeof(base_st));
+  base_st.wDay = 1;
+  base_st.wMonth = 1;
+  base_st.wYear = 1970;
+
+  FILETIME base_ft;
+  SystemTimeToFileTime(&base_st, &base_ft);
+
+  ULARGE_INTEGER base_ul, current_ul;
+  memcpy(&base_ul, &base_ft, sizeof(FILETIME));
+  memcpy(&current_ul, &ft, sizeof(FILETIME));
+
+  // Divide by big number to convert to seconds, then subtract out the 1970
+  // base date value.
+  const ULONGLONG RATIO = 10000000;
+  *ut = static_cast<time_t>((current_ul.QuadPart - base_ul.QuadPart) / RATIO);
+}
+
+void UnixTimeToFileTime(const time_t& ut, FILETIME* ft) {
+  RTC_DCHECK(nullptr != ft);
+
+  // FILETIME has an earlier date base than time_t (1/1/1970), so add in
+  // the difference.
+  SYSTEMTIME base_st;
+  memset(&base_st, 0, sizeof(base_st));
+  base_st.wDay = 1;
+  base_st.wMonth = 1;
+  base_st.wYear = 1970;
+
+  FILETIME base_ft;
+  SystemTimeToFileTime(&base_st, &base_ft);
+
+  ULARGE_INTEGER base_ul;
+  memcpy(&base_ul, &base_ft, sizeof(FILETIME));
+
+  // Multiply by big number to convert to 100ns units, then add in the 1970
+  // base date value.
+  const ULONGLONG RATIO = 10000000;
+  ULARGE_INTEGER current_ul;
+  current_ul.QuadPart = base_ul.QuadPart + static_cast<int64_t>(ut) * RATIO;
+  memcpy(ft, &current_ul, sizeof(FILETIME));
+}
+
+bool Utf8ToWindowsFilename(const std::string& utf8, std::wstring* filename) {
+  // TODO: Integrate into fileutils.h
+  // TODO: Handle wide and non-wide cases via TCHAR?
+  // TODO: Skip \\?\ processing if the length is not > MAX_PATH?
+  // TODO: Write unittests
+
+  // Convert to Utf16
+  int wlen =
+      ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
+                            static_cast<int>(utf8.length() + 1), nullptr, 0);
+  if (0 == wlen) {
+    return false;
+  }
+  wchar_t* wfilename = STACK_ARRAY(wchar_t, wlen);
+  if (0 == ::MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
+                                 static_cast<int>(utf8.length() + 1),
+                                 wfilename, wlen)) {
+    return false;
+  }
+  // Replace forward slashes with backslashes
+  std::replace(wfilename, wfilename + wlen, L'/', L'\\');
+  // Convert to complete filename
+  DWORD full_len = ::GetFullPathName(wfilename, 0, nullptr, nullptr);
+  if (0 == full_len) {
+    return false;
+  }
+  wchar_t* filepart = nullptr;
+  wchar_t* full_filename = STACK_ARRAY(wchar_t, full_len + 6);
+  wchar_t* start = full_filename + 6;
+  if (0 == ::GetFullPathName(wfilename, full_len, start, &filepart)) {
+    return false;
+  }
+  // Add long-path prefix
+  const wchar_t kLongPathPrefix[] = L"\\\\?\\UNC";
+  if ((start[0] != L'\\') || (start[1] != L'\\')) {
+    // Non-unc path:     <pathname>
+    //      Becomes: \\?\<pathname>
+    start -= 4;
+    RTC_DCHECK(start >= full_filename);
+    memcpy(start, kLongPathPrefix, 4 * sizeof(wchar_t));
+  } else if (start[2] != L'?') {
+    // Unc path:       \\<server>\<pathname>
+    //  Becomes: \\?\UNC\<server>\<pathname>
+    start -= 6;
+    RTC_DCHECK(start >= full_filename);
+    memcpy(start, kLongPathPrefix, 7 * sizeof(wchar_t));
+  } else {
+    // Already in long-path form.
+  }
+  filename->assign(start);
+  return true;
+}
+
+bool GetOsVersion(int* major, int* minor, int* build) {
+  OSVERSIONINFO info = {0};
+  info.dwOSVersionInfoSize = sizeof(info);
+  if (GetVersionEx(&info)) {
+    if (major) *major = info.dwMajorVersion;
+    if (minor) *minor = info.dwMinorVersion;
+    if (build) *build = info.dwBuildNumber;
+    return true;
+  }
+  return false;
+}
+
+bool GetCurrentProcessIntegrityLevel(int* level) {
+  bool ret = false;
+  HANDLE process = ::GetCurrentProcess(), token;
+  if (OpenProcessToken(process, TOKEN_QUERY | TOKEN_QUERY_SOURCE, &token)) {
+    DWORD size;
+    if (!GetTokenInformation(token, TokenIntegrityLevel, nullptr, 0, &size) &&
+        GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+      char* buf = STACK_ARRAY(char, size);
+      TOKEN_MANDATORY_LABEL* til =
+          reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buf);
+      if (GetTokenInformation(token, TokenIntegrityLevel, til, size, &size)) {
+
+        DWORD count = *GetSidSubAuthorityCount(til->Label.Sid);
+        *level = *GetSidSubAuthority(til->Label.Sid, count - 1);
+        ret = true;
+      }
+    }
+    CloseHandle(token);
+  }
+  return ret;
+}
+
+}  // namespace rtc
diff --git a/base/win32.h b/base/win32.h
index 413bd11..ad8a43d 100644
--- a/base/win32.h
+++ b/base/win32.h
@@ -11,9 +11,118 @@
 #ifndef WEBRTC_BASE_WIN32_H_
 #define WEBRTC_BASE_WIN32_H_
 
+#if defined(WEBRTC_WIN)
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/win32.h"
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
 
+// Make sure we don't get min/max macros
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#include <winsock2.h>
+#include <windows.h>
+
+#ifndef SECURITY_MANDATORY_LABEL_AUTHORITY
+// Add defines that we use if we are compiling against older sdks
+#define SECURITY_MANDATORY_MEDIUM_RID               (0x00002000L)
+#define TokenIntegrityLevel static_cast<TOKEN_INFORMATION_CLASS>(0x19)
+typedef struct _TOKEN_MANDATORY_LABEL {
+    SID_AND_ATTRIBUTES Label;
+} TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL;
+#endif  // SECURITY_MANDATORY_LABEL_AUTHORITY
+
+#undef SetPort
+
+#include <string>
+
+#include "webrtc/base/stringutils.h"
+#include "webrtc/base/basictypes.h"
+
+namespace rtc {
+
+const char* win32_inet_ntop(int af, const void *src, char* dst, socklen_t size);
+int win32_inet_pton(int af, const char* src, void *dst);
+
+inline std::wstring ToUtf16(const char* utf8, size_t len) {
+  int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(len),
+                                    nullptr, 0);
+  wchar_t* ws = STACK_ARRAY(wchar_t, len16);
+  ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(len), ws, len16);
+  return std::wstring(ws, len16);
+}
+
+inline std::wstring ToUtf16(const std::string& str) {
+  return ToUtf16(str.data(), str.length());
+}
+
+inline std::string ToUtf8(const wchar_t* wide, size_t len) {
+  int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(len),
+                                   nullptr, 0, nullptr, nullptr);
+  char* ns = STACK_ARRAY(char, len8);
+  ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(len), ns, len8,
+                        nullptr, nullptr);
+  return std::string(ns, len8);
+}
+
+inline std::string ToUtf8(const wchar_t* wide) {
+  return ToUtf8(wide, wcslen(wide));
+}
+
+inline std::string ToUtf8(const std::wstring& wstr) {
+  return ToUtf8(wstr.data(), wstr.length());
+}
+
+// Convert FILETIME to time_t
+void FileTimeToUnixTime(const FILETIME& ft, time_t* ut);
+
+// Convert time_t to FILETIME
+void UnixTimeToFileTime(const time_t& ut, FILETIME * ft);
+
+// Convert a Utf8 path representation to a non-length-limited Unicode pathname.
+bool Utf8ToWindowsFilename(const std::string& utf8, std::wstring* filename);
+
+// Convert a FILETIME to a UInt64
+inline uint64_t ToUInt64(const FILETIME& ft) {
+  ULARGE_INTEGER r = {{ft.dwLowDateTime, ft.dwHighDateTime}};
+  return r.QuadPart;
+}
+
+enum WindowsMajorVersions {
+  kWindows2000 = 5,
+  kWindowsVista = 6,
+};
+bool GetOsVersion(int* major, int* minor, int* build);
+
+inline bool IsWindowsVistaOrLater() {
+  int major;
+  return (GetOsVersion(&major, nullptr, nullptr) && major >= kWindowsVista);
+}
+
+inline bool IsWindowsXpOrLater() {
+  int major, minor;
+  return (GetOsVersion(&major, &minor, nullptr) &&
+          (major >= kWindowsVista || (major == kWindows2000 && minor >= 1)));
+}
+
+inline bool IsWindows8OrLater() {
+  int major, minor;
+  return (GetOsVersion(&major, &minor, nullptr) &&
+          (major > kWindowsVista || (major == kWindowsVista && minor >= 2)));
+}
+
+// Determine the current integrity level of the process.
+bool GetCurrentProcessIntegrityLevel(int* level);
+
+inline bool IsCurrentProcessLowIntegrity() {
+  int level;
+  return (GetCurrentProcessIntegrityLevel(&level) &&
+      level < SECURITY_MANDATORY_MEDIUM_RID);
+}
+
+}  // namespace rtc
+
+#endif  // WEBRTC_WIN
 #endif  // WEBRTC_BASE_WIN32_H_
diff --git a/base/win32_unittest.cc b/base/win32_unittest.cc
new file mode 100644
index 0000000..c41ca86
--- /dev/null
+++ b/base/win32_unittest.cc
@@ -0,0 +1,98 @@
+/*
+ *  Copyright 2010 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/nethelpers.h"
+#include "webrtc/base/win32.h"
+
+#if !defined(WEBRTC_WIN)
+#error Only for Windows
+#endif
+
+namespace rtc {
+
+class Win32Test : public testing::Test {
+ public:
+  Win32Test() {
+  }
+};
+
+TEST_F(Win32Test, FileTimeToUInt64Test) {
+  FILETIME ft;
+  ft.dwHighDateTime = 0xBAADF00D;
+  ft.dwLowDateTime = 0xFEED3456;
+
+  uint64_t expected = 0xBAADF00DFEED3456;
+  EXPECT_EQ(expected, ToUInt64(ft));
+}
+
+TEST_F(Win32Test, IPv6AddressCompression) {
+  IPAddress ipv6;
+
+  // Zero compression should be done on the leftmost 0s when there are
+  // multiple longest series.
+  ASSERT_TRUE(IPFromString("2a00:8a00:a000:1190:0000:0001:000:252", &ipv6));
+  EXPECT_EQ("2a00:8a00:a000:1190::1:0:252", ipv6.ToString());
+
+  // Ensure the zero compression could handle multiple octects.
+  ASSERT_TRUE(IPFromString("0:0:0:0:0:0:0:1", &ipv6));
+  EXPECT_EQ("::1", ipv6.ToString());
+
+  // Make sure multiple 0 octects compressed.
+  ASSERT_TRUE(IPFromString("fe80:0:0:0:2aa:ff:fe9a:4ca2", &ipv6));
+  EXPECT_EQ("fe80::2aa:ff:fe9a:4ca2", ipv6.ToString());
+
+  // Test zero compression at the end of string.
+  ASSERT_TRUE(IPFromString("2a00:8a00:a000:1190:0000:0001:000:00", &ipv6));
+  EXPECT_EQ("2a00:8a00:a000:1190:0:1::", ipv6.ToString());
+
+  // Test zero compression at the beginning of string.
+  ASSERT_TRUE(IPFromString("0:0:000:1190:0000:0001:000:00", &ipv6));
+  EXPECT_EQ("::1190:0:1:0:0", ipv6.ToString());
+
+  // Test zero compression only done once.
+  ASSERT_TRUE(IPFromString("0:1:000:1190:0000:0001:000:01", &ipv6));
+  EXPECT_EQ("::1:0:1190:0:1:0:1", ipv6.ToString());
+
+  // Make sure noncompressable IPv6 is the same.
+  ASSERT_TRUE(IPFromString("1234:5678:abcd:1234:5678:abcd:1234:5678", &ipv6));
+  EXPECT_EQ("1234:5678:abcd:1234:5678:abcd:1234:5678", ipv6.ToString());
+}
+
+// Test that invalid IPv6 addresses are recognized and false is returned.
+TEST_F(Win32Test, InvalidIPv6AddressParsing) {
+  IPAddress ipv6;
+
+  // More than 1 run of "::"s.
+  EXPECT_FALSE(IPFromString("1::2::3", &ipv6));
+
+  // More than 1 run of "::"s in a longer address.
+  // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=7592
+  EXPECT_FALSE(IPFromString("1::2::3::4::5::6::7::8", &ipv6));
+
+  // Three ':'s in a row.
+  EXPECT_FALSE(IPFromString("1:::2", &ipv6));
+
+  // Non-hex character.
+  EXPECT_FALSE(IPFromString("test::1", &ipv6));
+
+  // More than 4 hex digits per group.
+  EXPECT_FALSE(IPFromString("abcde::1", &ipv6));
+
+  // More than 8 groups.
+  EXPECT_FALSE(IPFromString("1:2:3:4:5:6:7:8:9", &ipv6));
+
+  // Less than 8 groups.
+  EXPECT_FALSE(IPFromString("1:2:3:4:5:6:7", &ipv6));
+}
+
+}  // namespace rtc
diff --git a/base/win32filesystem.cc b/base/win32filesystem.cc
new file mode 100644
index 0000000..5445140
--- /dev/null
+++ b/base/win32filesystem.cc
@@ -0,0 +1,155 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/win32filesystem.h"
+
+#include "webrtc/base/win32.h"
+#include <shellapi.h>
+#include <shlobj.h>
+#include <tchar.h>
+
+#include <memory>
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/fileutils.h"
+#include "webrtc/base/pathutils.h"
+#include "webrtc/base/stream.h"
+#include "webrtc/base/stringutils.h"
+
+// In several places in this file, we test the integrity level of the process
+// before calling GetLongPathName. We do this because calling GetLongPathName
+// when running under protected mode IE (a low integrity process) can result in
+// a virtualized path being returned, which is wrong if you only plan to read.
+// TODO: Waiting to hear back from IE team on whether this is the
+// best approach; IEIsProtectedModeProcess is another possible solution.
+
+namespace rtc {
+
+bool Win32Filesystem::CreateFolder(const Pathname &pathname) {
+  if (pathname.pathname().empty() || !pathname.filename().empty())
+    return false;
+
+  std::wstring path16;
+  if (!Utf8ToWindowsFilename(pathname.pathname(), &path16))
+    return false;
+
+  DWORD res = ::GetFileAttributes(path16.c_str());
+  if (res != INVALID_FILE_ATTRIBUTES) {
+    // Something exists at this location, check if it is a directory
+    return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0);
+  } else if ((GetLastError() != ERROR_FILE_NOT_FOUND)
+              && (GetLastError() != ERROR_PATH_NOT_FOUND)) {
+    // Unexpected error
+    return false;
+  }
+
+  // Directory doesn't exist, look up one directory level
+  if (!pathname.parent_folder().empty()) {
+    Pathname parent(pathname);
+    parent.SetFolder(pathname.parent_folder());
+    if (!CreateFolder(parent)) {
+      return false;
+    }
+  }
+
+  return (::CreateDirectory(path16.c_str(), nullptr) != 0);
+}
+
+bool Win32Filesystem::DeleteFile(const Pathname &filename) {
+  LOG(LS_INFO) << "Deleting file " << filename.pathname();
+  if (!IsFile(filename)) {
+    RTC_DCHECK(IsFile(filename));
+    return false;
+  }
+  return ::DeleteFile(ToUtf16(filename.pathname()).c_str()) != 0;
+}
+
+bool Win32Filesystem::GetTemporaryFolder(Pathname &pathname, bool create,
+                                         const std::string *append) {
+  wchar_t buffer[MAX_PATH + 1];
+  if (!::GetTempPath(arraysize(buffer), buffer))
+    return false;
+  if (!IsCurrentProcessLowIntegrity() &&
+      !::GetLongPathName(buffer, buffer, arraysize(buffer)))
+    return false;
+  size_t len = strlen(buffer);
+  if ((len > 0) && (buffer[len-1] != '\\')) {
+    len += strcpyn(buffer + len, arraysize(buffer) - len, L"\\");
+  }
+  if (len >= arraysize(buffer) - 1)
+    return false;
+  pathname.clear();
+  pathname.SetFolder(ToUtf8(buffer));
+  if (append != nullptr) {
+    RTC_DCHECK(!append->empty());
+    pathname.AppendFolder(*append);
+  }
+  return !create || CreateFolder(pathname);
+}
+
+std::string Win32Filesystem::TempFilename(const Pathname &dir,
+                                          const std::string &prefix) {
+  wchar_t filename[MAX_PATH];
+  if (::GetTempFileName(ToUtf16(dir.pathname()).c_str(),
+                        ToUtf16(prefix).c_str(), 0, filename) != 0)
+    return ToUtf8(filename);
+  RTC_NOTREACHED();
+  return "";
+}
+
+bool Win32Filesystem::MoveFile(const Pathname &old_path,
+                               const Pathname &new_path) {
+  if (!IsFile(old_path)) {
+    RTC_DCHECK(IsFile(old_path));
+    return false;
+  }
+  LOG(LS_INFO) << "Moving " << old_path.pathname()
+               << " to " << new_path.pathname();
+  return ::MoveFile(ToUtf16(old_path.pathname()).c_str(),
+                    ToUtf16(new_path.pathname()).c_str()) != 0;
+}
+
+bool Win32Filesystem::IsFolder(const Pathname &path) {
+  WIN32_FILE_ATTRIBUTE_DATA data = {0};
+  if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
+                                 GetFileExInfoStandard, &data))
+    return false;
+  return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
+      FILE_ATTRIBUTE_DIRECTORY;
+}
+
+bool Win32Filesystem::IsFile(const Pathname &path) {
+  WIN32_FILE_ATTRIBUTE_DATA data = {0};
+  if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
+                                 GetFileExInfoStandard, &data))
+    return false;
+  return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
+}
+
+bool Win32Filesystem::IsAbsent(const Pathname& path) {
+  WIN32_FILE_ATTRIBUTE_DATA data = {0};
+  if (0 != ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
+                                 GetFileExInfoStandard, &data))
+    return false;
+  DWORD err = ::GetLastError();
+  return (ERROR_FILE_NOT_FOUND == err || ERROR_PATH_NOT_FOUND == err);
+}
+
+bool Win32Filesystem::GetFileSize(const Pathname &pathname, size_t *size) {
+  WIN32_FILE_ATTRIBUTE_DATA data = {0};
+  if (::GetFileAttributesEx(ToUtf16(pathname.pathname()).c_str(),
+                            GetFileExInfoStandard, &data) == 0)
+  return false;
+  *size = data.nFileSizeLow;
+  return true;
+}
+
+}  // namespace rtc
diff --git a/base/win32filesystem.h b/base/win32filesystem.h
index d647c44..566cbaf 100644
--- a/base/win32filesystem.h
+++ b/base/win32filesystem.h
@@ -8,12 +8,57 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_BASE_WIN32FILESYSTEM_H_
-#define WEBRTC_BASE_WIN32FILESYSTEM_H_
+#ifndef _WEBRTC_BASE_WIN32FILESYSTEM_H__
+#define _WEBRTC_BASE_WIN32FILESYSTEM_H__
 
+#include "fileutils.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/win32filesystem.h"
+namespace rtc {
 
-#endif  // WEBRTC_BASE_WIN32FILESYSTEM_H_
+class Win32Filesystem : public FilesystemInterface {
+ public:
+  // This will attempt to delete the path located at filename.
+  // If the path points to a folder, it will fail with VERIFY
+  bool DeleteFile(const Pathname& filename) override;
+
+  // Creates a directory. This will call itself recursively to create /foo/bar even if
+  // /foo does not exist.
+  // Returns TRUE if function succeeds
+  bool CreateFolder(const Pathname& pathname) override;
+
+  // This moves a file from old_path to new_path. If the new path is on a 
+  // different volume than the old, it will attempt to copy and then delete
+  // the folder
+  // Returns true if the file is successfully moved
+  bool MoveFile(const Pathname& old_path, const Pathname& new_path) override;
+
+  // Returns true if a pathname is a directory
+  bool IsFolder(const Pathname& pathname) override;
+
+  // Returns true if a file exists at path
+  bool IsFile(const Pathname& path) override;
+
+  // Returns true if pathname refers to no filesystem object, every parent
+  // directory either exists, or is also absent.
+  bool IsAbsent(const Pathname& pathname) override;
+
+  // All of the following functions set pathname and return true if successful.
+  // Returned paths always include a trailing backslash.
+  // If create is true, the path will be recursively created.
+  // If append is non-null, it will be appended (and possibly created).
+
+  std::string TempFilename(const Pathname& dir,
+                           const std::string& prefix) override;
+
+  bool GetFileSize(const Pathname& path, size_t* size) override;
+
+  // A folder appropriate for storing temporary files (Contents are
+  // automatically deleted when the program exists)
+  bool GetTemporaryFolder(Pathname& path,
+                          bool create,
+                          const std::string* append) override;
+};
+
+}  // namespace rtc
+
+#endif  // WEBRTC_WINFILESYSTEM_H__
diff --git a/base/win32securityerrors.cc b/base/win32securityerrors.cc
new file mode 100644
index 0000000..71fe466
--- /dev/null
+++ b/base/win32securityerrors.cc
@@ -0,0 +1,49 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/win32.h"
+#include "webrtc/base/logging.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern const ConstantLabel SECURITY_ERRORS[];
+
+const ConstantLabel SECURITY_ERRORS[] = {
+  KLABEL(SEC_I_COMPLETE_AND_CONTINUE),
+  KLABEL(SEC_I_COMPLETE_NEEDED),
+  KLABEL(SEC_I_CONTEXT_EXPIRED),
+  KLABEL(SEC_I_CONTINUE_NEEDED),
+  KLABEL(SEC_I_INCOMPLETE_CREDENTIALS),
+  KLABEL(SEC_I_RENEGOTIATE),
+  KLABEL(SEC_E_CERT_EXPIRED),
+  KLABEL(SEC_E_INCOMPLETE_MESSAGE),
+  KLABEL(SEC_E_INSUFFICIENT_MEMORY),
+  KLABEL(SEC_E_INTERNAL_ERROR),
+  KLABEL(SEC_E_INVALID_HANDLE),
+  KLABEL(SEC_E_INVALID_TOKEN),
+  KLABEL(SEC_E_LOGON_DENIED),
+  KLABEL(SEC_E_NO_AUTHENTICATING_AUTHORITY),
+  KLABEL(SEC_E_NO_CREDENTIALS),
+  KLABEL(SEC_E_NOT_OWNER),
+  KLABEL(SEC_E_OK),
+  KLABEL(SEC_E_SECPKG_NOT_FOUND),
+  KLABEL(SEC_E_TARGET_UNKNOWN),
+  KLABEL(SEC_E_UNKNOWN_CREDENTIALS),
+  KLABEL(SEC_E_UNSUPPORTED_FUNCTION),
+  KLABEL(SEC_E_UNTRUSTED_ROOT),
+  KLABEL(SEC_E_WRONG_PRINCIPAL),
+  LASTLABEL
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
diff --git a/base/win32socketinit.cc b/base/win32socketinit.cc
new file mode 100644
index 0000000..02a6c26
--- /dev/null
+++ b/base/win32socketinit.cc
@@ -0,0 +1,46 @@
+/*
+ *  Copyright 2009 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/win32socketinit.h"
+
+#include "webrtc/base/win32.h"
+
+namespace rtc {
+
+// Please don't remove this function.
+void EnsureWinsockInit() {
+  // The default implementation uses a global initializer, so WSAStartup
+  // happens at module load time.  Thus we don't need to do anything here.
+  // The hook is provided so that a client that statically links with
+  // libjingle can override it, to provide its own initialization.
+}
+
+#if defined(WEBRTC_WIN)
+class WinsockInitializer {
+ public:
+  WinsockInitializer() {
+    WSADATA wsaData;
+    WORD wVersionRequested = MAKEWORD(1, 0);
+    err_ = WSAStartup(wVersionRequested, &wsaData);
+  }
+  ~WinsockInitializer() {
+    if (!err_)
+      WSACleanup();
+  }
+  int error() {
+    return err_;
+  }
+ private:
+  int err_;
+};
+WinsockInitializer g_winsockinit;
+#endif
+
+}  // namespace rtc
diff --git a/base/win32socketinit.h b/base/win32socketinit.h
index d7017e1..46d27cb 100644
--- a/base/win32socketinit.h
+++ b/base/win32socketinit.h
@@ -11,9 +11,10 @@
 #ifndef WEBRTC_BASE_WIN32SOCKETINIT_H_
 #define WEBRTC_BASE_WIN32SOCKETINIT_H_
 
+namespace rtc {
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/win32socketinit.h"
+void EnsureWinsockInit();
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_WIN32SOCKETINIT_H_
diff --git a/base/win32socketserver.cc b/base/win32socketserver.cc
new file mode 100644
index 0000000..daec986
--- /dev/null
+++ b/base/win32socketserver.cc
@@ -0,0 +1,834 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/win32socketserver.h"
+
+#include <algorithm>
+#include <ws2tcpip.h>  // NOLINT
+
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/win32window.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Socket
+///////////////////////////////////////////////////////////////////////////////
+
+// TODO: Move this to a common place where PhysicalSocketServer can
+// share it.
+// Standard MTUs
+static const uint16_t PACKET_MAXIMUMS[] = {
+    65535,  // Theoretical maximum, Hyperchannel
+    32000,  // Nothing
+    17914,  // 16Mb IBM Token Ring
+    8166,   // IEEE 802.4
+    // 4464   // IEEE 802.5 (4Mb max)
+    4352,   // FDDI
+    // 2048,  // Wideband Network
+    2002,   // IEEE 802.5 (4Mb recommended)
+    // 1536,  // Expermental Ethernet Networks
+    // 1500,  // Ethernet, Point-to-Point (default)
+    1492,   // IEEE 802.3
+    1006,   // SLIP, ARPANET
+    // 576,   // X.25 Networks
+    // 544,   // DEC IP Portal
+    // 512,   // NETBIOS
+    508,    // IEEE 802/Source-Rt Bridge, ARCNET
+    296,    // Point-to-Point (low delay)
+    68,     // Official minimum
+    0,      // End of list marker
+};
+
+static const int IP_HEADER_SIZE = 20u;
+static const int ICMP_HEADER_SIZE = 8u;
+static const int ICMP_PING_TIMEOUT_MILLIS = 10000u;
+
+// TODO: Enable for production builds also? Use FormatMessage?
+#if !defined(NDEBUG)
+LPCSTR WSAErrorToString(int error, LPCSTR *description_result) {
+  LPCSTR string = "Unspecified";
+  LPCSTR description = "Unspecified description";
+  switch (error) {
+    case ERROR_SUCCESS:
+      string = "SUCCESS";
+      description = "Operation succeeded";
+      break;
+    case WSAEWOULDBLOCK:
+      string = "WSAEWOULDBLOCK";
+      description = "Using a non-blocking socket, will notify later";
+      break;
+    case WSAEACCES:
+      string = "WSAEACCES";
+      description = "Access denied, or sharing violation";
+      break;
+    case WSAEADDRNOTAVAIL:
+      string = "WSAEADDRNOTAVAIL";
+      description = "Address is not valid in this context";
+      break;
+    case WSAENETDOWN:
+      string = "WSAENETDOWN";
+      description = "Network is down";
+      break;
+    case WSAENETUNREACH:
+      string = "WSAENETUNREACH";
+      description = "Network is up, but unreachable";
+      break;
+    case WSAENETRESET:
+      string = "WSANETRESET";
+      description = "Connection has been reset due to keep-alive activity";
+      break;
+    case WSAECONNABORTED:
+      string = "WSAECONNABORTED";
+      description = "Aborted by host";
+      break;
+    case WSAECONNRESET:
+      string = "WSAECONNRESET";
+      description = "Connection reset by host";
+      break;
+    case WSAETIMEDOUT:
+      string = "WSAETIMEDOUT";
+      description = "Timed out, host failed to respond";
+      break;
+    case WSAECONNREFUSED:
+      string = "WSAECONNREFUSED";
+      description = "Host actively refused connection";
+      break;
+    case WSAEHOSTDOWN:
+      string = "WSAEHOSTDOWN";
+      description = "Host is down";
+      break;
+    case WSAEHOSTUNREACH:
+      string = "WSAEHOSTUNREACH";
+      description = "Host is unreachable";
+      break;
+    case WSAHOST_NOT_FOUND:
+      string = "WSAHOST_NOT_FOUND";
+      description = "No such host is known";
+      break;
+  }
+  if (description_result) {
+    *description_result = description;
+  }
+  return string;
+}
+
+void ReportWSAError(LPCSTR context, int error, const SocketAddress& address) {
+  LPCSTR description_string;
+  LPCSTR error_string = WSAErrorToString(error, &description_string);
+  LOG(LS_INFO) << context << " = " << error
+    << " (" << error_string << ":" << description_string << ") ["
+    << address.ToString() << "]";
+}
+#else
+void ReportWSAError(LPCSTR context, int error, const SocketAddress& address) {}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Win32Socket::EventSink
+/////////////////////////////////////////////////////////////////////////////
+
+#define WM_SOCKETNOTIFY  (WM_USER + 50)
+#define WM_DNSNOTIFY     (WM_USER + 51)
+
+struct Win32Socket::DnsLookup {
+  HANDLE handle;
+  uint16_t port;
+  char buffer[MAXGETHOSTSTRUCT];
+};
+
+class Win32Socket::EventSink : public Win32Window {
+ public:
+  explicit EventSink(Win32Socket * parent) : parent_(parent) { }
+
+  void Dispose();
+
+  virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+                         LRESULT& result);
+  virtual void OnNcDestroy();
+
+ private:
+  bool OnSocketNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& result);
+  bool OnDnsNotify(WPARAM wParam, LPARAM lParam, LRESULT& result);
+
+  Win32Socket * parent_;
+};
+
+void Win32Socket::EventSink::Dispose() {
+  parent_ = nullptr;
+  if (::IsWindow(handle())) {
+    ::DestroyWindow(handle());
+  } else {
+    delete this;
+  }
+}
+
+bool Win32Socket::EventSink::OnMessage(UINT uMsg, WPARAM wParam,
+                                       LPARAM lParam, LRESULT& result) {
+  switch (uMsg) {
+  case WM_SOCKETNOTIFY:
+  case WM_TIMER:
+    return OnSocketNotify(uMsg, wParam, lParam, result);
+  case WM_DNSNOTIFY:
+    return OnDnsNotify(wParam, lParam, result);
+  }
+  return false;
+}
+
+bool Win32Socket::EventSink::OnSocketNotify(UINT uMsg, WPARAM wParam,
+                                            LPARAM lParam, LRESULT& result) {
+  result = 0;
+
+  int wsa_event = WSAGETSELECTEVENT(lParam);
+  int wsa_error = WSAGETSELECTERROR(lParam);
+
+  // Treat connect timeouts as close notifications
+  if (uMsg == WM_TIMER) {
+    wsa_event = FD_CLOSE;
+    wsa_error = WSAETIMEDOUT;
+  }
+
+  if (parent_)
+    parent_->OnSocketNotify(static_cast<SOCKET>(wParam), wsa_event, wsa_error);
+  return true;
+}
+
+bool Win32Socket::EventSink::OnDnsNotify(WPARAM wParam, LPARAM lParam,
+                                         LRESULT& result) {
+  result = 0;
+
+  int error = WSAGETASYNCERROR(lParam);
+  if (parent_)
+    parent_->OnDnsNotify(reinterpret_cast<HANDLE>(wParam), error);
+  return true;
+}
+
+void Win32Socket::EventSink::OnNcDestroy() {
+  if (parent_) {
+    LOG(LS_ERROR) << "EventSink hwnd is being destroyed, but the event sink"
+                     " hasn't yet been disposed.";
+  } else {
+    delete this;
+  }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Win32Socket
+/////////////////////////////////////////////////////////////////////////////
+
+Win32Socket::Win32Socket()
+    : socket_(INVALID_SOCKET),
+      error_(0),
+      state_(CS_CLOSED),
+      connect_time_(0),
+      closing_(false),
+      close_error_(0),
+      sink_(nullptr),
+      dns_(nullptr) {}
+
+Win32Socket::~Win32Socket() {
+  Close();
+}
+
+bool Win32Socket::CreateT(int family, int type) {
+  Close();
+  int proto = (SOCK_DGRAM == type) ? IPPROTO_UDP : IPPROTO_TCP;
+  socket_ = ::WSASocket(family, type, proto, nullptr, 0, 0);
+  if (socket_ == INVALID_SOCKET) {
+    UpdateLastError();
+    return false;
+  }
+  if ((SOCK_DGRAM == type) && !SetAsync(FD_READ | FD_WRITE)) {
+    return false;
+  }
+  return true;
+}
+
+int Win32Socket::Attach(SOCKET s) {
+  RTC_DCHECK(socket_ == INVALID_SOCKET);
+  if (socket_ != INVALID_SOCKET)
+    return SOCKET_ERROR;
+
+  RTC_DCHECK(s != INVALID_SOCKET);
+  if (s == INVALID_SOCKET)
+    return SOCKET_ERROR;
+
+  socket_ = s;
+  state_ = CS_CONNECTED;
+
+  if (!SetAsync(FD_READ | FD_WRITE | FD_CLOSE))
+    return SOCKET_ERROR;
+
+  return 0;
+}
+
+void Win32Socket::SetTimeout(int ms) {
+  if (sink_)
+    ::SetTimer(sink_->handle(), 1, ms, 0);
+}
+
+SocketAddress Win32Socket::GetLocalAddress() const {
+  sockaddr_storage addr = {0};
+  socklen_t addrlen = sizeof(addr);
+  int result = ::getsockname(socket_, reinterpret_cast<sockaddr*>(&addr),
+                             &addrlen);
+  SocketAddress address;
+  if (result >= 0) {
+    SocketAddressFromSockAddrStorage(addr, &address);
+  } else {
+    LOG(LS_WARNING) << "GetLocalAddress: unable to get local addr, socket="
+                    << socket_;
+  }
+  return address;
+}
+
+SocketAddress Win32Socket::GetRemoteAddress() const {
+  sockaddr_storage addr = {0};
+  socklen_t addrlen = sizeof(addr);
+  int result = ::getpeername(socket_, reinterpret_cast<sockaddr*>(&addr),
+                             &addrlen);
+  SocketAddress address;
+  if (result >= 0) {
+    SocketAddressFromSockAddrStorage(addr, &address);
+  } else {
+    LOG(LS_WARNING) << "GetRemoteAddress: unable to get remote addr, socket="
+                    << socket_;
+  }
+  return address;
+}
+
+int Win32Socket::Bind(const SocketAddress& addr) {
+  RTC_DCHECK(socket_ != INVALID_SOCKET);
+  if (socket_ == INVALID_SOCKET)
+    return SOCKET_ERROR;
+
+  sockaddr_storage saddr;
+  size_t len = addr.ToSockAddrStorage(&saddr);
+  int err = ::bind(socket_,
+                   reinterpret_cast<sockaddr*>(&saddr),
+                   static_cast<int>(len));
+  UpdateLastError();
+  return err;
+}
+
+int Win32Socket::Connect(const SocketAddress& addr) {
+  if (state_ != CS_CLOSED) {
+    SetError(EALREADY);
+    return SOCKET_ERROR;
+  }
+
+  if (!addr.IsUnresolvedIP()) {
+    return DoConnect(addr);
+  }
+
+  LOG_F(LS_INFO) << "async dns lookup (" << addr.hostname() << ")";
+  DnsLookup * dns = new DnsLookup;
+  if (!sink_) {
+    // Explicitly create the sink ourselves here; we can't rely on SetAsync
+    // because we don't have a socket_ yet.
+    CreateSink();
+  }
+  // TODO: Replace with IPv6 compatible lookup.
+  dns->handle = WSAAsyncGetHostByName(sink_->handle(), WM_DNSNOTIFY,
+                                      addr.hostname().c_str(), dns->buffer,
+                                      sizeof(dns->buffer));
+
+  if (!dns->handle) {
+    LOG_F(LS_ERROR) << "WSAAsyncGetHostByName error: " << WSAGetLastError();
+    delete dns;
+    UpdateLastError();
+    Close();
+    return SOCKET_ERROR;
+  }
+
+  dns->port = addr.port();
+  dns_ = dns;
+  state_ = CS_CONNECTING;
+  return 0;
+}
+
+int Win32Socket::DoConnect(const SocketAddress& addr) {
+  if ((socket_ == INVALID_SOCKET) && !CreateT(addr.family(), SOCK_STREAM)) {
+    return SOCKET_ERROR;
+  }
+  if (!SetAsync(FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE)) {
+    return SOCKET_ERROR;
+  }
+
+  sockaddr_storage saddr = {0};
+  size_t len = addr.ToSockAddrStorage(&saddr);
+  connect_time_ = Time();
+  int result = connect(socket_,
+                       reinterpret_cast<SOCKADDR*>(&saddr),
+                       static_cast<int>(len));
+  if (result != SOCKET_ERROR) {
+    state_ = CS_CONNECTED;
+  } else {
+    int code = WSAGetLastError();
+    if (code == WSAEWOULDBLOCK) {
+      state_ = CS_CONNECTING;
+    } else {
+      ReportWSAError("WSAAsync:connect", code, addr);
+      error_ = code;
+      Close();
+      return SOCKET_ERROR;
+    }
+  }
+  addr_ = addr;
+
+  return 0;
+}
+
+int Win32Socket::GetError() const {
+  return error_;
+}
+
+void Win32Socket::SetError(int error) {
+  error_ = error;
+}
+
+Socket::ConnState Win32Socket::GetState() const {
+  return state_;
+}
+
+int Win32Socket::GetOption(Option opt, int* value) {
+  int slevel;
+  int sopt;
+  if (TranslateOption(opt, &slevel, &sopt) == -1)
+    return -1;
+
+  char* p = reinterpret_cast<char*>(value);
+  int optlen = sizeof(value);
+  return ::getsockopt(socket_, slevel, sopt, p, &optlen);
+}
+
+int Win32Socket::SetOption(Option opt, int value) {
+  int slevel;
+  int sopt;
+  if (TranslateOption(opt, &slevel, &sopt) == -1)
+    return -1;
+
+  const char* p = reinterpret_cast<const char*>(&value);
+  return ::setsockopt(socket_, slevel, sopt, p, sizeof(value));
+}
+
+int Win32Socket::Send(const void* buffer, size_t length) {
+  int sent = ::send(socket_,
+                    reinterpret_cast<const char*>(buffer),
+                    static_cast<int>(length),
+                    0);
+  UpdateLastError();
+  return sent;
+}
+
+int Win32Socket::SendTo(const void* buffer, size_t length,
+                        const SocketAddress& addr) {
+  sockaddr_storage saddr;
+  size_t addr_len = addr.ToSockAddrStorage(&saddr);
+  int sent = ::sendto(socket_, reinterpret_cast<const char*>(buffer),
+                      static_cast<int>(length), 0,
+                      reinterpret_cast<sockaddr*>(&saddr),
+                      static_cast<int>(addr_len));
+  UpdateLastError();
+  return sent;
+}
+
+int Win32Socket::Recv(void* buffer, size_t length, int64_t* timestamp) {
+  if (timestamp) {
+    *timestamp = -1;
+  }
+  int received = ::recv(socket_, static_cast<char*>(buffer),
+                        static_cast<int>(length), 0);
+  UpdateLastError();
+  if (closing_ && received <= static_cast<int>(length))
+    PostClosed();
+  return received;
+}
+
+int Win32Socket::RecvFrom(void* buffer,
+                          size_t length,
+                          SocketAddress* out_addr,
+                          int64_t* timestamp) {
+  if (timestamp) {
+    *timestamp = -1;
+  }
+  sockaddr_storage saddr;
+  socklen_t addr_len = sizeof(saddr);
+  int received = ::recvfrom(socket_, static_cast<char*>(buffer),
+                            static_cast<int>(length), 0,
+                            reinterpret_cast<sockaddr*>(&saddr), &addr_len);
+  UpdateLastError();
+  if (received != SOCKET_ERROR)
+    SocketAddressFromSockAddrStorage(saddr, out_addr);
+  if (closing_ && received <= static_cast<int>(length))
+    PostClosed();
+  return received;
+}
+
+int Win32Socket::Listen(int backlog) {
+  int err = ::listen(socket_, backlog);
+  if (!SetAsync(FD_ACCEPT))
+    return SOCKET_ERROR;
+
+  UpdateLastError();
+  if (err == 0)
+    state_ = CS_CONNECTING;
+  return err;
+}
+
+Win32Socket* Win32Socket::Accept(SocketAddress* out_addr) {
+  sockaddr_storage saddr;
+  socklen_t addr_len = sizeof(saddr);
+  SOCKET s = ::accept(socket_, reinterpret_cast<sockaddr*>(&saddr), &addr_len);
+  UpdateLastError();
+  if (s == INVALID_SOCKET)
+    return nullptr;
+  if (out_addr)
+    SocketAddressFromSockAddrStorage(saddr, out_addr);
+  Win32Socket* socket = new Win32Socket;
+  if (0 == socket->Attach(s))
+    return socket;
+  delete socket;
+  return nullptr;
+}
+
+int Win32Socket::Close() {
+  int err = 0;
+  if (socket_ != INVALID_SOCKET) {
+    err = ::closesocket(socket_);
+    socket_ = INVALID_SOCKET;
+    closing_ = false;
+    close_error_ = 0;
+    UpdateLastError();
+  }
+  if (dns_) {
+    WSACancelAsyncRequest(dns_->handle);
+    delete dns_;
+    dns_ = nullptr;
+  }
+  if (sink_) {
+    sink_->Dispose();
+    sink_ = nullptr;
+  }
+  addr_.Clear();
+  state_ = CS_CLOSED;
+  return err;
+}
+
+void Win32Socket::CreateSink() {
+  RTC_DCHECK(nullptr == sink_);
+
+  // Create window
+  sink_ = new EventSink(this);
+  sink_->Create(nullptr, L"EventSink", 0, 0, 0, 0, 10, 10);
+}
+
+bool Win32Socket::SetAsync(int events) {
+  if (nullptr == sink_) {
+    CreateSink();
+    RTC_DCHECK(nullptr != sink_);
+  }
+
+  // start the async select
+  if (WSAAsyncSelect(socket_, sink_->handle(), WM_SOCKETNOTIFY, events)
+      == SOCKET_ERROR) {
+    UpdateLastError();
+    Close();
+    return false;
+  }
+
+  return true;
+}
+
+bool Win32Socket::HandleClosed(int close_error) {
+  // WM_CLOSE will be received before all data has been read, so we need to
+  // hold on to it until the read buffer has been drained.
+  char ch;
+  closing_ = true;
+  close_error_ = close_error;
+  return (::recv(socket_, &ch, 1, MSG_PEEK) <= 0);
+}
+
+void Win32Socket::PostClosed() {
+  // If we see that the buffer is indeed drained, then send the close.
+  closing_ = false;
+  ::PostMessage(sink_->handle(), WM_SOCKETNOTIFY,
+                socket_, WSAMAKESELECTREPLY(FD_CLOSE, close_error_));
+}
+
+void Win32Socket::UpdateLastError() {
+  error_ = WSAGetLastError();
+}
+
+int Win32Socket::TranslateOption(Option opt, int* slevel, int* sopt) {
+  switch (opt) {
+    case OPT_DONTFRAGMENT:
+      *slevel = IPPROTO_IP;
+      *sopt = IP_DONTFRAGMENT;
+      break;
+    case OPT_RCVBUF:
+      *slevel = SOL_SOCKET;
+      *sopt = SO_RCVBUF;
+      break;
+    case OPT_SNDBUF:
+      *slevel = SOL_SOCKET;
+      *sopt = SO_SNDBUF;
+      break;
+    case OPT_NODELAY:
+      *slevel = IPPROTO_TCP;
+      *sopt = TCP_NODELAY;
+      break;
+    case OPT_DSCP:
+      LOG(LS_WARNING) << "Socket::OPT_DSCP not supported.";
+      return -1;
+    default:
+      RTC_NOTREACHED();
+      return -1;
+  }
+  return 0;
+}
+
+void Win32Socket::OnSocketNotify(SOCKET socket, int event, int error) {
+  // Ignore events if we're already closed.
+  if (socket != socket_)
+    return;
+
+  error_ = error;
+  switch (event) {
+    case FD_CONNECT:
+      if (error != ERROR_SUCCESS) {
+        ReportWSAError("WSAAsync:connect notify", error, addr_);
+#if !defined(NDEBUG)
+        int64_t duration = TimeSince(connect_time_);
+        LOG(LS_INFO) << "WSAAsync:connect error (" << duration
+                     << " ms), faking close";
+#endif
+        state_ = CS_CLOSED;
+        // If you get an error connecting, close doesn't really do anything
+        // and it certainly doesn't send back any close notification, but
+        // we really only maintain a few states, so it is easiest to get
+        // back into a known state by pretending that a close happened, even
+        // though the connect event never did occur.
+        SignalCloseEvent(this, error);
+      } else {
+#if !defined(NDEBUG)
+        int64_t duration = TimeSince(connect_time_);
+        LOG(LS_INFO) << "WSAAsync:connect (" << duration << " ms)";
+#endif
+        state_ = CS_CONNECTED;
+        SignalConnectEvent(this);
+      }
+      break;
+
+    case FD_ACCEPT:
+    case FD_READ:
+      if (error != ERROR_SUCCESS) {
+        ReportWSAError("WSAAsync:read notify", error, addr_);
+      } else {
+        SignalReadEvent(this);
+      }
+      break;
+
+    case FD_WRITE:
+      if (error != ERROR_SUCCESS) {
+        ReportWSAError("WSAAsync:write notify", error, addr_);
+      } else {
+        SignalWriteEvent(this);
+      }
+      break;
+
+    case FD_CLOSE:
+      if (HandleClosed(error)) {
+        ReportWSAError("WSAAsync:close notify", error, addr_);
+        state_ = CS_CLOSED;
+        SignalCloseEvent(this, error);
+      }
+      break;
+  }
+}
+
+void Win32Socket::OnDnsNotify(HANDLE task, int error) {
+  if (!dns_ || dns_->handle != task)
+    return;
+
+  uint32_t ip = 0;
+  if (error == 0) {
+    hostent* pHost = reinterpret_cast<hostent*>(dns_->buffer);
+    uint32_t net_ip = *reinterpret_cast<uint32_t*>(pHost->h_addr_list[0]);
+    ip = NetworkToHost32(net_ip);
+  }
+
+  LOG_F(LS_INFO) << "(" << IPAddress(ip).ToSensitiveString()
+                 << ", " << error << ")";
+
+  if (error == 0) {
+    SocketAddress address(ip, dns_->port);
+    error = DoConnect(address);
+  } else {
+    Close();
+  }
+
+  if (error) {
+    error_ = error;
+    SignalCloseEvent(this, error_);
+  } else {
+    delete dns_;
+    dns_ = nullptr;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32SocketServer
+// Provides cricket base services on top of a win32 gui thread
+///////////////////////////////////////////////////////////////////////////////
+
+static UINT s_wm_wakeup_id = 0;
+const TCHAR Win32SocketServer::kWindowName[] = L"libjingle Message Window";
+
+Win32SocketServer::Win32SocketServer()
+    : wnd_(this),
+      posted_(false),
+      hdlg_(nullptr) {
+  if (s_wm_wakeup_id == 0)
+    s_wm_wakeup_id = RegisterWindowMessage(L"WM_WAKEUP");
+  if (!wnd_.Create(nullptr, kWindowName, 0, 0, 0, 0, 0, 0)) {
+    LOG_GLE(LS_ERROR) << "Failed to create message window.";
+  }
+}
+
+Win32SocketServer::~Win32SocketServer() {
+  if (wnd_.handle() != nullptr) {
+    KillTimer(wnd_.handle(), 1);
+    wnd_.Destroy();
+  }
+}
+
+Socket* Win32SocketServer::CreateSocket(int type) {
+  return CreateSocket(AF_INET, type);
+}
+
+Socket* Win32SocketServer::CreateSocket(int family, int type) {
+  return CreateAsyncSocket(family, type);
+}
+
+AsyncSocket* Win32SocketServer::CreateAsyncSocket(int type) {
+  return CreateAsyncSocket(AF_INET, type);
+}
+
+AsyncSocket* Win32SocketServer::CreateAsyncSocket(int family, int type) {
+  Win32Socket* socket = new Win32Socket;
+  if (socket->CreateT(family, type)) {
+    return socket;
+  }
+  delete socket;
+  return nullptr;
+}
+
+void Win32SocketServer::SetMessageQueue(MessageQueue* queue) {
+  message_queue_ = queue;
+}
+
+bool Win32SocketServer::Wait(int cms, bool process_io) {
+  BOOL b;
+  if (process_io) {
+    // Spin the Win32 message pump at least once, and as long as requested.
+    // This is the Thread::ProcessMessages case.
+    uint32_t start = Time();
+    do {
+      MSG msg;
+      SetTimer(wnd_.handle(), 0, cms, nullptr);
+      // Get the next available message. If we have a modeless dialog, give
+      // give the message to IsDialogMessage, which will return true if it
+      // was a message for the dialog that it handled internally.
+      // Otherwise, dispatch as usual via Translate/DispatchMessage.
+      b = GetMessage(&msg, nullptr, 0, 0);
+      if (b == -1) {
+        LOG_GLE(LS_ERROR) << "GetMessage failed.";
+        return false;
+      } else if(b) {
+        if (!hdlg_ || !IsDialogMessage(hdlg_, &msg)) {
+          TranslateMessage(&msg);
+          DispatchMessage(&msg);
+        }
+      }
+      KillTimer(wnd_.handle(), 0);
+    } while (b && TimeSince(start) < cms);
+  } else if (cms != 0) {
+    // Sit and wait forever for a WakeUp. This is the Thread::Send case.
+    RTC_DCHECK(cms == -1);
+    MSG msg;
+    b = GetMessage(&msg, nullptr, s_wm_wakeup_id, s_wm_wakeup_id);
+    {
+      CritScope scope(&cs_);
+      posted_ = false;
+    }
+  } else {
+    // No-op (cms == 0 && !process_io). This is the Pump case.
+    b = TRUE;
+  }
+  return (b != FALSE);
+}
+
+void Win32SocketServer::WakeUp() {
+  if (wnd_.handle()) {
+    // Set the "message pending" flag, if not already set.
+    {
+      CritScope scope(&cs_);
+      if (posted_)
+        return;
+      posted_ = true;
+    }
+
+    PostMessage(wnd_.handle(), s_wm_wakeup_id, 0, 0);
+  }
+}
+
+void Win32SocketServer::Pump() {
+  // Clear the "message pending" flag.
+  {
+    CritScope scope(&cs_);
+    posted_ = false;
+  }
+
+  // Dispatch all the messages that are currently in our queue. If new messages
+  // are posted during the dispatch, they will be handled in the next Pump.
+  // We use max(1, ...) to make sure we try to dispatch at least once, since
+  // this allow us to process "sent" messages, not included in the size() count.
+  Message msg;
+  for (size_t max_messages_to_process =
+           std::max<size_t>(1, message_queue_->size());
+       max_messages_to_process > 0 && message_queue_->Get(&msg, 0, false);
+       --max_messages_to_process) {
+    message_queue_->Dispatch(&msg);
+  }
+
+  // Anything remaining?
+  int delay = message_queue_->GetDelay();
+  if (delay == -1) {
+    KillTimer(wnd_.handle(), 1);
+  } else {
+    SetTimer(wnd_.handle(), 1, delay, nullptr);
+  }
+}
+
+bool Win32SocketServer::MessageWindow::OnMessage(UINT wm, WPARAM wp,
+                                                 LPARAM lp, LRESULT& lr) {
+  bool handled = false;
+  if (wm == s_wm_wakeup_id || (wm == WM_TIMER && wp == 1)) {
+    ss_->Pump();
+    lr = 0;
+    handled = true;
+  }
+  return handled;
+}
+
+}  // namespace rtc
diff --git a/base/win32socketserver.h b/base/win32socketserver.h
index c143692..146b4e2 100644
--- a/base/win32socketserver.h
+++ b/base/win32socketserver.h
@@ -11,9 +11,152 @@
 #ifndef WEBRTC_BASE_WIN32SOCKETSERVER_H_
 #define WEBRTC_BASE_WIN32SOCKETSERVER_H_
 
+#if defined(WEBRTC_WIN)
+#include "webrtc/base/asyncsocket.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/messagequeue.h"
+#include "webrtc/base/socketserver.h"
+#include "webrtc/base/socketfactory.h"
+#include "webrtc/base/socket.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/win32window.h"
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/win32socketserver.h"
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Socket
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32Socket : public AsyncSocket {
+ public:
+  Win32Socket();
+  virtual ~Win32Socket();
+
+  bool CreateT(int family, int type);
+
+  int Attach(SOCKET s);
+  void SetTimeout(int ms);
+
+  // AsyncSocket Interface
+  virtual SocketAddress GetLocalAddress() const;
+  virtual SocketAddress GetRemoteAddress() const;
+  virtual int Bind(const SocketAddress& addr);
+  virtual int Connect(const SocketAddress& addr);
+  virtual int Send(const void *buffer, size_t length);
+  virtual int SendTo(const void *buffer, size_t length, const SocketAddress& addr);
+  virtual int Recv(void* buffer, size_t length, int64_t* timestamp);
+  virtual int RecvFrom(void* buffer,
+                       size_t length,
+                       SocketAddress* out_addr,
+                       int64_t* timestamp);
+  virtual int Listen(int backlog);
+  virtual Win32Socket *Accept(SocketAddress *out_addr);
+  virtual int Close();
+  virtual int GetError() const;
+  virtual void SetError(int error);
+  virtual ConnState GetState() const;
+  virtual int GetOption(Option opt, int* value);
+  virtual int SetOption(Option opt, int value);
+
+ private:
+  void CreateSink();
+  bool SetAsync(int events);
+  int DoConnect(const SocketAddress& addr);
+  bool HandleClosed(int close_error);
+  void PostClosed();
+  void UpdateLastError();
+  static int TranslateOption(Option opt, int* slevel, int* sopt);
+
+  void OnSocketNotify(SOCKET socket, int event, int error);
+  void OnDnsNotify(HANDLE task, int error);
+
+  SOCKET socket_;
+  int error_;
+  ConnState state_;
+  SocketAddress addr_;         // address that we connected to (see DoConnect)
+  uint32_t connect_time_;
+  bool closing_;
+  int close_error_;
+
+  class EventSink;
+  friend class EventSink;
+  EventSink * sink_;
+
+  struct DnsLookup;
+  DnsLookup * dns_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32SocketServer
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32SocketServer : public SocketServer {
+ public:
+  Win32SocketServer();
+  virtual ~Win32SocketServer();
+
+  void set_modeless_dialog(HWND hdlg) {
+    hdlg_ = hdlg;
+  }
+
+  // SocketServer Interface
+  virtual Socket* CreateSocket(int type);
+  virtual Socket* CreateSocket(int family, int type);
+
+  virtual AsyncSocket* CreateAsyncSocket(int type);
+  virtual AsyncSocket* CreateAsyncSocket(int family, int type);
+
+  virtual void SetMessageQueue(MessageQueue* queue);
+  virtual bool Wait(int cms, bool process_io);
+  virtual void WakeUp();
+
+  void Pump();
+
+  HWND handle() { return wnd_.handle(); }
+
+ private:
+  class MessageWindow : public Win32Window {
+   public:
+    explicit MessageWindow(Win32SocketServer* ss) : ss_(ss) {}
+   private:
+    virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result);
+    Win32SocketServer* ss_;
+  };
+
+  static const TCHAR kWindowName[];
+  MessageQueue *message_queue_;
+  MessageWindow wnd_;
+  CriticalSection cs_;
+  bool posted_;
+  HWND hdlg_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Thread. Automatically pumps Windows messages.
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32Thread : public Thread {
+ public:
+  explicit Win32Thread(SocketServer* ss) : Thread(ss),  id_(0) {}
+  virtual ~Win32Thread() {
+    Stop();
+  }
+  virtual void Run() {
+    id_ = GetCurrentThreadId();
+    Thread::Run();
+    id_ = 0;
+  }
+  virtual void Quit() {
+    PostThreadMessage(id_, WM_QUIT, 0, 0);
+  }
+ private:
+  DWORD id_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
+
+#endif  // WEBRTC_WIN
 
 #endif  // WEBRTC_BASE_WIN32SOCKETSERVER_H_
diff --git a/base/win32socketserver_unittest.cc b/base/win32socketserver_unittest.cc
new file mode 100644
index 0000000..7211cd7
--- /dev/null
+++ b/base/win32socketserver_unittest.cc
@@ -0,0 +1,161 @@
+/*
+ *  Copyright 2009 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/socket_unittest.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/win32socketserver.h"
+
+namespace rtc {
+
+// Test that Win32SocketServer::Wait works as expected.
+TEST(Win32SocketServerTest, TestWait) {
+  Win32SocketServer server;
+  uint32_t start = Time();
+  server.Wait(1000, true);
+  EXPECT_GE(TimeSince(start), 1000);
+}
+
+// Test that Win32Socket::Pump does not touch general Windows messages.
+TEST(Win32SocketServerTest, TestPump) {
+  Win32SocketServer server;
+  rtc::AutoSocketServerThread thread(&server);
+  EXPECT_EQ(TRUE, PostMessage(nullptr, WM_USER, 999, 0));
+  server.Pump();
+  MSG msg;
+  EXPECT_EQ(TRUE, PeekMessage(&msg, nullptr, WM_USER, 0, PM_REMOVE));
+  EXPECT_EQ(static_cast<UINT>(WM_USER), msg.message);
+  EXPECT_EQ(999u, msg.wParam);
+}
+
+// Test that Win32Socket passes all the generic Socket tests.
+class Win32SocketTest : public SocketTest {
+ protected:
+  Win32SocketTest() : thread_(&server_) {}
+  Win32SocketServer server_;
+  rtc::AutoSocketServerThread thread_;
+};
+
+TEST_F(Win32SocketTest, TestConnectIPv4) {
+  SocketTest::TestConnectIPv4();
+}
+
+TEST_F(Win32SocketTest, TestConnectIPv6) {
+  SocketTest::TestConnectIPv6();
+}
+
+TEST_F(Win32SocketTest, TestConnectWithDnsLookupIPv4) {
+  SocketTest::TestConnectWithDnsLookupIPv4();
+}
+
+TEST_F(Win32SocketTest, TestConnectWithDnsLookupIPv6) {
+  SocketTest::TestConnectWithDnsLookupIPv6();
+}
+
+TEST_F(Win32SocketTest, TestConnectFailIPv4) {
+  SocketTest::TestConnectFailIPv4();
+}
+
+TEST_F(Win32SocketTest, TestConnectFailIPv6) {
+  SocketTest::TestConnectFailIPv6();
+}
+
+TEST_F(Win32SocketTest, TestConnectWithDnsLookupFailIPv4) {
+  SocketTest::TestConnectWithDnsLookupFailIPv4();
+}
+
+TEST_F(Win32SocketTest, TestConnectWithDnsLookupFailIPv6) {
+  SocketTest::TestConnectWithDnsLookupFailIPv6();
+}
+
+TEST_F(Win32SocketTest, TestConnectWithClosedSocketIPv4) {
+  SocketTest::TestConnectWithClosedSocketIPv4();
+}
+
+TEST_F(Win32SocketTest, TestConnectWithClosedSocketIPv6) {
+  SocketTest::TestConnectWithClosedSocketIPv6();
+}
+
+TEST_F(Win32SocketTest, TestConnectWhileNotClosedIPv4) {
+  SocketTest::TestConnectWhileNotClosedIPv4();
+}
+
+TEST_F(Win32SocketTest, TestConnectWhileNotClosedIPv6) {
+  SocketTest::TestConnectWhileNotClosedIPv6();
+}
+
+TEST_F(Win32SocketTest, TestServerCloseDuringConnectIPv4) {
+  SocketTest::TestServerCloseDuringConnectIPv4();
+}
+
+TEST_F(Win32SocketTest, TestServerCloseDuringConnectIPv6) {
+  SocketTest::TestServerCloseDuringConnectIPv6();
+}
+
+TEST_F(Win32SocketTest, TestClientCloseDuringConnectIPv4) {
+  SocketTest::TestClientCloseDuringConnectIPv4();
+}
+
+TEST_F(Win32SocketTest, TestClientCloseDuringConnectIPv6) {
+  SocketTest::TestClientCloseDuringConnectIPv6();
+}
+
+TEST_F(Win32SocketTest, TestServerCloseIPv4) {
+  SocketTest::TestServerCloseIPv4();
+}
+
+TEST_F(Win32SocketTest, TestServerCloseIPv6) {
+  SocketTest::TestServerCloseIPv6();
+}
+
+TEST_F(Win32SocketTest, TestCloseInClosedCallbackIPv4) {
+  SocketTest::TestCloseInClosedCallbackIPv4();
+}
+
+TEST_F(Win32SocketTest, TestCloseInClosedCallbackIPv6) {
+  SocketTest::TestCloseInClosedCallbackIPv6();
+}
+
+TEST_F(Win32SocketTest, TestSocketServerWaitIPv4) {
+  SocketTest::TestSocketServerWaitIPv4();
+}
+
+TEST_F(Win32SocketTest, TestSocketServerWaitIPv6) {
+  SocketTest::TestSocketServerWaitIPv6();
+}
+
+TEST_F(Win32SocketTest, TestTcpIPv4) {
+  SocketTest::TestTcpIPv4();
+}
+
+TEST_F(Win32SocketTest, TestTcpIPv6) {
+  SocketTest::TestTcpIPv6();
+}
+
+TEST_F(Win32SocketTest, TestUdpIPv4) {
+  SocketTest::TestUdpIPv4();
+}
+
+TEST_F(Win32SocketTest, TestUdpIPv6) {
+  SocketTest::TestUdpIPv6();
+}
+
+// Breaks win_x64_dbg bot.
+// https://bugs.chromium.org/p/webrtc/issues/detail?id=6178
+TEST_F(Win32SocketTest, DISABLED_TestGetSetOptionsIPv4) {
+  SocketTest::TestGetSetOptionsIPv4();
+}
+
+// Breaks win_x64_dbg bot.
+// https://bugs.chromium.org/p/webrtc/issues/detail?id=6178
+TEST_F(Win32SocketTest, DISABLED_TestGetSetOptionsIPv6) {
+  SocketTest::TestGetSetOptionsIPv6();
+}
+
+}  // namespace rtc
diff --git a/base/win32window.cc b/base/win32window.cc
new file mode 100644
index 0000000..0e02eef
--- /dev/null
+++ b/base/win32window.cc
@@ -0,0 +1,121 @@
+/*
+ *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/win32window.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Window
+///////////////////////////////////////////////////////////////////////////////
+
+static const wchar_t kWindowBaseClassName[] = L"WindowBaseClass";
+HINSTANCE Win32Window::instance_ = nullptr;
+ATOM Win32Window::window_class_ = 0;
+
+Win32Window::Win32Window() : wnd_(nullptr) {}
+
+Win32Window::~Win32Window() {
+  RTC_DCHECK(nullptr == wnd_);
+}
+
+bool Win32Window::Create(HWND parent, const wchar_t* title, DWORD style,
+                         DWORD exstyle, int x, int y, int cx, int cy) {
+  if (wnd_) {
+    // Window already exists.
+    return false;
+  }
+
+  if (!window_class_) {
+    if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+                           GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+                           reinterpret_cast<LPCWSTR>(&Win32Window::WndProc),
+                           &instance_)) {
+      LOG_GLE(LS_ERROR) << "GetModuleHandleEx failed";
+      return false;
+    }
+
+    // Class not registered, register it.
+    WNDCLASSEX wcex;
+    memset(&wcex, 0, sizeof(wcex));
+    wcex.cbSize = sizeof(wcex);
+    wcex.hInstance = instance_;
+    wcex.lpfnWndProc = &Win32Window::WndProc;
+    wcex.lpszClassName = kWindowBaseClassName;
+    window_class_ = ::RegisterClassEx(&wcex);
+    if (!window_class_) {
+      LOG_GLE(LS_ERROR) << "RegisterClassEx failed";
+      return false;
+    }
+  }
+  wnd_ = ::CreateWindowEx(exstyle, kWindowBaseClassName, title, style, x, y, cx,
+                          cy, parent, nullptr, instance_, this);
+  return (nullptr != wnd_);
+}
+
+void Win32Window::Destroy() {
+  const bool success = ::DestroyWindow(wnd_);
+  RTC_DCHECK(success);
+}
+
+void Win32Window::Shutdown() {
+  if (window_class_) {
+    ::UnregisterClass(MAKEINTATOM(window_class_), instance_);
+    window_class_ = 0;
+  }
+}
+
+bool Win32Window::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+                            LRESULT& result) {
+  switch (uMsg) {
+  case WM_CLOSE:
+    if (!OnClose()) {
+      result = 0;
+      return true;
+    }
+    break;
+  }
+  return false;
+}
+
+LRESULT Win32Window::WndProc(HWND hwnd, UINT uMsg,
+                             WPARAM wParam, LPARAM lParam) {
+  Win32Window* that = reinterpret_cast<Win32Window*>(
+      ::GetWindowLongPtr(hwnd, GWLP_USERDATA));
+  if (!that && (WM_CREATE == uMsg)) {
+    CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
+    that = static_cast<Win32Window*>(cs->lpCreateParams);
+    that->wnd_ = hwnd;
+    ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(that));
+  }
+  if (that) {
+    LRESULT result;
+    bool handled = that->OnMessage(uMsg, wParam, lParam, result);
+    if (WM_DESTROY == uMsg) {
+      for (HWND child = ::GetWindow(hwnd, GW_CHILD); child;
+           child = ::GetWindow(child, GW_HWNDNEXT)) {
+        LOG(LS_INFO) << "Child window: " << static_cast<void*>(child);
+      }
+    }
+    if (WM_NCDESTROY == uMsg) {
+      ::SetWindowLongPtr(hwnd, GWLP_USERDATA, NULL);
+      that->wnd_ = nullptr;
+      that->OnNcDestroy();
+    }
+    if (handled) {
+      return result;
+    }
+  }
+  return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+}  // namespace rtc
diff --git a/base/win32window.h b/base/win32window.h
index ffffdf9..c0ba6b2 100644
--- a/base/win32window.h
+++ b/base/win32window.h
@@ -11,9 +11,50 @@
 #ifndef WEBRTC_BASE_WIN32WINDOW_H_
 #define WEBRTC_BASE_WIN32WINDOW_H_
 
+#if defined(WEBRTC_WIN)
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/win32window.h"
+#include "webrtc/base/win32.h"
+
+namespace rtc {
+
+///////////////////////////////////////////////////////////////////////////////
+// Win32Window
+///////////////////////////////////////////////////////////////////////////////
+
+class Win32Window {
+ public:
+  Win32Window();
+  virtual ~Win32Window();
+
+  HWND handle() const { return wnd_; }
+
+  bool Create(HWND parent, const wchar_t* title, DWORD style, DWORD exstyle,
+              int x, int y, int cx, int cy);
+  void Destroy();
+
+  // Call this when your DLL unloads.
+  static void Shutdown();
+
+ protected:
+  virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
+                         LRESULT& result);
+
+  virtual bool OnClose() { return true; }
+  virtual void OnNcDestroy() { }
+
+ private:
+  static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
+                                  LPARAM lParam);
+
+  HWND wnd_;
+  static HINSTANCE instance_;
+  static ATOM window_class_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}  // namespace rtc
+
+#endif  // WEBRTC_WIN 
 
 #endif  // WEBRTC_BASE_WIN32WINDOW_H_
diff --git a/base/win32window_unittest.cc b/base/win32window_unittest.cc
new file mode 100644
index 0000000..f814752
--- /dev/null
+++ b/base/win32window_unittest.cc
@@ -0,0 +1,65 @@
+/*
+ *  Copyright 2009 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/win32window.h"
+#include "webrtc/base/logging.h"
+
+static LRESULT kDummyResult = 0x1234ABCD;
+
+class TestWindow : public rtc::Win32Window {
+ public:
+  TestWindow() : destroyed_(false) { memset(&msg_, 0, sizeof(msg_)); }
+  const MSG& msg() const { return msg_; }
+  bool destroyed() const { return destroyed_; }
+
+  virtual bool OnMessage(UINT uMsg, WPARAM wParam,
+                         LPARAM lParam, LRESULT& result) {
+    msg_.message = uMsg;
+    msg_.wParam = wParam;
+    msg_.lParam = lParam;
+    result = kDummyResult;
+    return true;
+  }
+  virtual void OnNcDestroy() {
+    destroyed_ = true;
+  }
+
+ private:
+  MSG msg_;
+  bool destroyed_;
+};
+
+TEST(Win32WindowTest, Basics) {
+  TestWindow wnd;
+  EXPECT_TRUE(wnd.handle() == nullptr);
+  EXPECT_FALSE(wnd.destroyed());
+  EXPECT_TRUE(wnd.Create(0, L"Test", 0, 0, 0, 0, 100, 100));
+  EXPECT_TRUE(wnd.handle() != nullptr);
+  EXPECT_EQ(kDummyResult, ::SendMessage(wnd.handle(), WM_USER, 1, 2));
+  EXPECT_EQ(static_cast<UINT>(WM_USER), wnd.msg().message);
+  EXPECT_EQ(1u, wnd.msg().wParam);
+  EXPECT_EQ(2l, wnd.msg().lParam);
+  wnd.Destroy();
+  EXPECT_TRUE(wnd.handle() == nullptr);
+  EXPECT_TRUE(wnd.destroyed());
+}
+
+TEST(Win32WindowTest, MultipleWindows) {
+  TestWindow wnd1, wnd2;
+  EXPECT_TRUE(wnd1.Create(0, L"Test", 0, 0, 0, 0, 100, 100));
+  EXPECT_TRUE(wnd2.Create(0, L"Test", 0, 0, 0, 0, 100, 100));
+  EXPECT_TRUE(wnd1.handle() != nullptr);
+  EXPECT_TRUE(wnd2.handle() != nullptr);
+  wnd1.Destroy();
+  wnd2.Destroy();
+  EXPECT_TRUE(wnd2.handle() == nullptr);
+  EXPECT_TRUE(wnd1.handle() == nullptr);
+}
diff --git a/base/window.h b/base/window.h
index d515f7c..a4a9aa4 100644
--- a/base/window.h
+++ b/base/window.h
@@ -11,9 +11,68 @@
 #ifndef WEBRTC_BASE_WINDOW_H_
 #define WEBRTC_BASE_WINDOW_H_
 
+#include <stdint.h>
 
-// This header is deprecated and is just left here temporarily during
-// refactoring. See https://bugs.webrtc.org/7634 for more details.
-#include "webrtc/rtc_base/window.h"
+#include "webrtc/base/stringencode.h"
+
+// Define platform specific window types.
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+typedef unsigned long Window;  // Avoid include <X11/Xlib.h>.
+#elif defined(WEBRTC_WIN)
+// We commonly include win32.h in webrtc/base so just include it here.
+#include "webrtc/base/win32.h"  // Include HWND, HMONITOR.
+#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+typedef unsigned int CGWindowID;
+typedef unsigned int CGDirectDisplayID;
+#endif
+
+namespace rtc {
+
+class WindowId {
+ public:
+  // Define WindowT for each platform.
+#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
+  typedef Window WindowT;
+#elif defined(WEBRTC_WIN)
+  typedef HWND WindowT;
+#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+  typedef CGWindowID WindowT;
+#else
+  typedef unsigned int WindowT;
+#endif
+
+  static WindowId Cast(uint64_t id) {
+#if defined(WEBRTC_WIN)
+    return WindowId(reinterpret_cast<WindowId::WindowT>(id));
+#else
+    return WindowId(static_cast<WindowId::WindowT>(id));
+#endif
+  }
+
+  static uint64_t Format(const WindowT& id) {
+#if defined(WEBRTC_WIN)
+    return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(id));
+#else
+    return static_cast<uint64_t>(id);
+#endif
+  }
+
+  WindowId() : id_(0) {}
+  WindowId(const WindowT& id) : id_(id) {}  // NOLINT
+  const WindowT& id() const { return id_; }
+  bool IsValid() const { return id_ != 0; }
+  bool Equals(const WindowId& other) const {
+    return id_ == other.id();
+  }
+
+ private:
+  WindowT id_;
+};
+
+inline std::string ToString(const WindowId& window) {
+  return ToString(window.id());
+}
+
+}  // namespace rtc
 
 #endif  // WEBRTC_BASE_WINDOW_H_