Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/tools/android/OWNERS b/tools/android/OWNERS
new file mode 100644
index 0000000..f8370f6
--- /dev/null
+++ b/tools/android/OWNERS
@@ -0,0 +1,4 @@
+digit@chromium.org
+michaelbai@chromium.org
+wangxianzhu@chromium.org
+yfriedman@chromium.org
diff --git a/tools/android/adb_reboot/adb_reboot.c b/tools/android/adb_reboot/adb_reboot.c
new file mode 100644
index 0000000..d414dd5
--- /dev/null
+++ b/tools/android/adb_reboot/adb_reboot.c
@@ -0,0 +1,43 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+int main(int argc, char ** argv) {
+  int i = fork();
+  struct stat ft;
+  time_t ct;
+
+  if (i < 0) {
+    printf("fork error");
+    return 1;
+  }
+  if (i > 0)
+    return 0;
+
+  /* child (daemon) continues */
+  int j;
+  for (j = 0; j < sysconf(_SC_OPEN_MAX); j++)
+    close(j);
+
+  setsid(); /* obtain a new process group */
+
+  while (1) {
+    sleep(120);
+
+    stat("/sdcard/host_heartbeat", &ft);
+    time(&ct);
+    if (ct - ft.st_mtime  > 120) {
+      /* File was not touched for some time. */
+      system("su -c reboot");
+    }
+  }
+
+  return 0;
+}
diff --git a/tools/android/adb_reboot/adb_reboot.gyp b/tools/android/adb_reboot/adb_reboot.gyp
new file mode 100644
index 0000000..85134b9
--- /dev/null
+++ b/tools/android/adb_reboot/adb_reboot.gyp
@@ -0,0 +1,14 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+  'targets': [
+    {
+      'target_name': 'adb_reboot',
+      'type': 'executable',
+      'sources': [
+        'adb_reboot.c',
+      ],
+    },
+  ],
+}
diff --git a/tools/android/adb_remote_setup.sh b/tools/android/adb_remote_setup.sh
new file mode 100755
index 0000000..87c6601
--- /dev/null
+++ b/tools/android/adb_remote_setup.sh
@@ -0,0 +1,95 @@
+#!/bin/bash
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# URL from which the latest version of this script can be downloaded.
+script_url="http://src.chromium.org/svn/trunk/src/tools/android/adb_remote_setup.sh"
+
+# Replaces this file with the latest version of the script and runs it.
+update-self() {
+  local script="${BASH_SOURCE[0]}"
+  local new_script="${script}.new"
+  local updater_script="${script}.updater"
+  curl -sSf -o "$new_script" "$script_url" || return
+  chmod +x "$new_script" || return
+
+  # Replace this file with the newly downloaded script.
+  cat > "$updater_script" << EOF
+#!/bin/bash
+if mv "$new_script" "$script"; then
+  rm -- "$updater_script"
+else
+  echo "Note: script update failed."
+fi
+ADB_REMOTE_SETUP_NO_UPDATE=1 exec /bin/bash "$script" $@
+EOF
+  exec /bin/bash "$updater_script" "$@"
+}
+
+if [[ "$ADB_REMOTE_SETUP_NO_UPDATE" -ne 1 ]]; then
+  update-self "$@" || echo 'Note: script update failed'
+fi
+
+if [[ $# -ne 1 && $# -ne 2 ]]; then
+  cat <<'EOF'
+Usage: adb_remote_setup.sh REMOTE_HOST [REMOTE_ADB]
+
+Configures adb on a remote machine to communicate with a device attached to the
+local machine. This is useful for installing APKs, running tests, etc while
+working remotely.
+
+Arguments:
+  REMOTE_HOST  hostname of remote machine
+  REMOTE_ADB   path to adb on the remote machine (you can omit this if adb is in
+               the remote host's path)
+EOF
+  exit 1
+fi
+
+remote_host="$1"
+remote_adb="${2:-adb}"
+
+# Ensure adb is in the local machine's path.
+if ! which adb >/dev/null; then
+  echo "error: adb must be in your local machine's path."
+  exit 1
+fi
+
+if which kinit >/dev/null; then
+  # Allow ssh to succeed without typing your password multiple times.
+  kinit -R || kinit
+fi
+
+# Ensure local and remote versions of adb are the same.
+remote_adb_version=$(ssh "$remote_host" "$remote_adb version")
+local_adb_version=$(adb version)
+if [[ "$local_adb_version" != "$remote_adb_version" ]]; then
+  echo >&2
+  echo "WARNING: local adb is not the same version as remote adb." >&2
+  echo "This should be fixed since it may result in protocol errors." >&2
+  echo "  local adb:  $local_adb_version" >&2
+  echo "  remote adb: $remote_adb_version" >&2
+  echo >&2
+  sleep 5
+fi
+
+# Kill the adb server on the remote host.
+ssh "$remote_host" "$remote_adb kill-server"
+
+# Start the adb server locally.
+adb start-server
+
+# Forward various ports from the remote host to the local host:
+#   5037: adb
+#   8001: http server
+#   9031: sync server
+#   10000: net unittests
+#   10201: net unittests
+ssh -C \
+    -R 5037:localhost:5037 \
+    -L 8001:localhost:8001 \
+    -L 9031:localhost:9031 \
+    -R 10000:localhost:10000 \
+    -R 10201:localhost:10201 \
+    "$remote_host"
diff --git a/tools/android/android_tools.gyp b/tools/android/android_tools.gyp
new file mode 100644
index 0000000..84de85c
--- /dev/null
+++ b/tools/android/android_tools.gyp
@@ -0,0 +1,52 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    # Intermediate target grouping the android tools needed to run native
+    # unittests and instrumentation test apks.
+    {
+      'target_name': 'android_tools',
+      'type': 'none',
+      'dependencies': [
+        'adb_reboot/adb_reboot.gyp:adb_reboot',
+        'file_poller/file_poller.gyp:file_poller',
+        'forwarder2/forwarder.gyp:forwarder2',
+        'md5sum/md5sum.gyp:md5sum',
+        'purge_ashmem/purge_ashmem.gyp:purge_ashmem',
+        'run_pie/run_pie.gyp:run_pie',
+        '../../tools/telemetry/telemetry.gyp:*#host',
+      ],
+    },
+    {
+      'target_name': 'heap_profiler',
+      'type': 'none',
+      'dependencies': [
+        'heap_profiler/heap_profiler.gyp:heap_dump',
+        'heap_profiler/heap_profiler.gyp:heap_profiler',
+      ],
+    },
+    {
+      'target_name': 'memdump',
+      'type': 'none',
+      'dependencies': [
+        'memdump/memdump.gyp:memdump',
+      ],
+    },
+    {
+      'target_name': 'memconsumer',
+      'type': 'none',
+      'dependencies': [
+        'memconsumer/memconsumer.gyp:memconsumer',
+      ],
+    },
+    {
+      'target_name': 'ps_ext',
+      'type': 'none',
+      'dependencies': [
+        'ps_ext/ps_ext.gyp:ps_ext',
+      ],
+    },
+  ],
+}
diff --git a/tools/android/asan/asan_device_setup.sh b/tools/android/asan/asan_device_setup.sh
new file mode 100755
index 0000000..5948f2b
--- /dev/null
+++ b/tools/android/asan/asan_device_setup.sh
@@ -0,0 +1,194 @@
+#!/bin/bash -e
+#===- lib/asan/scripts/asan_device_setup.py -----------------------------------===#
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+# Prepare Android device to run ASan applications.
+#
+#===------------------------------------------------------------------------===#
+
+
+HERE="$(cd "$(dirname "$0")" && pwd)"
+
+revert=no
+extra_options=
+device=
+lib=
+
+function usage {
+    echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra_options options]"
+    echo "  --revert: Uninstall ASan from the device."
+    echo "  --lib: Path to ASan runtime library."
+    echo "  --extra_options: Extra ASAN_OPTIONS."
+    echo "  --device: Install to the given device. Use 'adb devices' to find"
+    echo "            device-id."
+    echo
+    exit 1
+}
+
+while [[ $# > 0 ]]; do
+  case $1 in
+    --revert)
+      revert=yes
+      ;;
+    --extra-options)
+      shift
+      if [[ $# == 0 ]]; then
+        echo "--extra-options requires an argument."
+        exit 1
+      fi
+      extra_options="$1"
+      ;;
+    --lib)
+      shift
+      if [[ $# == 0 ]]; then
+        echo "--lib requires an argument."
+        exit 1
+      fi
+      lib="$1"
+      ;;
+    --device)
+      shift
+      if [[ $# == 0 ]]; then
+        echo "--device requires an argument."
+        exit 1
+      fi
+      device="$1"
+      ;;
+    *)
+      usage
+      ;;
+  esac
+  shift
+done
+
+ADB=${ADB:-adb}
+if [[ x$device != x ]]; then
+    ADB="$ADB -s $device"
+fi
+
+ASAN_RT="libclang_rt.asan-arm-android.so"
+
+if [[ x$revert == xyes ]]; then
+    echo '>> Uninstalling ASan'
+    $ADB root
+    $ADB wait-for-device
+    $ADB remount
+    $ADB shell mv /system/bin/app_process.real /system/bin/app_process
+    $ADB shell rm /system/bin/asanwrapper
+    $ADB shell rm /system/lib/$ASAN_RT
+
+    echo '>> Restarting shell'
+    $ADB shell stop
+    $ADB shell start
+
+    echo '>> Done'
+    exit 0
+fi
+
+if [[ -d "$lib" ]]; then
+    ASAN_RT_PATH="$lib"
+elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then
+    ASAN_RT_PATH=$(dirname "$lib")
+elif [[ -f "$HERE/$ASAN_RT" ]]; then
+    ASAN_RT_PATH="$HERE"
+elif [[ $(basename "$HERE") == "bin" ]]; then
+    # We could be in the toolchain's base directory.
+    # Consider ../lib and ../lib/clang/$VERSION/lib/linux.
+    P=$(ls "$HERE"/../lib/"$ASAN_RT" "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1)
+    if [[ -n "$P" ]]; then
+        ASAN_RT_PATH="$(dirname "$P")"
+    fi
+fi
+
+if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then
+    echo "ASan runtime library not found"
+    exit 1
+fi
+
+TMPDIRBASE=$(mktemp -d)
+TMPDIROLD="$TMPDIRBASE/old"
+TMPDIR="$TMPDIRBASE/new"
+mkdir "$TMPDIROLD"
+
+echo '>> Remounting /system rw'
+$ADB root
+$ADB wait-for-device
+$ADB remount
+
+echo '>> Copying files from the device'
+$ADB pull /system/bin/app_process "$TMPDIROLD"
+$ADB pull /system/bin/app_process.real "$TMPDIROLD" || true
+$ADB pull /system/bin/asanwrapper "$TMPDIROLD" || true
+$ADB pull /system/lib/libclang_rt.asan-arm-android.so "$TMPDIROLD" || true
+cp -r "$TMPDIROLD" "$TMPDIR"
+
+if ! [[ -f "$TMPDIR/app_process" ]]; then
+    echo "app_process missing???"
+    exit 1
+fi
+
+if [[ -f "$TMPDIR/app_process.real" ]]; then
+    echo "app_process.real exists, updating the wrapper"
+else
+    echo "app_process.real missing, new installation"
+    mv "$TMPDIR/app_process" "$TMPDIR/app_process.real"
+fi
+
+echo '>> Generating wrappers'
+
+cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/"
+
+# FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup,
+# which may or may not be a real bug (probably not).
+ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0
+if [[ x$extra_options != x ]] ; then
+    ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"
+fi
+
+# Zygote wrapper.
+cat <<EOF >"$TMPDIR/app_process"
+#!/system/bin/sh
+ASAN_OPTIONS=$ASAN_OPTIONS \\
+LD_PRELOAD=libclang_rt.asan-arm-android.so \\
+exec /system/bin/app_process.real \$@
+
+EOF
+
+# General command-line tool wrapper (use for anything that's not started as
+# zygote).
+cat <<EOF >"$TMPDIR/asanwrapper"
+#!/system/bin/sh
+LD_PRELOAD=libclang_rt.asan-arm-android.so \\
+exec \$@
+
+EOF
+
+if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
+    echo '>> Pushing files to the device'
+    $ADB push "$TMPDIR/$ASAN_RT" /system/lib/
+    $ADB push "$TMPDIR/app_process" /system/bin/app_process
+    $ADB push "$TMPDIR/app_process.real" /system/bin/app_process.real
+    $ADB push "$TMPDIR/asanwrapper" /system/bin/asanwrapper
+    $ADB shell chown root.shell \
+        /system/bin/app_process \
+        /system/bin/app_process.real \
+        /system/bin/asanwrapper
+    $ADB shell chmod 755 \
+        /system/bin/app_process \
+        /system/bin/app_process.real \
+        /system/bin/asanwrapper
+
+    echo '>> Restarting shell (asynchronous)'
+    $ADB shell stop
+    $ADB shell start
+
+    echo '>> Please wait until the device restarts'
+else
+    echo '>> Device is up to date'
+fi
+
+rm -r "$TMPDIRBASE"
diff --git a/tools/android/checkstyle/checkstyle.py b/tools/android/checkstyle/checkstyle.py
new file mode 100644
index 0000000..25f202f
--- /dev/null
+++ b/tools/android/checkstyle/checkstyle.py
@@ -0,0 +1,69 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Script that is used by PRESUBMIT.py to run style checks on Java files."""
+
+import os
+import subprocess
+
+
+CHROMIUM_SRC = os.path.normpath(
+    os.path.join(os.path.dirname(__file__),
+                 os.pardir, os.pardir, os.pardir))
+CHECKSTYLE_ROOT = os.path.join(CHROMIUM_SRC, 'third_party', 'checkstyle',
+                               'checkstyle-5.7-all.jar')
+
+
+def RunCheckstyle(input_api, output_api, style_file):
+  if not os.path.exists(style_file):
+    file_error = ('  Java checkstyle configuration file is missing: '
+                  + style_file)
+    return [output_api.PresubmitError(file_error)]
+
+  # Filter out non-Java files and files that were deleted.
+  java_files = [x.LocalPath() for x in input_api.AffectedFiles(False, False)
+                if os.path.splitext(x.LocalPath())[1] == '.java']
+  if not java_files:
+    return []
+
+  # Run checkstyle
+  checkstyle_env = os.environ.copy()
+  checkstyle_env['JAVA_CMD'] = 'java'
+  try:
+    check = subprocess.Popen(['java', '-cp',
+                              CHECKSTYLE_ROOT,
+                              'com.puppycrawl.tools.checkstyle.Main', '-c',
+                              style_file] + java_files,
+                             stdout=subprocess.PIPE, env=checkstyle_env)
+    stdout, _ = check.communicate()
+    if check.returncode == 0:
+      return []
+  except OSError as e:
+    import errno
+    if e.errno == errno.ENOENT:
+      install_error = ('  checkstyle is not installed. Please run '
+                       'build/install-build-deps-android.sh')
+      return [output_api.PresubmitPromptWarning(install_error)]
+
+  # Remove non-error values from stdout
+  errors = stdout.splitlines()
+
+  if errors and errors[0] == 'Starting audit...':
+    del errors[0]
+  if errors and errors[-1] == 'Audit done.':
+    del errors[-1]
+
+  # Filter out warnings
+  errors = [x for x in errors if 'warning: ' not in x]
+  if not errors:
+    return []
+
+  local_path = input_api.PresubmitLocalPath()
+  output = []
+  for error in errors:
+    # Change the full file path to relative path in the output lines
+    full_path, end = error.split(':', 1)
+    rel_path = os.path.relpath(full_path, local_path)
+    output.append('  %s:%s' % (rel_path, end))
+  return [output_api.PresubmitPromptWarning('\n'.join(output))]
diff --git a/tools/android/checkstyle/chromium-style-5.0.xml b/tools/android/checkstyle/chromium-style-5.0.xml
new file mode 100644
index 0000000..bc40f87
--- /dev/null
+++ b/tools/android/checkstyle/chromium-style-5.0.xml
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<!--
+  See installation instructions: https://sites.google.com/a/chromium.org/dev/checkstyle
+-->
+<module name="Checker">
+  <property name="severity" value="warning"/>
+  <property name="charset" value="UTF-8"/>
+  <module name="TreeWalker">
+    <module name="AvoidStarImport">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="IllegalCatch">
+      <property name="severity" value="warning"/>
+    </module>
+    <module name="RedundantImport">
+      <message key="import.redundant" value="Redundant import: {0}. Use :JavaImportOrganize (ECLIM) or Ctrl+Shift+O (Eclipse) to sort imports"/>
+      <property name="severity" value="error"/>
+    </module>
+    <module name="UnusedImports">
+      <property name="severity" value="error"/>
+      <property name="processJavadoc" value="true"/>
+      <message key="import.unused" value="Unused import: {0}. Use :JavaImportOrganize (ECLIM) or Ctrl+Shift+O (Eclipse) to sort imports"/>
+    </module>
+    <module name="JavadocType">
+      <property name="severity" value="error"/>
+      <property name="tokens" value="INTERFACE_DEF, CLASS_DEF"/>
+      <property name="scope" value="public"/>
+      <message key="javadoc.missing" value="Public classes and interfaces require JavaDoc comments."/>
+    </module>
+    <module name="JavadocMethod">
+      <property name="severity" value="warning"/>
+      <property name="scope" value="public"/>
+      <property name="allowMissingParamTags" value="true"/>
+      <property name="allowMissingPropertyJavadoc" value="true"/>
+      <property name="allowMissingReturnTag" value="true"/>
+      <property name="allowMissingThrowsTags" value="true"/>
+    </module>
+    <module name="PackageName">
+      <property name="severity" value="error"/>
+      <property name="format" value="^[a-z]+(\.[a-z][a-z0-9_]{1,})*$"/>
+    </module>
+    <module name="SimplifyBooleanExpression">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="SimplifyBooleanReturn">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="TypeName">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="ConstantName">
+      <property name="severity" value="error"/>
+      <property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*)|(s[A-Z][a-zA-Z0-9]*)$"/>
+      <message key="name.invalidPattern" value="Static final field names must either be all caps (e.g. int HEIGHT_PX) for 'true' constants, or start with s (e.g. AtomicInteger sNextId or Runnable sSuspendTask) for fields with mutable state or that don't 'feel' like constants."/>
+    </module>
+    <!-- Non-public, non-static field names start with m. -->
+    <module name="MemberName">
+        <property name="severity" value="error"/>
+        <property name="format" value="^m[A-Z][a-zA-Z0-9]*$"/>
+        <property name="applyToPublic" value="false"/>
+        <message key="name.invalidPattern" value="Non-public, non-static field names start with m."/>
+    </module>
+    <!-- Static field names start with s. -->
+    <module name="StaticVariableName">
+        <property name="severity" value="error"/>
+        <property name="format" value="^s[A-Z][a-zA-Z0-9]*$"/>
+        <property name="applyToPublic" value="false"/>
+        <message key="name.invalidPattern" value="Static field names start with s."/>
+    </module>
+    <module name="MethodName">
+        <property name="severity" value="error"/>
+        <property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
+        <message key="name.invalidPattern" value="Method names should start with a lower case letter (e.g. getWidth())"/>
+    </module>
+    <module name="ParameterName">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="LocalFinalVariableName">
+      <property name="severity" value="error"/>
+      <property name="format" value="^m|s|((([ms][a-z0-9])|([a-ln-rt-z]))[a-zA-Z0-9]*)$"/>
+      <message key="name.invalidPattern" value="Local variables should be camel-cased (e.g. int minWidth = 4)."/>
+    </module>
+    <module name="LocalVariableName">
+      <property name="severity" value="error"/>
+      <property name="format" value="^m|s|((([ms][a-z0-9])|([a-ln-rt-z]))[a-zA-Z0-9]*)$"/>
+      <message key="name.invalidPattern" value="Local variables should be camel-cased (e.g. int minWidth = 4)."/>
+    </module>
+    <module name="LineLength">
+      <property name="severity" value="error"/>
+      <property name="ignorePattern" value="^import.*$" />
+      <property name="max" value="100"/>
+    </module>
+    <module name="LeftCurly">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="RightCurly">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="NeedBraces">
+      <property name="severity" value="warning"/>
+      <property name="tokens" value="LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
+    </module>
+    <module name="EmptyBlock">
+      <property name="severity" value="error"/>
+      <property name="option" value="text"/>
+      <metadata name="altname" value="EmptyCatchBlock"/>
+    </module>
+    <module name="UpperEll">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="FallThrough">
+      <property name="severity" value="error"/>
+      <property name="reliefPattern" value=".*"/>
+    </module>
+    <module name="ModifierOrder">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="WhitespaceAround">
+      <property name="severity" value="error"/>
+      <property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, EQUAL, GE, GT, LAND, LE, LITERAL_ASSERT, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN, TYPE_EXTENSION_AND" />
+      <property name="allowEmptyConstructors" value="true"/>
+      <property name="allowEmptyMethods" value="true"/>
+   </module>
+    <module name="WhitespaceAfter">
+      <property name="severity" value="error"/>
+      <property name="tokens" value="COMMA, SEMI, TYPECAST"/>
+    </module>
+    <module name="NoWhitespaceAfter">
+      <property name="severity" value="error"/>
+      <property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS"/>
+    </module>
+    <module name="NoWhitespaceBefore">
+      <property name="severity" value="error"/>
+      <property name="allowLineBreaks" value="true"/>
+      <property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
+    </module>
+    <module name="EmptyStatement">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="NoFinalizer">
+      <property name="severity" value="warning"/>
+    </module>
+    <module name="ParenPad">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="ImportOrder">
+      <property name="severity" value="error"/>
+      <message key="import.ordering" value="Wrong order for {0} import. Use :JavaImportOrganize (ECLIM) or Ctrl+Shift+O (Eclipse) to sort imports"/>
+      <property name="groups" value="android, com, dalvik, gov, junit, libcore, net, org, java, javax"/>
+      <property name="ordered" value="true"/>
+      <property name="option" value="top"/>
+      <property name="separated" value="true"/>
+    </module>
+    <!-- TODO(aurimas): make indentation an error once https://github.com/checkstyle/checkstyle/issues/255 is fixed. -->
+    <module name="Indentation">
+      <property name="severity" value="warning"/>
+      <property name="basicOffset" value="4"/>
+      <property name="throwsIndent" value="8"/>
+    </module>
+  </module>
+  <module name="FileTabCharacter">
+    <property name="severity" value="error"/>
+  </module>
+  <module name="RegexpSingleline">
+    <property name="format" value="((//.*)|(\*.*))FIXME"/>
+    <property name="message" value="TODO is preferred to FIXME.  e.g. &quot;TODO(johndoe):"/>
+  </module>
+  <module name="RegexpSingleline">
+    <property name="format" value="((//.*)|(\*.*))(?&lt;!TODO\(.{0,100})(TODO[^(])|(TODO\([^)]*$)"/>
+    <property name="message" value="All TODOs should be named.  e.g. &quot;TODO(johndoe):"/>
+  </module>
+  <module name="RegexpSingleline">
+    <property name="severity" value="error"/>
+    <property name="format" value="[ \t]+$"/>
+    <property name="message" value="Trailing whitespace"/>
+    </module>
+  <module name="RegexpHeader">
+    <property name="severity" value="error"/>
+    <property name="header" value="^// Copyright 20\d\d The Chromium Authors. All rights reserved.$\n^// Use of this source code is governed by a BSD-style license that can be$\n^// found in the LICENSE file.$"/>
+  </module>
+</module>
diff --git a/tools/android/common/adb_connection.cc b/tools/android/common/adb_connection.cc
new file mode 100644
index 0000000..9985a3a
--- /dev/null
+++ b/tools/android/common/adb_connection.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/common/adb_connection.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "tools/android/common/net.h"
+
+namespace tools {
+namespace {
+
+void CloseSocket(int fd) {
+  if (fd >= 0) {
+    int old_errno = errno;
+    close(fd);
+    errno = old_errno;
+  }
+}
+
+}  // namespace
+
+int ConnectAdbHostSocket(const char* forward_to) {
+  // ADB port forward request format: HHHHtcp:port:address.
+  // HHHH is the hexidecimal length of the "tcp:port:address" part.
+  const size_t kBufferMaxLength = 30;
+  const size_t kLengthOfLength = 4;
+  const size_t kAddressMaxLength = kBufferMaxLength - kLengthOfLength;
+
+  const char kAddressPrefix[] = { 't', 'c', 'p', ':' };
+  size_t address_length = arraysize(kAddressPrefix) + strlen(forward_to);
+  if (address_length > kBufferMaxLength - kLengthOfLength) {
+    LOG(ERROR) << "Forward to address is too long: " << forward_to;
+    return -1;
+  }
+
+  char request[kBufferMaxLength];
+  memcpy(request + kLengthOfLength, kAddressPrefix, arraysize(kAddressPrefix));
+  memcpy(request + kLengthOfLength + arraysize(kAddressPrefix),
+         forward_to, strlen(forward_to));
+
+  char length_buffer[kLengthOfLength + 1];
+  snprintf(length_buffer, arraysize(length_buffer), "%04X",
+           static_cast<int>(address_length));
+  memcpy(request, length_buffer, kLengthOfLength);
+
+  int host_socket = socket(AF_INET, SOCK_STREAM, 0);
+  if (host_socket < 0) {
+    LOG(ERROR) << "Failed to create adb socket: " << strerror(errno);
+    return -1;
+  }
+
+  DisableNagle(host_socket);
+
+  const int kAdbPort = 5037;
+  sockaddr_in addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  addr.sin_port = htons(kAdbPort);
+  if (HANDLE_EINTR(connect(host_socket, reinterpret_cast<sockaddr*>(&addr),
+                           sizeof(addr))) < 0) {
+    LOG(ERROR) << "Failed to connect adb socket: " << strerror(errno);
+    CloseSocket(host_socket);
+    return -1;
+  }
+
+  size_t bytes_remaining = address_length + kLengthOfLength;
+  size_t bytes_sent = 0;
+  while (bytes_remaining > 0) {
+    int ret = HANDLE_EINTR(send(host_socket, request + bytes_sent,
+                                bytes_remaining, 0));
+    if (ret < 0) {
+      LOG(ERROR) << "Failed to send request: " << strerror(errno);
+      CloseSocket(host_socket);
+      return -1;
+    }
+
+    bytes_sent += ret;
+    bytes_remaining -= ret;
+  }
+
+  const size_t kAdbStatusLength = 4;
+  char response[kBufferMaxLength];
+  int response_length = HANDLE_EINTR(recv(host_socket, response,
+                                          kBufferMaxLength, 0));
+  if (response_length < kAdbStatusLength ||
+      strncmp("OKAY", response, kAdbStatusLength) != 0) {
+    LOG(ERROR) << "Bad response from ADB: length: " << response_length
+               << " data: " << DumpBinary(response, response_length);
+    CloseSocket(host_socket);
+    return -1;
+  }
+
+  return host_socket;
+}
+
+}  // namespace tools
diff --git a/tools/android/common/adb_connection.h b/tools/android/common/adb_connection.h
new file mode 100644
index 0000000..3fa0fb3
--- /dev/null
+++ b/tools/android/common/adb_connection.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_COMMON_ADB_CONNECTION_H_
+#define TOOLS_ANDROID_COMMON_ADB_CONNECTION_H_
+
+namespace tools {
+
+// Creates a socket that can forward to a host socket through ADB.
+// The format of forward_to is <port>:<ip_address>.
+// Returns the socket handle, or -1 on any error.
+int ConnectAdbHostSocket(const char* forward_to);
+
+}  // namespace tools
+
+#endif  // TOOLS_ANDROID_COMMON_ADB_CONNECTION_H_
+
diff --git a/tools/android/common/common.gyp b/tools/android/common/common.gyp
new file mode 100644
index 0000000..8622625
--- /dev/null
+++ b/tools/android/common/common.gyp
@@ -0,0 +1,26 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'android_tools_common',
+      'type': 'static_library',
+      'toolsets': ['host', 'target'],
+      'include_dirs': [
+        '..',
+        '../../..',
+      ],
+      'sources': [
+        'adb_connection.cc',
+        'adb_connection.h',
+        'daemon.cc',
+        'daemon.h',
+        'net.cc',
+        'net.h',
+      ],
+    },
+  ],
+}
+
diff --git a/tools/android/common/daemon.cc b/tools/android/common/daemon.cc
new file mode 100644
index 0000000..699c615
--- /dev/null
+++ b/tools/android/common/daemon.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/common/daemon.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+
+namespace {
+
+const char kNoSpawnDaemon[] = "D";
+
+int g_exit_status = 0;
+
+void Exit(int unused) {
+  _exit(g_exit_status);
+}
+
+void CloseFileDescriptor(int fd) {
+  int old_errno = errno;
+  close(fd);
+  errno = old_errno;
+}
+
+}  // namespace
+
+namespace tools {
+
+bool HasHelpSwitch(const CommandLine& command_line) {
+  return command_line.HasSwitch("h") || command_line.HasSwitch("help");
+}
+
+bool HasNoSpawnDaemonSwitch(const CommandLine& command_line) {
+  return command_line.HasSwitch(kNoSpawnDaemon);
+}
+
+void ShowHelp(const char* program,
+              const char* extra_title,
+              const char* extra_descriptions) {
+  printf("Usage: %s [-%s] %s\n"
+         " -%s  stops from spawning a daemon process\n%s",
+         program, kNoSpawnDaemon, extra_title, kNoSpawnDaemon,
+         extra_descriptions);
+}
+
+void SpawnDaemon(int exit_status) {
+  g_exit_status = exit_status;
+  signal(SIGUSR1, Exit);
+
+  if (fork()) {
+    // In parent process.
+    sleep(10);  // Wait for the child process to finish setsid().
+    NOTREACHED();
+  }
+
+  // In child process.
+  setsid();  // Detach the child process from its parent.
+  kill(getppid(), SIGUSR1);  // Inform the parent process to exit.
+
+  // Close the standard input and outputs, otherwise the process may block
+  // adbd when the shell exits.
+  // Comment out these lines if you want to see outputs for debugging.
+  CloseFileDescriptor(0);
+  CloseFileDescriptor(1);
+  CloseFileDescriptor(2);
+}
+
+}  // namespace tools
diff --git a/tools/android/common/daemon.h b/tools/android/common/daemon.h
new file mode 100644
index 0000000..99faf72
--- /dev/null
+++ b/tools/android/common/daemon.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_COMMON_DAEMON_H_
+#define TOOLS_ANDROID_COMMON_DAEMON_H_
+
+namespace base {
+class CommandLine;
+}
+
+namespace tools {
+
+bool HasHelpSwitch(const base::CommandLine& command_line);
+
+bool HasNoSpawnDaemonSwitch(const base::CommandLine& command_line);
+
+void ShowHelp(const char* program,
+              const char* extra_title,
+              const char* extra_descriptions);
+
+// Spawns a daemon process and exits the current process with exit_status.
+// Any code executed after this function returns will be executed in the
+// spawned daemon process.
+void SpawnDaemon(int exit_status);
+
+}  // namespace tools
+
+#endif  // TOOLS_ANDROID_COMMON_DAEMON_H_
+
diff --git a/tools/android/common/net.cc b/tools/android/common/net.cc
new file mode 100644
index 0000000..3b9ef15
--- /dev/null
+++ b/tools/android/common/net.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/common/net.h"
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "base/strings/stringprintf.h"
+
+namespace tools {
+
+int DisableNagle(int socket) {
+  int on = 1;
+  return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+}
+
+int DeferAccept(int socket) {
+  int on = 1;
+  return setsockopt(socket, IPPROTO_TCP, TCP_DEFER_ACCEPT, &on, sizeof(on));
+}
+
+std::string DumpBinary(const char* buffer, size_t length) {
+  std::string result = "[";
+  for (int i = 0; i < length; ++i) {
+    base::StringAppendF(&result, "%02x,",
+                        static_cast<unsigned char>(buffer[i]));
+  }
+
+  if (length)
+    result.erase(result.length() - 1);
+
+  return result + "]";
+}
+
+}  // namespace tools
+
diff --git a/tools/android/common/net.h b/tools/android/common/net.h
new file mode 100644
index 0000000..e361954
--- /dev/null
+++ b/tools/android/common/net.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_COMMON_NET_H_
+#define TOOLS_ANDROID_COMMON_NET_H_
+
+#include <string>
+
+namespace tools {
+
+// DisableNagle can improve TCP transmission performance. Both Chrome net stack
+// and adb tool use it.
+int DisableNagle(int socket);
+
+// Wake up listener only when data arrive.
+int DeferAccept(int socket);
+
+// Dumps a binary buffer into a string in a human-readable format.
+std::string DumpBinary(const char* buffer, size_t length);
+
+}  // namespace tools
+
+#endif  // TOOLS_ANDROID_COMMON_NET_H_
+
diff --git a/tools/android/file_poller/file_poller.cc b/tools/android/file_poller/file_poller.cc
new file mode 100644
index 0000000..c73db8b
--- /dev/null
+++ b/tools/android/file_poller/file_poller.cc
@@ -0,0 +1,207 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// When run with 2 or more arguments the file_poller tool will open a port on
+// the device, print it on its standard output and then start collect file
+// contents.  The first argument is the polling rate in Hz, and the following
+// arguments are file to poll.
+// When run with the port of an already running file_poller, the tool will
+// contact the first instance, retrieve the sample and print those on its
+// standard output. This will also terminate the first instance.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+
+// Context containing the files to poll and the polling rate.
+struct Context {
+  size_t nb_files;
+  int* file_fds;
+  int poll_rate;
+};
+
+// Write from the buffer to the given file descriptor.
+void safe_write(int fd, const char* buffer, int size) {
+  const char* index = buffer;
+  size_t to_write = size;
+  while (to_write > 0) {
+    int written = write(fd, index, to_write);
+    if (written < 0)
+      PLOG(FATAL);
+    index += written;
+    to_write -= written;
+  }
+}
+
+// Transfer the content of a file descriptor to another.
+void transfer_to_fd(int fd_in, int fd_out) {
+  char buffer[1024];
+  int n;
+  while ((n = read(fd_in, buffer, sizeof(buffer))) > 0)
+    safe_write(fd_out, buffer, n);
+}
+
+// Transfer the content of a file descriptor to a buffer.
+int transfer_to_buffer(int fd_in, char* bufffer, size_t size) {
+  char* index = bufffer;
+  size_t to_read = size;
+  int n;
+  while (to_read > 0 && ((n = read(fd_in, index, to_read)) > 0)) {
+    index += n;
+    to_read -= n;
+  }
+  if (n < 0)
+    PLOG(FATAL);
+  return size - to_read;
+}
+
+// Try to open the file at the given path for reading. Exit in case of failure.
+int checked_open(const char* path) {
+  int fd = open(path, O_RDONLY);
+  if (fd < 0)
+    PLOG(FATAL);
+  return fd;
+}
+
+void transfer_measurement(int fd_in, int fd_out, bool last) {
+  char buffer[1024];
+  if (lseek(fd_in, 0, SEEK_SET) < 0)
+    PLOG(FATAL);
+  int n = transfer_to_buffer(fd_in, buffer, sizeof(buffer));
+  safe_write(fd_out, buffer, n - 1);
+  safe_write(fd_out, last ? "\n" : " ", 1);
+}
+
+// Acquire a sample and save it to the given file descriptor.
+void acquire_sample(int fd, const Context& context) {
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  char buffer[1024];
+  int n = snprintf(buffer, sizeof(buffer), "%d.%06d ", tv.tv_sec, tv.tv_usec);
+  safe_write(fd, buffer, n);
+
+  for (int i = 0; i < context.nb_files; ++i)
+    transfer_measurement(context.file_fds[i], fd, i == (context.nb_files - 1));
+}
+
+void poll_content(const Context& context) {
+  // Create and bind the socket so that the port can be written to stdout.
+  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+  struct sockaddr_in socket_info;
+  socket_info.sin_family = AF_INET;
+  socket_info.sin_addr.s_addr = htonl(INADDR_ANY);
+  socket_info.sin_port = htons(0);
+  if (bind(sockfd, (struct sockaddr*)&socket_info, sizeof(socket_info)) < 0)
+    PLOG(FATAL);
+  socklen_t size = sizeof(socket_info);
+  getsockname(sockfd, (struct sockaddr*)&socket_info, &size);
+  printf("%d\n", ntohs(socket_info.sin_port));
+  // Using a pipe to ensure child is diconnected from the terminal before
+  // quitting.
+  int pipes[2];
+  pipe(pipes);
+  pid_t pid = fork();
+  if (pid < 0)
+    PLOG(FATAL);
+  if (pid != 0) {
+    close(pipes[1]);
+    // Not expecting any data to be received.
+    read(pipes[0], NULL, 1);
+    signal(SIGCHLD, SIG_IGN);
+    return;
+  }
+
+  // Detach from terminal.
+  setsid();
+  close(STDIN_FILENO);
+  close(STDOUT_FILENO);
+  close(STDERR_FILENO);
+  close(pipes[0]);
+
+  // Start listening for incoming connection.
+  if (listen(sockfd, 1) < 0)
+    PLOG(FATAL);
+
+  // Signal the parent that it can now safely exit.
+  close(pipes[1]);
+
+  // Prepare file to store the samples.
+  int fd;
+  char filename[] = "/data/local/tmp/fileXXXXXX";
+  fd = mkstemp(filename);
+  unlink(filename);
+
+  // Collect samples until a client connect on the socket.
+  fd_set rfds;
+  struct timeval timeout;
+  do {
+    acquire_sample(fd, context);
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 1000000 / context.poll_rate;
+    FD_ZERO(&rfds);
+    FD_SET(sockfd, &rfds);
+  } while (select(sockfd + 1, &rfds, NULL, NULL, &timeout) == 0);
+
+  // Collect a final sample.
+  acquire_sample(fd, context);
+
+  // Send the result back.
+  struct sockaddr_in remote_socket_info;
+  int rfd = accept(sockfd, (struct sockaddr*)&remote_socket_info, &size);
+  if (rfd < 0)
+    PLOG(FATAL);
+  if (lseek(fd, 0, SEEK_SET) < 0)
+    PLOG(FATAL);
+  transfer_to_fd(fd, rfd);
+}
+
+void content_collection(int port) {
+  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+  // Connect to localhost.
+  struct sockaddr_in socket_info;
+  socket_info.sin_family = AF_INET;
+  socket_info.sin_addr.s_addr = htonl(0x7f000001);
+  socket_info.sin_port = htons(port);
+  if (connect(sockfd, (struct sockaddr*)&socket_info, sizeof(socket_info)) <
+      0) {
+    PLOG(FATAL);
+  }
+  transfer_to_fd(sockfd, STDOUT_FILENO);
+}
+
+int main(int argc, char** argv) {
+  if (argc == 1) {
+    fprintf(stderr,
+            "Usage: \n"
+            " %s port\n"
+            " %s rate FILE...\n",
+            argv[0],
+            argv[0]);
+    exit(EXIT_FAILURE);
+  }
+  if (argc == 2) {
+    // Argument is the port to connect to.
+    content_collection(atoi(argv[1]));
+  } else {
+    // First argument is the poll frequency, in Hz, following arguments are the
+    // file to poll.
+    Context context;
+    context.poll_rate = atoi(argv[1]);
+    context.nb_files = argc - 2;
+    context.file_fds = new int[context.nb_files];
+    for (int i = 2; i < argc; ++i)
+      context.file_fds[i - 2] = checked_open(argv[i]);
+    poll_content(context);
+  }
+  return EXIT_SUCCESS;
+}
diff --git a/tools/android/file_poller/file_poller.gyp b/tools/android/file_poller/file_poller.gyp
new file mode 100644
index 0000000..097344d
--- /dev/null
+++ b/tools/android/file_poller/file_poller.gyp
@@ -0,0 +1,18 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'file_poller',
+      'type': 'executable',
+      'dependencies': [
+        '../../../base/base.gyp:base',
+      ],
+      'sources': [
+        'file_poller.cc',
+      ],
+    },
+  ],
+}
diff --git a/tools/android/find_unused_resources.py b/tools/android/find_unused_resources.py
new file mode 100755
index 0000000..1e8fa48
--- /dev/null
+++ b/tools/android/find_unused_resources.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Lists unused Java strings and other resources."""
+
+import optparse
+import re
+import subprocess
+import sys
+
+
+def GetLibraryResources(r_txt_paths):
+  """Returns the resources packaged in a list of libraries.
+
+  Args:
+    r_txt_paths: paths to each library's generated R.txt file which lists the
+        resources it contains.
+
+  Returns:
+    The resources in the libraries as a list of tuples (type, name). Example:
+    [('drawable', 'arrow'), ('layout', 'month_picker'), ...]
+  """
+  resources = []
+  for r_txt_path in r_txt_paths:
+    with open(r_txt_path, 'r') as f:
+      for line in f:
+        line = line.strip()
+        if not line:
+          continue
+        data_type, res_type, name, _ = line.split(None, 3)
+        assert data_type in ('int', 'int[]')
+        # Hide attrs, which are redundant with styleables and always appear
+        # unused, and hide ids, which are innocuous even if unused.
+        if res_type in ('attr', 'id'):
+          continue
+        resources.append((res_type, name))
+  return resources
+
+
+def GetUsedResources(source_paths, resource_types):
+  """Returns the types and names of resources used in Java or resource files.
+
+  Args:
+    source_paths: a list of files or folders collectively containing all the
+        Java files, resource files, and the AndroidManifest.xml.
+    resource_types: a list of resource types to look for.  Example:
+        ['string', 'drawable']
+
+  Returns:
+    The resources referenced by the Java and resource files as a list of tuples
+    (type, name).  Example:
+    [('drawable', 'app_icon'), ('layout', 'month_picker'), ...]
+  """
+  type_regex = '|'.join(map(re.escape, resource_types))
+  patterns = [r'@(())(%s)/(\w+)' % type_regex,
+              r'\b((\w+\.)*)R\.(%s)\.(\w+)' % type_regex]
+  resources = []
+  for pattern in patterns:
+    p = subprocess.Popen(
+        ['grep', '-REIhoe', pattern] + source_paths,
+        stdout=subprocess.PIPE)
+    grep_out, grep_err = p.communicate()
+    # Check stderr instead of return code, since return code is 1 when no
+    # matches are found.
+    assert not grep_err, 'grep failed'
+    matches = re.finditer(pattern, grep_out)
+    for match in matches:
+      package = match.group(1)
+      if package == 'android.':
+        continue
+      type_ = match.group(3)
+      name = match.group(4)
+      resources.append((type_, name))
+  return resources
+
+
+def FormatResources(resources):
+  """Formats a list of resources for printing.
+
+  Args:
+    resources: a list of resources, given as (type, name) tuples.
+  """
+  return '\n'.join(['%-12s %s' % (t, n) for t, n in sorted(resources)])
+
+
+def ParseArgs(args):
+  parser = optparse.OptionParser()
+  parser.add_option('-v', help='Show verbose output', action='store_true')
+  parser.add_option('-s', '--source-path', help='Specify a source folder path '
+                    '(e.g. ui/android/java)', action='append', default=[])
+  parser.add_option('-r', '--r-txt-path', help='Specify a "first-party" R.txt '
+                    'file (e.g. out/Debug/content_shell_apk/R.txt)',
+                    action='append', default=[])
+  parser.add_option('-t', '--third-party-r-txt-path', help='Specify an R.txt '
+                    'file for a third party library', action='append',
+                    default=[])
+  options, args = parser.parse_args(args=args)
+  if args:
+    parser.error('positional arguments not allowed')
+  if not options.source_path:
+    parser.error('at least one source folder path must be specified with -s')
+  if not options.r_txt_path:
+    parser.error('at least one R.txt path must be specified with -r')
+  return (options.v, options.source_path, options.r_txt_path,
+          options.third_party_r_txt_path)
+
+
+def main(args=None):
+  verbose, source_paths, r_txt_paths, third_party_r_txt_paths = ParseArgs(args)
+  defined_resources = (set(GetLibraryResources(r_txt_paths)) -
+                       set(GetLibraryResources(third_party_r_txt_paths)))
+  resource_types = list(set([r[0] for r in defined_resources]))
+  used_resources = set(GetUsedResources(source_paths, resource_types))
+  unused_resources = defined_resources - used_resources
+  undefined_resources = used_resources - defined_resources
+
+  # aapt dump fails silently. Notify the user if things look wrong.
+  if not defined_resources:
+    print >> sys.stderr, (
+        'Warning: No resources found. Did you provide the correct R.txt paths?')
+  if not used_resources:
+    print >> sys.stderr, (
+        'Warning: No resources referenced from Java or resource files. Did you '
+        'provide the correct source paths?')
+  if undefined_resources:
+    print >> sys.stderr, (
+        'Warning: found %d "undefined" resources that are referenced by Java '
+        'files or by other resources, but are not defined anywhere. Run with '
+        '-v to see them.' % len(undefined_resources))
+
+  if verbose:
+    print '%d undefined resources:' % len(undefined_resources)
+    print FormatResources(undefined_resources), '\n'
+    print '%d resources defined:' % len(defined_resources)
+    print FormatResources(defined_resources), '\n'
+    print '%d used resources:' % len(used_resources)
+    print FormatResources(used_resources), '\n'
+    print '%d unused resources:' % len(unused_resources)
+  print FormatResources(unused_resources)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/android/findbugs_plugin/README b/tools/android/findbugs_plugin/README
new file mode 100644
index 0000000..3ba3f53
--- /dev/null
+++ b/tools/android/findbugs_plugin/README
@@ -0,0 +1,15 @@
+This is the FindBugs plugin for chrome on android.
+
+Currently it detects:
+- synchronized method
+- synchronized 'this'
+
+We don't want the synchronized method and synchronized 'this' to be
+used, the exception is the synchronized method defined in Android
+API.
+
+The plugin jar file was prebuilt and checked in, to rebuild the
+plugin, you need ant, and run below command, the new jar file will
+be in lib directory.
+
+ant install
diff --git a/tools/android/findbugs_plugin/build.xml b/tools/android/findbugs_plugin/build.xml
new file mode 100644
index 0000000..09ee13c
--- /dev/null
+++ b/tools/android/findbugs_plugin/build.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright (c) 2012 The Chromium Authors. All rights reserved.
+  Use of this source code is governed by a BSD-style license that can be
+  found in the LICENSE file.
+-->
+
+<project name="findbugs_plugin" basedir=".">
+
+  <description>
+    Build findbugs_plugin for Chromium Android
+  </description>
+  <property name="src.dir" location="src" />
+  <property name="lib.dir" location="../../../third_party/findbugs/lib" />
+  <property name="bin.dir" location="lib" />
+  <property name="intermediate.dir" location="intermediate" />
+  <property name="jar.name" value="chromiumPlugin.jar" />
+
+  <path id="classpath.id">
+    <fileset dir="${lib.dir}">
+      <include name="**/*.jar" />
+    </fileset>
+  </path>
+
+  <target name="makedir">
+    <mkdir dir="${intermediate.dir}" />
+    <mkdir dir="${bin.dir}" />
+  </target>
+
+  <target name="findbugs_plugin_classes" depends="makedir">
+    <javac srcdir="${src.dir}" destdir="${intermediate.dir}"
+        classpathref="classpath.id" includeantruntime="false" />
+  </target>
+
+  <target name="copy_xml_files" depends="makedir">
+    <copy file="messages.xml" todir="${intermediate.dir}" />
+    <copy file="findbugs.xml" todir="${intermediate.dir}" />
+  </target>
+
+  <target name="findbugs_plugin_jar" depends="findbugs_plugin_classes, copy_xml_files">
+    <jar destfile="${bin.dir}/${jar.name}" basedir="${intermediate.dir}">
+    </jar>
+  </target>
+
+  <target name="install" depends="findbugs_plugin_jar">
+    <delete dir="${intermediate.dir}" />
+  </target>
+</project>
diff --git a/tools/android/findbugs_plugin/findbugs.xml b/tools/android/findbugs_plugin/findbugs.xml
new file mode 100644
index 0000000..43b1f34
--- /dev/null
+++ b/tools/android/findbugs_plugin/findbugs.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright (c) 2012 The Chromium Authors. All rights reserved.
+  Use of this source code is governed by a BSD-style license that can be
+  found in the LICENSE file.
+-->
+
+<FindbugsPlugin xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:noNamespaceSchemaLocation="findbugsplugin.xsd"
+        pluginid="SynchronizedThisDetector"
+        provider="chromium"
+        website="http://code.google.com/p/chromium/wiki/UseFindBugsForAndroid">
+        <Detector class="org.chromium.tools.findbugs.plugin.SynchronizedThisDetector" reports="CHROMIUM_SYNCHRONIZED_THIS" />
+        <BugPattern type="CHROMIUM_SYNCHRONIZED_THIS" abbrev="CST" category="CORRECTNESS"/>
+
+        <Detector class="org.chromium.tools.findbugs.plugin.SynchronizedMethodDetector" reports="CHROMIUM_SYNCHRONIZED_METHOD" />
+        <BugPattern type="CHROMIUM_SYNCHRONIZED_METHOD" abbrev="CSM" category="CORRECTNESS"/>
+</FindbugsPlugin>
diff --git a/tools/android/findbugs_plugin/findbugs_plugin.gyp b/tools/android/findbugs_plugin/findbugs_plugin.gyp
new file mode 100644
index 0000000..16d06e6
--- /dev/null
+++ b/tools/android/findbugs_plugin/findbugs_plugin.gyp
@@ -0,0 +1,16 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+   'targets': [
+      {
+         'target_name': 'findbugs_plugin_test',
+         'type': 'none',
+         'variables': {
+           'java_in_dir': 'test/java/',
+         },
+         'includes': [ '../../../build/java.gypi' ],
+      }
+   ]
+}
diff --git a/tools/android/findbugs_plugin/lib/chromiumPlugin.jar b/tools/android/findbugs_plugin/lib/chromiumPlugin.jar
new file mode 100644
index 0000000..6ccf61b
--- /dev/null
+++ b/tools/android/findbugs_plugin/lib/chromiumPlugin.jar
Binary files differ
diff --git a/tools/android/findbugs_plugin/messages.xml b/tools/android/findbugs_plugin/messages.xml
new file mode 100644
index 0000000..aea983b
--- /dev/null
+++ b/tools/android/findbugs_plugin/messages.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Copyright (c) 2012 The Chromium Authors. All rights reserved.
+  Use of this source code is governed by a BSD-style license that can be
+  found in the LICENSE file.
+-->
+
+<MessageCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:noNamespaceSchemaLocation="messagecollection.xsd">
+
+        <Plugin>
+                <ShortDescription>Chromium FindBugs Plugin </ShortDescription>
+                <Details>Adds style checks enforced in the chromium project.</Details>
+        </Plugin>
+
+        <Detector class="org.chromium.tools.findbugs.plugin.SynchronizedThisDetector">
+                <Details>
+                          <![CDATA[
+                        Shouldn't use synchronized(this).
+                              ]]>
+                </Details>
+
+        </Detector>
+
+        <BugPattern type="CHROMIUM_SYNCHRONIZED_THIS">
+                <ShortDescription>Shouldn't use synchronized(this)</ShortDescription>
+                <LongDescription>Shouldn't use synchronized(this), please narrow down the synchronization scope.</LongDescription>
+                <Details>
+<![CDATA[
+<p>Shouldn't use synchronized(this), please narrow down the synchronization scope.</p>
+]]>
+                </Details>
+        </BugPattern>
+
+        <Detector class="org.chromium.tools.findbugs.plugin.SynchronizedMethodDetector">
+                <Details>
+                          <![CDATA[
+                        Shouldn't use synchronized method.
+                              ]]>
+                </Details>
+
+        </Detector>
+
+        <BugPattern type="CHROMIUM_SYNCHRONIZED_METHOD">
+                <ShortDescription>Shouldn't use synchronized method</ShortDescription>
+                <LongDescription>Shouldn't use synchronized method, please narrow down the synchronization scope.</LongDescription>
+                <Details>
+<![CDATA[
+<p>Shouldn't use synchronized method, please narrow down the synchronization scope.</p>
+]]>
+                </Details>
+        </BugPattern>
+
+        <BugCode abbrev="CHROMIUM">CHROMIUM</BugCode>
+</MessageCollection>
diff --git a/tools/android/findbugs_plugin/src/org/chromium/tools/findbugs/plugin/SynchronizedMethodDetector.java b/tools/android/findbugs_plugin/src/org/chromium/tools/findbugs/plugin/SynchronizedMethodDetector.java
new file mode 100644
index 0000000..d1d7614
--- /dev/null
+++ b/tools/android/findbugs_plugin/src/org/chromium/tools/findbugs/plugin/SynchronizedMethodDetector.java
@@ -0,0 +1,37 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.tools.findbugs.plugin;
+
+import org.apache.bcel.classfile.Code;
+
+import edu.umd.cs.findbugs.BugInstance;
+import edu.umd.cs.findbugs.BugReporter;
+import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
+
+/**
+ * This class detects the synchronized method.
+ */
+public class SynchronizedMethodDetector extends OpcodeStackDetector {
+    private BugReporter mBugReporter;
+
+    public SynchronizedMethodDetector(BugReporter bugReporter) {
+        this.mBugReporter = bugReporter;
+    }
+
+    @Override
+    public void visit(Code code) {
+        if (getMethod().isSynchronized()) {
+            mBugReporter.reportBug(new BugInstance(this, "CHROMIUM_SYNCHRONIZED_METHOD",
+                                                   NORMAL_PRIORITY)
+                    .addClassAndMethod(this)
+                    .addSourceLine(this));
+        }
+        super.visit(code);
+    }
+
+    @Override
+    public void sawOpcode(int arg0) {
+    }
+}
diff --git a/tools/android/findbugs_plugin/src/org/chromium/tools/findbugs/plugin/SynchronizedThisDetector.java b/tools/android/findbugs_plugin/src/org/chromium/tools/findbugs/plugin/SynchronizedThisDetector.java
new file mode 100644
index 0000000..9a4e5e1
--- /dev/null
+++ b/tools/android/findbugs_plugin/src/org/chromium/tools/findbugs/plugin/SynchronizedThisDetector.java
@@ -0,0 +1,73 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.tools.findbugs.plugin;
+
+import org.apache.bcel.classfile.Code;
+
+import edu.umd.cs.findbugs.BugInstance;
+import edu.umd.cs.findbugs.BugReporter;
+import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
+
+/**
+ * This class detects the synchronized(this).
+ *
+ * The pattern of byte code of synchronized(this) is
+ * aload_0         # Load the 'this' pointer on top of stack
+ * dup             # Duplicate the 'this' pointer
+ * astore_x        # Store this for late use, it might be astore.
+ * monitorenter
+ */
+public class SynchronizedThisDetector extends OpcodeStackDetector {
+    private static final int PATTERN[] = {ALOAD_0, DUP, 0xff, 0xff, MONITORENTER};
+
+    private int mStep = 0;
+    private BugReporter mBugReporter;
+
+    public SynchronizedThisDetector(BugReporter bugReporter) {
+        mBugReporter = bugReporter;
+    }
+
+    @Override
+    public void visit(Code code) {
+        mStep = 0;
+        super.visit(code);
+    }
+
+    @Override
+    public void sawOpcode(int seen) {
+        if (PATTERN[mStep] == seen) {
+            mStep++;
+            if (mStep == PATTERN.length) {
+                mBugReporter.reportBug(new BugInstance(this, "CHROMIUM_SYNCHRONIZED_THIS",
+                                                       NORMAL_PRIORITY)
+                        .addClassAndMethod(this)
+                        .addSourceLine(this));
+                mStep = 0;
+                return;
+            }
+        } else if (mStep == 2) {
+            // This could be astore_x
+            switch (seen) {
+                case ASTORE_0:
+                case ASTORE_1:
+                case ASTORE_2:
+                case ASTORE_3:
+                    mStep += 2;
+                    break;
+                case ASTORE:
+                    mStep++;
+                    break;
+                default:
+                    mStep = 0;
+                    break;
+            }
+        } else if (mStep == 3) {
+            // Could be any byte following the ASTORE.
+            mStep++;
+        } else {
+            mStep = 0;
+        }
+    }
+}
diff --git a/tools/android/findbugs_plugin/test/expected_result.txt b/tools/android/findbugs_plugin/test/expected_result.txt
new file mode 100644
index 0000000..076b007
--- /dev/null
+++ b/tools/android/findbugs_plugin/test/expected_result.txt
@@ -0,0 +1,3 @@
+M C CSM: Shouldn't use synchronized method, please narrow down the synchronization scope.  At SimpleSynchronizedMethod.java
+M C CSM: Shouldn't use synchronized method, please narrow down the synchronization scope.  At SimpleSynchronizedStaticMethod.java
+M C CST: Shouldn't use synchronized(this), please narrow down the synchronization scope.  At SimpleSynchronizedThis.java
diff --git a/tools/android/findbugs_plugin/test/java/src/org/chromium/tools/findbugs/plugin/SimpleSynchronizedMethod.java b/tools/android/findbugs_plugin/test/java/src/org/chromium/tools/findbugs/plugin/SimpleSynchronizedMethod.java
new file mode 100644
index 0000000..ded7848
--- /dev/null
+++ b/tools/android/findbugs_plugin/test/java/src/org/chromium/tools/findbugs/plugin/SimpleSynchronizedMethod.java
@@ -0,0 +1,17 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.tools.findbugs.plugin;
+
+/**
+ * This class has synchronized method and is used to test
+ * SynchronizedMethodDetector.
+ */
+class SimpleSynchronizedMethod {
+    private int mCounter = 0;
+
+    synchronized void synchronizedMethod() {
+        mCounter++;
+    }
+}
diff --git a/tools/android/findbugs_plugin/test/java/src/org/chromium/tools/findbugs/plugin/SimpleSynchronizedStaticMethod.java b/tools/android/findbugs_plugin/test/java/src/org/chromium/tools/findbugs/plugin/SimpleSynchronizedStaticMethod.java
new file mode 100644
index 0000000..d652dbe
--- /dev/null
+++ b/tools/android/findbugs_plugin/test/java/src/org/chromium/tools/findbugs/plugin/SimpleSynchronizedStaticMethod.java
@@ -0,0 +1,16 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.tools.findbugs.plugin;
+
+/**
+ * This class is used to test SynchronizedMethodDetector
+ */
+class SimpleSynchronizedStaticMethod {
+    private static int sCounter = 0;
+
+    static synchronized void synchronizedStaticMethod() {
+        sCounter++;
+    }
+}
diff --git a/tools/android/findbugs_plugin/test/java/src/org/chromium/tools/findbugs/plugin/SimpleSynchronizedThis.java b/tools/android/findbugs_plugin/test/java/src/org/chromium/tools/findbugs/plugin/SimpleSynchronizedThis.java
new file mode 100644
index 0000000..9125155
--- /dev/null
+++ b/tools/android/findbugs_plugin/test/java/src/org/chromium/tools/findbugs/plugin/SimpleSynchronizedThis.java
@@ -0,0 +1,19 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.tools.findbugs.plugin;
+
+/**
+ * This class has synchronized(this) statement and is used to test
+ * SynchronizedThisDetector.
+ */
+class SimpleSynchronizedThis {
+    private int mCounter = 0;
+
+    void synchronizedThis() {
+        synchronized (this) {
+            mCounter++;
+        }
+    }
+}
diff --git a/tools/android/findbugs_plugin/test/run_findbugs_plugin_tests.py b/tools/android/findbugs_plugin/test/run_findbugs_plugin_tests.py
new file mode 100755
index 0000000..c2e1531
--- /dev/null
+++ b/tools/android/findbugs_plugin/test/run_findbugs_plugin_tests.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This is used to test the findbugs plugin, it calls
+# build/android/pylib/utils/findbugs.py to analyze the classes in
+# org.chromium.tools.findbugs.plugin package, and expects to get the same
+# issue with those in expected_result.txt.
+#
+# Useful command line:
+# --rebaseline to generate the expected_result.txt, please make sure don't
+# remove the expected result of exsting tests.
+
+
+import optparse
+import os
+import sys
+
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                             '..', '..', '..', '..',
+                                             'build', 'android')))
+
+from pylib import constants
+from pylib.utils import findbugs
+
+
+def main(argv):
+  parser = findbugs.GetCommonParser()
+
+  options, _ = parser.parse_args()
+
+  if not options.known_bugs:
+    options.known_bugs = os.path.join(constants.DIR_SOURCE_ROOT, 'tools',
+                                      'android', 'findbugs_plugin', 'test',
+                                      'expected_result.txt')
+  if not options.only_analyze:
+    options.only_analyze = 'org.chromium.tools.findbugs.plugin.*'
+
+  return findbugs.Run(options)
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/tools/android/forwarder/forwarder.cc b/tools/android/forwarder/forwarder.cc
new file mode 100644
index 0000000..fe49903
--- /dev/null
+++ b/tools/android/forwarder/forwarder.cc
@@ -0,0 +1,426 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "tools/android/common/adb_connection.h"
+#include "tools/android/common/daemon.h"
+#include "tools/android/common/net.h"
+
+namespace {
+
+const pthread_t kInvalidThread = static_cast<pthread_t>(-1);
+volatile bool g_killed = false;
+
+void CloseSocket(int fd) {
+  if (fd >= 0) {
+    int old_errno = errno;
+    close(fd);
+    errno = old_errno;
+  }
+}
+
+class Buffer {
+ public:
+  Buffer()
+      : bytes_read_(0),
+        write_offset_(0) {
+  }
+
+  bool CanRead() {
+    return bytes_read_ == 0;
+  }
+
+  bool CanWrite() {
+    return write_offset_ < bytes_read_;
+  }
+
+  int Read(int fd) {
+    int ret = -1;
+    if (CanRead()) {
+      ret = HANDLE_EINTR(read(fd, buffer_, kBufferSize));
+      if (ret > 0)
+        bytes_read_ = ret;
+    }
+    return ret;
+  }
+
+  int Write(int fd) {
+    int ret = -1;
+    if (CanWrite()) {
+      ret = HANDLE_EINTR(write(fd, buffer_ + write_offset_,
+                               bytes_read_ - write_offset_));
+      if (ret > 0) {
+        write_offset_ += ret;
+        if (write_offset_ == bytes_read_) {
+          write_offset_ = 0;
+          bytes_read_ = 0;
+        }
+      }
+    }
+    return ret;
+  }
+
+ private:
+  // A big buffer to let our file-over-http bridge work more like real file.
+  static const int kBufferSize = 1024 * 128;
+  int bytes_read_;
+  int write_offset_;
+  char buffer_[kBufferSize];
+
+  DISALLOW_COPY_AND_ASSIGN(Buffer);
+};
+
+class Server;
+
+struct ForwarderThreadInfo {
+  ForwarderThreadInfo(Server* a_server, int a_forwarder_index)
+      : server(a_server),
+        forwarder_index(a_forwarder_index) {
+  }
+  Server* server;
+  int forwarder_index;
+};
+
+struct ForwarderInfo {
+  time_t start_time;
+  int socket1;
+  time_t socket1_last_byte_time;
+  size_t socket1_bytes;
+  int socket2;
+  time_t socket2_last_byte_time;
+  size_t socket2_bytes;
+};
+
+class Server {
+ public:
+  Server()
+      : thread_(kInvalidThread),
+        socket_(-1) {
+    memset(forward_to_, 0, sizeof(forward_to_));
+    memset(&forwarders_, 0, sizeof(forwarders_));
+  }
+
+  int GetFreeForwarderIndex() {
+    for (int i = 0; i < kMaxForwarders; i++) {
+      if (forwarders_[i].start_time == 0)
+        return i;
+    }
+    return -1;
+  }
+
+  void DisposeForwarderInfo(int index) {
+    forwarders_[index].start_time = 0;
+  }
+
+  ForwarderInfo* GetForwarderInfo(int index) {
+    return &forwarders_[index];
+  }
+
+  void DumpInformation() {
+    LOG(INFO) << "Server information: " << forward_to_;
+    LOG(INFO) << "No.: age up(bytes,idle) down(bytes,idle)";
+    int count = 0;
+    time_t now = time(NULL);
+    for (int i = 0; i < kMaxForwarders; i++) {
+      const ForwarderInfo& info = forwarders_[i];
+      if (info.start_time) {
+        count++;
+        LOG(INFO) << count << ": " << now - info.start_time << " up("
+                  << info.socket1_bytes << ","
+                  << now - info.socket1_last_byte_time << " down("
+                  << info.socket2_bytes << ","
+                  << now - info.socket2_last_byte_time << ")";
+      }
+    }
+  }
+
+  void Shutdown() {
+    if (socket_ >= 0)
+      shutdown(socket_, SHUT_RDWR);
+  }
+
+  bool InitSocket(const char* arg);
+
+  void StartThread() {
+    pthread_create(&thread_, NULL, ServerThread, this);
+  }
+
+  void JoinThread() {
+    if (thread_ != kInvalidThread)
+      pthread_join(thread_, NULL);
+  }
+
+ private:
+  static void* ServerThread(void* arg);
+
+  // There are 3 kinds of threads that will access the array:
+  // 1. Server thread will get a free ForwarderInfo and initialize it;
+  // 2. Forwarder threads will dispose the ForwarderInfo when it finishes;
+  // 3. Main thread will iterate and print the forwarders.
+  // Using an array is not optimal, but can avoid locks or other complex
+  // inter-thread communication.
+  static const int kMaxForwarders = 512;
+  ForwarderInfo forwarders_[kMaxForwarders];
+
+  pthread_t thread_;
+  int socket_;
+  char forward_to_[40];
+
+  DISALLOW_COPY_AND_ASSIGN(Server);
+};
+
+// Forwards all outputs from one socket to another socket.
+void* ForwarderThread(void* arg) {
+  ForwarderThreadInfo* thread_info =
+      reinterpret_cast<ForwarderThreadInfo*>(arg);
+  Server* server = thread_info->server;
+  int index = thread_info->forwarder_index;
+  delete thread_info;
+  ForwarderInfo* info = server->GetForwarderInfo(index);
+  int socket1 = info->socket1;
+  int socket2 = info->socket2;
+  int nfds = socket1 > socket2 ? socket1 + 1 : socket2 + 1;
+  fd_set read_fds;
+  fd_set write_fds;
+  Buffer buffer1;
+  Buffer buffer2;
+
+  while (!g_killed) {
+    FD_ZERO(&read_fds);
+    if (buffer1.CanRead())
+      FD_SET(socket1, &read_fds);
+    if (buffer2.CanRead())
+      FD_SET(socket2, &read_fds);
+
+    FD_ZERO(&write_fds);
+    if (buffer1.CanWrite())
+      FD_SET(socket2, &write_fds);
+    if (buffer2.CanWrite())
+      FD_SET(socket1, &write_fds);
+
+    if (HANDLE_EINTR(select(nfds, &read_fds, &write_fds, NULL, NULL)) <= 0) {
+      LOG(ERROR) << "Select error: " << strerror(errno);
+      break;
+    }
+
+    int now = time(NULL);
+    if (FD_ISSET(socket1, &read_fds)) {
+      info->socket1_last_byte_time = now;
+      int bytes = buffer1.Read(socket1);
+      if (bytes <= 0)
+        break;
+      info->socket1_bytes += bytes;
+    }
+    if (FD_ISSET(socket2, &read_fds)) {
+      info->socket2_last_byte_time = now;
+      int bytes = buffer2.Read(socket2);
+      if (bytes <= 0)
+        break;
+      info->socket2_bytes += bytes;
+    }
+    if (FD_ISSET(socket1, &write_fds)) {
+      if (buffer2.Write(socket1) <= 0)
+        break;
+    }
+    if (FD_ISSET(socket2, &write_fds)) {
+      if (buffer1.Write(socket2) <= 0)
+        break;
+    }
+  }
+
+  CloseSocket(socket1);
+  CloseSocket(socket2);
+  server->DisposeForwarderInfo(index);
+  return NULL;
+}
+
+// Listens to a server socket. On incoming request, forward it to the host.
+// static
+void* Server::ServerThread(void* arg) {
+  Server* server = reinterpret_cast<Server*>(arg);
+  while (!g_killed) {
+    int forwarder_index = server->GetFreeForwarderIndex();
+    if (forwarder_index < 0) {
+      LOG(ERROR) << "Too many forwarders";
+      continue;
+    }
+
+    struct sockaddr_in addr;
+    socklen_t addr_len = sizeof(addr);
+    int socket = HANDLE_EINTR(accept(server->socket_,
+                                     reinterpret_cast<sockaddr*>(&addr),
+                                     &addr_len));
+    if (socket < 0) {
+      LOG(ERROR) << "Failed to accept: " << strerror(errno);
+      break;
+    }
+    tools::DisableNagle(socket);
+
+    int host_socket = tools::ConnectAdbHostSocket(server->forward_to_);
+    if (host_socket >= 0) {
+      // Set NONBLOCK flag because we use select().
+      fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) | O_NONBLOCK);
+      fcntl(host_socket, F_SETFL, fcntl(host_socket, F_GETFL) | O_NONBLOCK);
+
+      ForwarderInfo* forwarder_info = server->GetForwarderInfo(forwarder_index);
+      time_t now = time(NULL);
+      forwarder_info->start_time = now;
+      forwarder_info->socket1 = socket;
+      forwarder_info->socket1_last_byte_time = now;
+      forwarder_info->socket1_bytes = 0;
+      forwarder_info->socket2 = host_socket;
+      forwarder_info->socket2_last_byte_time = now;
+      forwarder_info->socket2_bytes = 0;
+
+      pthread_t thread;
+      pthread_create(&thread, NULL, ForwarderThread,
+                     new ForwarderThreadInfo(server, forwarder_index));
+    } else {
+      // Close the unused client socket which is failed to connect to host.
+      CloseSocket(socket);
+    }
+  }
+
+  CloseSocket(server->socket_);
+  server->socket_ = -1;
+  return NULL;
+}
+
+// Format of arg: <Device port>[:<Forward to port>:<Forward to address>]
+bool Server::InitSocket(const char* arg) {
+  char* endptr;
+  int local_port = static_cast<int>(strtol(arg, &endptr, 10));
+  if (local_port < 0)
+    return false;
+
+  if (*endptr != ':') {
+    snprintf(forward_to_, sizeof(forward_to_), "%d:127.0.0.1", local_port);
+  } else {
+    strncpy(forward_to_, endptr + 1, sizeof(forward_to_) - 1);
+  }
+
+  socket_ = socket(AF_INET, SOCK_STREAM, 0);
+  if (socket_ < 0) {
+    perror("server socket");
+    return false;
+  }
+  tools::DisableNagle(socket_);
+
+  sockaddr_in addr;
+  memset(&addr, 0, sizeof(addr));
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  addr.sin_port = htons(local_port);
+  int reuse_addr = 1;
+  setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR,
+             &reuse_addr, sizeof(reuse_addr));
+  tools::DeferAccept(socket_);
+  if (HANDLE_EINTR(bind(socket_, reinterpret_cast<sockaddr*>(&addr),
+                        sizeof(addr))) < 0 ||
+      HANDLE_EINTR(listen(socket_, 5)) < 0) {
+    perror("server bind");
+    CloseSocket(socket_);
+    socket_ = -1;
+    return false;
+  }
+
+  if (local_port == 0) {
+    socklen_t addrlen = sizeof(addr);
+    if (getsockname(socket_, reinterpret_cast<sockaddr*>(&addr), &addrlen)
+        != 0) {
+      perror("get listen address");
+      CloseSocket(socket_);
+      socket_ = -1;
+      return false;
+    }
+    local_port = ntohs(addr.sin_port);
+  }
+
+  printf("Forwarding device port %d to host %s\n", local_port, forward_to_);
+  return true;
+}
+
+int g_server_count = 0;
+Server* g_servers = NULL;
+
+void KillHandler(int unused) {
+  g_killed = true;
+  for (int i = 0; i < g_server_count; i++)
+    g_servers[i].Shutdown();
+}
+
+void DumpInformation(int unused) {
+  for (int i = 0; i < g_server_count; i++)
+    g_servers[i].DumpInformation();
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  printf("Android device to host TCP forwarder\n");
+  printf("Like 'adb forward' but in the reverse direction\n");
+
+  CommandLine command_line(argc, argv);
+  CommandLine::StringVector server_args = command_line.GetArgs();
+  if (tools::HasHelpSwitch(command_line) || server_args.empty()) {
+    tools::ShowHelp(
+        argv[0],
+        "<Device port>[:<Forward to port>:<Forward to address>] ...",
+        "  <Forward to port> default is <Device port>\n"
+        "  <Forward to address> default is 127.0.0.1\n"
+        "If <Device port> is 0, a port will by dynamically allocated.\n");
+    return 0;
+  }
+
+  g_servers = new Server[server_args.size()];
+  g_server_count = 0;
+  int failed_count = 0;
+  for (size_t i = 0; i < server_args.size(); i++) {
+    if (!g_servers[g_server_count].InitSocket(server_args[i].c_str())) {
+      printf("Couldn't start forwarder server for port spec: %s\n",
+             server_args[i].c_str());
+      ++failed_count;
+    } else {
+      ++g_server_count;
+    }
+  }
+
+  if (g_server_count == 0) {
+    printf("No forwarder servers could be started. Exiting.\n");
+    delete [] g_servers;
+    return failed_count;
+  }
+
+  if (!tools::HasNoSpawnDaemonSwitch(command_line))
+    tools::SpawnDaemon(failed_count);
+
+  signal(SIGTERM, KillHandler);
+  signal(SIGUSR2, DumpInformation);
+
+  for (int i = 0; i < g_server_count; i++)
+    g_servers[i].StartThread();
+  for (int i = 0; i < g_server_count; i++)
+    g_servers[i].JoinThread();
+  g_server_count = 0;
+  delete [] g_servers;
+
+  return 0;
+}
+
diff --git a/tools/android/forwarder/forwarder.gyp b/tools/android/forwarder/forwarder.gyp
new file mode 100644
index 0000000..1df518b
--- /dev/null
+++ b/tools/android/forwarder/forwarder.gyp
@@ -0,0 +1,43 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'forwarder',
+      'type': 'none',
+      'dependencies': [
+        'forwarder_symbols',
+      ],
+      'actions': [
+        {
+          'action_name': 'strip_forwarder',
+          'inputs': ['<(PRODUCT_DIR)/forwarder_symbols'],
+          'outputs': ['<(PRODUCT_DIR)/forwarder'],
+          'action': [
+            '<(android_strip)',
+            '--strip-unneeded',
+            '<@(_inputs)',
+            '-o',
+            '<@(_outputs)',
+          ],
+        },
+      ],
+    }, {
+      'target_name': 'forwarder_symbols',
+      'type': 'executable',
+      'dependencies': [
+        '../../../base/base.gyp:base',
+        '../common/common.gyp:android_tools_common',
+      ],
+      'include_dirs': [
+        '../../..',
+      ],
+      'sources': [
+        'forwarder.cc',
+      ],
+    },
+  ],
+}
+
diff --git a/tools/android/forwarder2/command.cc b/tools/android/forwarder2/command.cc
new file mode 100644
index 0000000..9b0aa24
--- /dev/null
+++ b/tools/android/forwarder2/command.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/command.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/safe_strerror_posix.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "tools/android/forwarder2/socket.h"
+
+using base::StringPiece;
+
+namespace {
+
+
+// Command format:
+//      <port>:<type>
+//
+// Where:
+//   <port> is a 5-chars zero-padded ASCII decimal integer
+//          matching the target port for the command (e.g.
+//          '08080' for port 8080)
+//   <type> is a 3-char zero-padded ASCII decimal integer
+//          matching a command::Type value (e.g. 002 for
+//          ACK).
+// The column (:) is used as a separator for easier reading.
+const int kPortStringSize = 5;
+const int kCommandTypeStringSize = 2;
+// Command string size also includes the ':' separator char.
+const int kCommandStringSize = kPortStringSize + kCommandTypeStringSize + 1;
+
+}  // namespace
+
+namespace forwarder2 {
+
+bool ReadCommand(Socket* socket,
+                 int* port_out,
+                 command::Type* command_type_out) {
+  char command_buffer[kCommandStringSize + 1];
+  // To make logging easier.
+  command_buffer[kCommandStringSize] = '\0';
+
+  int bytes_read = socket->ReadNumBytes(command_buffer, kCommandStringSize);
+  if (bytes_read != kCommandStringSize) {
+    if (bytes_read < 0)
+      LOG(ERROR) << "Read() error: " << safe_strerror(errno);
+    else if (!bytes_read)
+      LOG(ERROR) << "Read() error, endpoint was unexpectedly closed.";
+    else
+      LOG(ERROR) << "Read() error, not enough data received from the socket.";
+    return false;
+  }
+
+  StringPiece port_str(command_buffer, kPortStringSize);
+  if (!StringToInt(port_str, port_out)) {
+    LOG(ERROR) << "Could not parse the command port string: "
+               << port_str;
+    return false;
+  }
+
+  StringPiece command_type_str(
+      &command_buffer[kPortStringSize + 1], kCommandTypeStringSize);
+  int command_type;
+  if (!StringToInt(command_type_str, &command_type)) {
+    LOG(ERROR) << "Could not parse the command type string: "
+               << command_type_str;
+    return false;
+  }
+  *command_type_out = static_cast<command::Type>(command_type);
+  return true;
+}
+
+bool SendCommand(command::Type command, int port, Socket* socket) {
+  char buffer[kCommandStringSize + 1];
+  int len = snprintf(buffer, sizeof(buffer), "%05d:%02d", port, command);
+  CHECK_EQ(len, kCommandStringSize);
+  // Write the full command minus the leading \0 char.
+  return socket->WriteNumBytes(buffer, len) == len;
+}
+
+bool ReceivedCommand(command::Type command, Socket* socket) {
+  int port;
+  command::Type received_command;
+  if (!ReadCommand(socket, &port, &received_command))
+    return false;
+  return received_command == command;
+}
+
+}  // namespace forwarder
diff --git a/tools/android/forwarder2/command.h b/tools/android/forwarder2/command.h
new file mode 100644
index 0000000..8e222ef
--- /dev/null
+++ b/tools/android/forwarder2/command.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_COMMAND_H_
+#define TOOLS_ANDROID_FORWARDER2_COMMAND_H_
+
+#include "base/basictypes.h"
+
+namespace forwarder2 {
+
+class Socket;
+
+namespace command {
+
+enum Type {
+  ACCEPT_ERROR = 0,
+  ACCEPT_SUCCESS,
+  ACK,
+  ADB_DATA_SOCKET_ERROR,
+  ADB_DATA_SOCKET_SUCCESS,
+  BIND_ERROR,
+  BIND_SUCCESS,
+  DATA_CONNECTION,
+  HOST_SERVER_ERROR,
+  HOST_SERVER_SUCCESS,
+  KILL_ALL_LISTENERS,
+  LISTEN,
+  UNLISTEN,
+  UNLISTEN_ERROR,
+  UNLISTEN_SUCCESS,
+};
+
+}  // namespace command
+
+bool ReadCommand(Socket* socket,
+                 int* port_out,
+                 command::Type* command_type_out);
+
+// Helper function to read the command from the |socket| and return true if the
+// |command| is equal to the given command parameter.
+bool ReceivedCommand(command::Type command, Socket* socket);
+
+bool SendCommand(command::Type command, int port, Socket* socket);
+
+}  // namespace forwarder
+
+#endif  // TOOLS_ANDROID_FORWARDER2_COMMAND_H_
diff --git a/tools/android/forwarder2/common.cc b/tools/android/forwarder2/common.cc
new file mode 100644
index 0000000..3b7387d
--- /dev/null
+++ b/tools/android/forwarder2/common.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/common.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/safe_strerror_posix.h"
+
+namespace forwarder2 {
+
+void PError(const char* msg) {
+  LOG(ERROR) << msg << ": " << safe_strerror(errno);
+}
+
+void CloseFD(int fd) {
+  const int errno_copy = errno;
+  if (IGNORE_EINTR(close(fd)) < 0) {
+    PError("close");
+    errno = errno_copy;
+  }
+}
+
+}  // namespace forwarder2
diff --git a/tools/android/forwarder2/common.h b/tools/android/forwarder2/common.h
new file mode 100644
index 0000000..43de57b
--- /dev/null
+++ b/tools/android/forwarder2/common.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Common helper functions/classes used both in the host and device forwarder.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_COMMON_H_
+#define TOOLS_ANDROID_FORWARDER2_COMMON_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+// Preserving errno for Close() is important because the function is very often
+// used in cleanup code, after an error occurred, and it is very easy to pass an
+// invalid file descriptor to close() in this context, or more rarely, a
+// spurious signal might make close() return -1 + setting errno to EINTR,
+// masking the real reason for the original error. This leads to very unpleasant
+// debugging sessions.
+#define PRESERVE_ERRNO_HANDLE_EINTR(Func)                     \
+  do {                                                        \
+    int local_errno = errno;                                  \
+    (void) HANDLE_EINTR(Func);                                \
+    errno = local_errno;                                      \
+  } while (false);
+
+// Wrapper around RAW_LOG() which is signal-safe. The only purpose of this macro
+// is to avoid documenting uses of RawLog().
+#define SIGNAL_SAFE_LOG(Level, Msg) \
+  RAW_LOG(Level, Msg);
+
+namespace forwarder2 {
+
+// Note that the two following functions are not signal-safe.
+
+// Chromium logging-aware implementation of libc's perror().
+void PError(const char* msg);
+
+// Closes the provided file descriptor and logs an error if it failed.
+void CloseFD(int fd);
+
+// Helps build a formatted C-string allocated in a fixed-size array. This is
+// useful in signal handlers where base::StringPrintf() can't be used safely
+// (due to its use of LOG()).
+template <int BufferSize>
+class FixedSizeStringBuilder {
+ public:
+  FixedSizeStringBuilder() {
+    Reset();
+  }
+
+  const char* buffer() const { return buffer_; }
+
+  void Reset() {
+    buffer_[0] = 0;
+    write_ptr_ = buffer_;
+  }
+
+  // Returns the number of bytes appended to the underlying buffer or -1 if it
+  // failed.
+  int Append(const char* format, ...) PRINTF_FORMAT(/* + 1 for 'this' */ 2, 3) {
+    if (write_ptr_ >= buffer_ + BufferSize)
+      return -1;
+    va_list ap;
+    va_start(ap, format);
+    const int bytes_written = vsnprintf(
+        write_ptr_, BufferSize - (write_ptr_ - buffer_), format, ap);
+    va_end(ap);
+    if (bytes_written > 0)
+      write_ptr_ += bytes_written;
+    return bytes_written;
+  }
+
+ private:
+  char* write_ptr_;
+  char buffer_[BufferSize];
+
+  COMPILE_ASSERT(BufferSize >= 1, Size_of_buffer_must_be_at_least_one);
+  DISALLOW_COPY_AND_ASSIGN(FixedSizeStringBuilder);
+};
+
+}  // namespace forwarder2
+
+#endif  // TOOLS_ANDROID_FORWARDER2_COMMON_H_
diff --git a/tools/android/forwarder2/daemon.cc b/tools/android/forwarder2/daemon.cc
new file mode 100644
index 0000000..19a1054
--- /dev/null
+++ b/tools/android/forwarder2/daemon.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/daemon.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/safe_strerror_posix.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "tools/android/forwarder2/common.h"
+#include "tools/android/forwarder2/socket.h"
+
+namespace forwarder2 {
+namespace {
+
+const int kBufferSize = 256;
+
+// Timeout constant used for polling when connecting to the daemon's Unix Domain
+// Socket and also when waiting for its death when it is killed.
+const int kNumTries = 100;
+const int kIdleTimeMSec = 20;
+
+void InitLoggingForDaemon(const std::string& log_file) {
+  logging::LoggingSettings settings;
+  settings.logging_dest =
+      log_file.empty() ?
+      logging::LOG_TO_SYSTEM_DEBUG_LOG : logging::LOG_TO_FILE;
+  settings.log_file = log_file.c_str();
+  settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+  CHECK(logging::InitLogging(settings));
+}
+
+bool RunServerAcceptLoop(const std::string& welcome_message,
+                         Socket* server_socket,
+                         Daemon::ServerDelegate* server_delegate) {
+  bool failed = false;
+  for (;;) {
+    scoped_ptr<Socket> client_socket(new Socket());
+    if (!server_socket->Accept(client_socket.get())) {
+      if (server_socket->DidReceiveEvent())
+        break;
+      PError("Accept()");
+      failed = true;
+      break;
+    }
+    if (!client_socket->Write(welcome_message.c_str(),
+                              welcome_message.length() + 1)) {
+      PError("Write()");
+      failed = true;
+      continue;
+    }
+    server_delegate->OnClientConnected(client_socket.Pass());
+  }
+  return !failed;
+}
+
+void SigChildHandler(int signal_number) {
+  DCHECK_EQ(signal_number, SIGCHLD);
+  int status;
+  pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
+  if (child_pid < 0) {
+    PError("waitpid");
+    return;
+  }
+  if (child_pid == 0)
+    return;
+  if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+    return;
+  // Avoid using StringAppendF() since it's unsafe in a signal handler due to
+  // its use of LOG().
+  FixedSizeStringBuilder<256> string_builder;
+  string_builder.Append("Daemon (pid=%d) died unexpectedly with ", child_pid);
+  if (WIFEXITED(status))
+    string_builder.Append("status %d.", WEXITSTATUS(status));
+  else if (WIFSIGNALED(status))
+    string_builder.Append("signal %d.", WTERMSIG(status));
+  else
+    string_builder.Append("unknown reason.");
+  SIGNAL_SAFE_LOG(ERROR, string_builder.buffer());
+}
+
+// Note that 0 is written to |lock_owner_pid| in case the file is not locked.
+bool GetFileLockOwnerPid(int fd, pid_t* lock_owner_pid) {
+  struct flock lock_info = {};
+  lock_info.l_type = F_WRLCK;
+  lock_info.l_whence = SEEK_CUR;
+  const int ret = HANDLE_EINTR(fcntl(fd, F_GETLK, &lock_info));
+  if (ret < 0) {
+    if (errno == EBADF) {
+      // Assume that the provided file descriptor corresponding to the PID file
+      // was valid until the daemon removed this file.
+      *lock_owner_pid = 0;
+      return true;
+    }
+    PError("fcntl");
+    return false;
+  }
+  if (lock_info.l_type == F_UNLCK) {
+    *lock_owner_pid = 0;
+    return true;
+  }
+  CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type);
+  *lock_owner_pid = lock_info.l_pid;
+  return true;
+}
+
+scoped_ptr<Socket> ConnectToUnixDomainSocket(
+    const std::string& socket_name,
+    int tries_count,
+    int idle_time_msec,
+    const std::string& expected_welcome_message) {
+  for (int i = 0; i < tries_count; ++i) {
+    scoped_ptr<Socket> socket(new Socket());
+    if (!socket->ConnectUnix(socket_name)) {
+      if (idle_time_msec)
+        usleep(idle_time_msec * 1000);
+      continue;
+    }
+    char buf[kBufferSize];
+    DCHECK(expected_welcome_message.length() + 1 <= sizeof(buf));
+    memset(buf, 0, sizeof(buf));
+    if (socket->Read(buf, expected_welcome_message.length() + 1) < 0) {
+      perror("read");
+      continue;
+    }
+    if (expected_welcome_message != buf) {
+      LOG(ERROR) << "Unexpected message read from daemon: " << buf;
+      break;
+    }
+    return socket.Pass();
+  }
+  return scoped_ptr<Socket>();
+}
+
+}  // namespace
+
+Daemon::Daemon(const std::string& log_file_path,
+               const std::string& identifier,
+               ClientDelegate* client_delegate,
+               ServerDelegate* server_delegate,
+               GetExitNotifierFDCallback get_exit_fd_callback)
+  : log_file_path_(log_file_path),
+    identifier_(identifier),
+    client_delegate_(client_delegate),
+    server_delegate_(server_delegate),
+    get_exit_fd_callback_(get_exit_fd_callback) {
+  DCHECK(client_delegate_);
+  DCHECK(server_delegate_);
+  DCHECK(get_exit_fd_callback_);
+}
+
+Daemon::~Daemon() {}
+
+bool Daemon::SpawnIfNeeded() {
+  const int kSingleTry = 1;
+  const int kNoIdleTime = 0;
+  scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
+      identifier_, kSingleTry, kNoIdleTime, identifier_);
+  if (!client_socket) {
+    switch (fork()) {
+      case -1:
+        PError("fork()");
+        return false;
+      // Child.
+      case 0: {
+        if (setsid() < 0) {  // Detach the child process from its parent.
+          PError("setsid()");
+          exit(1);
+        }
+        InitLoggingForDaemon(log_file_path_);
+        CloseFD(STDIN_FILENO);
+        CloseFD(STDOUT_FILENO);
+        CloseFD(STDERR_FILENO);
+        const int null_fd = open("/dev/null", O_RDWR);
+        CHECK_EQ(null_fd, STDIN_FILENO);
+        CHECK_EQ(dup(null_fd), STDOUT_FILENO);
+        CHECK_EQ(dup(null_fd), STDERR_FILENO);
+        Socket command_socket;
+        if (!command_socket.BindUnix(identifier_)) {
+          scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
+              identifier_, kSingleTry, kNoIdleTime, identifier_);
+          if (client_socket.get()) {
+            // The daemon was spawned by a concurrent process.
+            exit(0);
+          }
+          PError("bind()");
+          exit(1);
+        }
+        server_delegate_->Init();
+        command_socket.AddEventFd(get_exit_fd_callback_());
+        return RunServerAcceptLoop(
+            identifier_, &command_socket, server_delegate_);
+      }
+      default:
+        break;
+    }
+  }
+  // Parent.
+  // Install the custom SIGCHLD handler.
+  sigset_t blocked_signals_set;
+  if (sigprocmask(0 /* first arg ignored */, NULL, &blocked_signals_set) < 0) {
+    PError("sigprocmask()");
+    return false;
+  }
+  struct sigaction old_action;
+  struct sigaction new_action;
+  memset(&new_action, 0, sizeof(new_action));
+  new_action.sa_handler = SigChildHandler;
+  new_action.sa_flags = SA_NOCLDSTOP;
+  sigemptyset(&new_action.sa_mask);
+  if (sigaction(SIGCHLD, &new_action, &old_action) < 0) {
+    PError("sigaction()");
+    return false;
+  }
+  // Connect to the daemon's Unix Domain Socket.
+  bool failed = false;
+  if (!client_socket) {
+    client_socket = ConnectToUnixDomainSocket(
+        identifier_, kNumTries, kIdleTimeMSec, identifier_);
+    if (!client_socket) {
+      LOG(ERROR) << "Could not connect to daemon's Unix Daemon socket";
+      failed = true;
+    }
+  }
+  if (!failed)
+    client_delegate_->OnDaemonReady(client_socket.get());
+  // Restore the previous signal action for SIGCHLD.
+  if (sigaction(SIGCHLD, &old_action, NULL) < 0) {
+    PError("sigaction");
+    failed = true;
+  }
+  return !failed;
+}
+
+bool Daemon::Kill() {
+  pid_t daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
+  if (daemon_pid < 0) {
+    LOG(ERROR) << "No forwarder daemon seems to be running";
+    return true;
+  }
+  if (kill(daemon_pid, SIGTERM) < 0) {
+    if (errno == ESRCH /* invalid PID */) {
+      // The daemon exited for some reason (e.g. kill by a process other than
+      // us) right before the call to kill() above.
+      LOG(ERROR) << "Could not kill daemon with PID " << daemon_pid;
+      return true;
+    }
+    PError("kill");
+    return false;
+  }
+  for (int i = 0; i < kNumTries; ++i) {
+    const pid_t previous_pid = daemon_pid;
+    daemon_pid = Socket::GetUnixDomainSocketProcessOwner(identifier_);
+    if (daemon_pid < 0)
+      return true;
+    // Since we are polling we might not see the 'daemon exited' event if
+    // another daemon was spawned during our idle period.
+    if (daemon_pid != previous_pid) {
+      LOG(WARNING) << "Daemon (pid=" << previous_pid
+                   << ") was successfully killed but a new daemon (pid="
+                   << daemon_pid << ") seems to be running now.";
+      return true;
+    }
+    usleep(kIdleTimeMSec * 1000);
+  }
+  LOG(ERROR) << "Timed out while killing daemon. "
+                "It might still be tearing down.";
+  return false;
+}
+
+}  // namespace forwarder2
diff --git a/tools/android/forwarder2/daemon.h b/tools/android/forwarder2/daemon.h
new file mode 100644
index 0000000..4b05ea4
--- /dev/null
+++ b/tools/android/forwarder2/daemon.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_DAEMON_H_
+#define TOOLS_ANDROID_FORWARDER2_DAEMON_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace forwarder2 {
+
+class Socket;
+
+// Provides a way to spawn a daemon and communicate with it.
+class Daemon {
+ public:
+  // Callback used by the daemon to shutdown properly. See pipe_notifier.h for
+  // more details.
+  typedef int (*GetExitNotifierFDCallback)();
+
+  class ClientDelegate {
+   public:
+    virtual ~ClientDelegate() {}
+
+    // Called after the daemon is ready to receive commands.
+    virtual void OnDaemonReady(Socket* daemon_socket) = 0;
+  };
+
+  class ServerDelegate {
+   public:
+    virtual ~ServerDelegate() {}
+
+    // Called after the daemon bound its Unix Domain Socket. This can be used to
+    // setup signal handlers or perform global initialization.
+    virtual void Init() = 0;
+
+    virtual void OnClientConnected(scoped_ptr<Socket> client_socket) = 0;
+  };
+
+  // |identifier| should be a unique string identifier. It is used to
+  // bind/connect the underlying Unix Domain Socket.
+  // Note that this class does not take ownership of |client_delegate| and
+  // |server_delegate|.
+  Daemon(const std::string& log_file_path,
+         const std::string& identifier,
+         ClientDelegate* client_delegate,
+         ServerDelegate* server_delegate,
+         GetExitNotifierFDCallback get_exit_fd_callback);
+
+  ~Daemon();
+
+  // Returns whether the daemon was successfully spawned. Note that this does
+  // not necessarily mean that the current process was forked in case the daemon
+  // is already running.
+  bool SpawnIfNeeded();
+
+  // Kills the daemon and blocks until it exited. Returns whether it succeeded.
+  bool Kill();
+
+ private:
+  const std::string log_file_path_;
+  const std::string identifier_;
+  ClientDelegate* const client_delegate_;
+  ServerDelegate* const server_delegate_;
+  const GetExitNotifierFDCallback get_exit_fd_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(Daemon);
+};
+
+}  // namespace forwarder2
+
+#endif  // TOOLS_ANDROID_FORWARDER2_DAEMON_H_
diff --git a/tools/android/forwarder2/device_controller.cc b/tools/android/forwarder2/device_controller.cc
new file mode 100644
index 0000000..a4cb9c7
--- /dev/null
+++ b/tools/android/forwarder2/device_controller.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/device_controller.h"
+
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+#include "tools/android/forwarder2/command.h"
+#include "tools/android/forwarder2/device_listener.h"
+#include "tools/android/forwarder2/socket.h"
+#include "tools/android/forwarder2/util.h"
+
+namespace forwarder2 {
+
+// static
+scoped_ptr<DeviceController> DeviceController::Create(
+    const std::string& adb_unix_socket,
+    int exit_notifier_fd) {
+  scoped_ptr<DeviceController> device_controller;
+  scoped_ptr<Socket> host_socket(new Socket());
+  if (!host_socket->BindUnix(adb_unix_socket)) {
+    PLOG(ERROR) << "Could not BindAndListen DeviceController socket on port "
+                << adb_unix_socket << ": ";
+    return device_controller.Pass();
+  }
+  LOG(INFO) << "Listening on Unix Domain Socket " << adb_unix_socket;
+  device_controller.reset(
+      new DeviceController(host_socket.Pass(), exit_notifier_fd));
+  return device_controller.Pass();
+}
+
+DeviceController::~DeviceController() {
+  DCHECK(construction_task_runner_->RunsTasksOnCurrentThread());
+}
+
+void DeviceController::Start() {
+  AcceptHostCommandSoon();
+}
+
+DeviceController::DeviceController(scoped_ptr<Socket> host_socket,
+                                   int exit_notifier_fd)
+    : host_socket_(host_socket.Pass()),
+      exit_notifier_fd_(exit_notifier_fd),
+      construction_task_runner_(base::MessageLoopProxy::current()),
+      weak_ptr_factory_(this) {
+  host_socket_->AddEventFd(exit_notifier_fd);
+}
+
+void DeviceController::AcceptHostCommandSoon() {
+  base::MessageLoopProxy::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&DeviceController::AcceptHostCommandInternal,
+                 base::Unretained(this)));
+}
+
+void DeviceController::AcceptHostCommandInternal() {
+  scoped_ptr<Socket> socket(new Socket);
+  if (!host_socket_->Accept(socket.get())) {
+    if (!host_socket_->DidReceiveEvent())
+      PLOG(ERROR) << "Could not Accept DeviceController socket";
+    else
+      LOG(INFO) << "Received exit notification";
+    return;
+  }
+  base::ScopedClosureRunner accept_next_client(
+      base::Bind(&DeviceController::AcceptHostCommandSoon,
+                 base::Unretained(this)));
+  // So that |socket| doesn't block on read if it has notifications.
+  socket->AddEventFd(exit_notifier_fd_);
+  int port;
+  command::Type command;
+  if (!ReadCommand(socket.get(), &port, &command)) {
+    LOG(ERROR) << "Invalid command received.";
+    return;
+  }
+  const ListenersMap::iterator listener_it = listeners_.find(port);
+  DeviceListener* const listener = listener_it == listeners_.end()
+      ? static_cast<DeviceListener*>(NULL) : listener_it->second.get();
+  switch (command) {
+    case command::LISTEN: {
+      if (listener != NULL) {
+        LOG(WARNING) << "Already forwarding port " << port
+                     << ". Attempting to restart the listener.\n";
+        DeleteRefCountedValueInMapFromIterator(listener_it, &listeners_);
+      }
+      scoped_ptr<DeviceListener> new_listener(
+          DeviceListener::Create(
+              socket.Pass(), port,
+              base::Bind(&DeviceController::DeleteListenerOnError,
+                         weak_ptr_factory_.GetWeakPtr())));
+      if (!new_listener)
+        return;
+      new_listener->Start();
+      // |port| can be zero, to allow dynamically allocated port, so instead, we
+      // call DeviceListener::listener_port() to retrieve the currently
+      // allocated port to this new listener.
+      const int listener_port = new_listener->listener_port();
+      listeners_.insert(
+          std::make_pair(listener_port,
+                         linked_ptr<DeviceListener>(new_listener.release())));
+      LOG(INFO) << "Forwarding device port " << listener_port << " to host.";
+      break;
+    }
+    case command::DATA_CONNECTION:
+      if (listener == NULL) {
+        LOG(ERROR) << "Data Connection command received, but "
+                   << "listener has not been set up yet for port " << port;
+        // After this point it is assumed that, once we close our Adb Data
+        // socket, the Adb forwarder command will propagate the closing of
+        // sockets all the way to the host side.
+        break;
+      }
+      listener->SetAdbDataSocket(socket.Pass());
+      break;
+    case command::UNLISTEN:
+      LOG(INFO) << "Unmapping port " << port;
+      if (!listener) {
+        LOG(ERROR) << "No listener found for port " << port;
+        SendCommand(command::UNLISTEN_ERROR, port, socket.get());
+        break;
+      }
+      DeleteRefCountedValueInMapFromIterator(listener_it, &listeners_);
+      SendCommand(command::UNLISTEN_SUCCESS, port, socket.get());
+      break;
+    default:
+      // TODO(felipeg): add a KillAllListeners command.
+      LOG(ERROR) << "Invalid command received. Port: " << port
+                 << " Command: " << command;
+  }
+}
+
+// static
+void DeviceController::DeleteListenerOnError(
+      const base::WeakPtr<DeviceController>& device_controller_ptr,
+      scoped_ptr<DeviceListener> device_listener) {
+  DeviceListener* const listener = device_listener.release();
+  DeviceController* const controller = device_controller_ptr.get();
+  if (!controller) {
+    // |listener| was already deleted by the controller that did have its
+    // ownership.
+    return;
+  }
+  DCHECK(controller->construction_task_runner_->RunsTasksOnCurrentThread());
+  bool listener_did_exist = DeleteRefCountedValueInMap(
+      listener->listener_port(), &controller->listeners_);
+  DCHECK(listener_did_exist);
+  // Note that |listener| was deleted by DeleteRefCountedValueInMap().
+}
+
+}  // namespace forwarder
diff --git a/tools/android/forwarder2/device_controller.h b/tools/android/forwarder2/device_controller.h
new file mode 100644
index 0000000..567a08d
--- /dev/null
+++ b/tools/android/forwarder2/device_controller.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_DEVICE_CONTROLLER_H_
+#define TOOLS_ANDROID_FORWARDER2_DEVICE_CONTROLLER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "tools/android/forwarder2/socket.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace forwarder2 {
+
+class DeviceListener;
+
+// There is a single DeviceController per device_forwarder process, and it is in
+// charge of managing all active redirections on the device side (one
+// DeviceListener each).
+class DeviceController {
+ public:
+  static scoped_ptr<DeviceController> Create(const std::string& adb_unix_socket,
+                                             int exit_notifier_fd);
+  ~DeviceController();
+
+  void Start();
+
+ private:
+  typedef base::hash_map<
+      int /* port */, linked_ptr<DeviceListener> > ListenersMap;
+
+  DeviceController(scoped_ptr<Socket> host_socket, int exit_notifier_fd);
+
+  void AcceptHostCommandSoon();
+  void AcceptHostCommandInternal();
+
+  // Note that this can end up being called after the DeviceController is
+  // destroyed which is why a weak pointer is used.
+  static void DeleteListenerOnError(
+      const base::WeakPtr<DeviceController>& device_controller_ptr,
+      scoped_ptr<DeviceListener> device_listener);
+
+  const scoped_ptr<Socket> host_socket_;
+  // Used to notify the controller to exit.
+  const int exit_notifier_fd_;
+  // Lets ensure DeviceListener instances are deleted on the thread they were
+  // created on.
+  const scoped_refptr<base::SingleThreadTaskRunner> construction_task_runner_;
+  ListenersMap listeners_;
+
+  //WeakPtrFactory's documentation says:
+  // 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.
+  base::WeakPtrFactory<DeviceController> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceController);
+};
+
+}  // namespace forwarder
+
+#endif  // TOOLS_ANDROID_FORWARDER2_DEVICE_CONTROLLER_H_
diff --git a/tools/android/forwarder2/device_forwarder_main.cc b/tools/android/forwarder2/device_forwarder_main.cc
new file mode 100644
index 0000000..cad46f4
--- /dev/null
+++ b/tools/android/forwarder2/device_forwarder_main.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <signal.h>
+#include <stdlib.h>
+
+#include <iostream>
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread.h"
+#include "tools/android/forwarder2/common.h"
+#include "tools/android/forwarder2/daemon.h"
+#include "tools/android/forwarder2/device_controller.h"
+#include "tools/android/forwarder2/pipe_notifier.h"
+
+namespace forwarder2 {
+namespace {
+
+// Leaky global instance, accessed from the signal handler.
+forwarder2::PipeNotifier* g_notifier = NULL;
+
+const int kBufSize = 256;
+
+const char kUnixDomainSocketPath[] = "chrome_device_forwarder";
+const char kDaemonIdentifier[] = "chrome_device_forwarder_daemon";
+
+void KillHandler(int /* unused */) {
+  CHECK(g_notifier);
+  if (!g_notifier->Notify())
+    exit(1);
+}
+
+// Lets the daemon fetch the exit notifier file descriptor.
+int GetExitNotifierFD() {
+  DCHECK(g_notifier);
+  return g_notifier->receiver_fd();
+}
+
+class ServerDelegate : public Daemon::ServerDelegate {
+ public:
+  ServerDelegate() : initialized_(false) {}
+
+  virtual ~ServerDelegate() {
+    if (!controller_thread_.get())
+      return;
+    // The DeviceController instance, if any, is constructed on the controller
+    // thread. Make sure that it gets deleted on that same thread. Note that
+    // DeleteSoon() is not used here since it would imply reading |controller_|
+    // from the main thread while it's set on the internal thread.
+    controller_thread_->message_loop_proxy()->PostTask(
+        FROM_HERE,
+        base::Bind(&ServerDelegate::DeleteControllerOnInternalThread,
+                   base::Unretained(this)));
+  }
+
+  void DeleteControllerOnInternalThread() {
+    DCHECK(
+        controller_thread_->message_loop_proxy()->RunsTasksOnCurrentThread());
+    controller_.reset();
+  }
+
+  // Daemon::ServerDelegate:
+  virtual void Init() OVERRIDE {
+    DCHECK(!g_notifier);
+    g_notifier = new forwarder2::PipeNotifier();
+    signal(SIGTERM, KillHandler);
+    signal(SIGINT, KillHandler);
+    controller_thread_.reset(new base::Thread("controller_thread"));
+    controller_thread_->Start();
+  }
+
+  virtual void OnClientConnected(scoped_ptr<Socket> client_socket) OVERRIDE {
+    if (initialized_) {
+      client_socket->WriteString("OK");
+      return;
+    }
+    controller_thread_->message_loop()->PostTask(
+        FROM_HERE,
+        base::Bind(&ServerDelegate::StartController, base::Unretained(this),
+                   GetExitNotifierFD(), base::Passed(&client_socket)));
+    initialized_ = true;
+  }
+
+ private:
+  void StartController(int exit_notifier_fd, scoped_ptr<Socket> client_socket) {
+    DCHECK(!controller_.get());
+    scoped_ptr<DeviceController> controller(
+        DeviceController::Create(kUnixDomainSocketPath, exit_notifier_fd));
+    if (!controller.get()) {
+      client_socket->WriteString(
+          base::StringPrintf("ERROR: Could not initialize device controller "
+                             "with ADB socket path: %s",
+                             kUnixDomainSocketPath));
+      return;
+    }
+    controller_.swap(controller);
+    controller_->Start();
+    client_socket->WriteString("OK");
+    client_socket->Close();
+  }
+
+  scoped_ptr<DeviceController> controller_;
+  scoped_ptr<base::Thread> controller_thread_;
+  bool initialized_;
+};
+
+class ClientDelegate : public Daemon::ClientDelegate {
+ public:
+  ClientDelegate() : has_failed_(false) {}
+
+  bool has_failed() const { return has_failed_; }
+
+  // Daemon::ClientDelegate:
+  virtual void OnDaemonReady(Socket* daemon_socket) OVERRIDE {
+    char buf[kBufSize];
+    const int bytes_read = daemon_socket->Read(
+        buf, sizeof(buf) - 1 /* leave space for null terminator */);
+    CHECK_GT(bytes_read, 0);
+    DCHECK(bytes_read < sizeof(buf));
+    buf[bytes_read] = 0;
+    base::StringPiece msg(buf, bytes_read);
+    if (msg.starts_with("ERROR")) {
+      LOG(ERROR) << msg;
+      has_failed_ = true;
+      return;
+    }
+  }
+
+ private:
+  bool has_failed_;
+};
+
+int RunDeviceForwarder(int argc, char** argv) {
+  CommandLine::Init(argc, argv);  // Needed by logging.
+  const bool kill_server = CommandLine::ForCurrentProcess()->HasSwitch(
+      "kill-server");
+  if ((kill_server && argc != 2) || (!kill_server && argc != 1)) {
+    std::cerr << "Usage: device_forwarder [--kill-server]" << std::endl;
+    return 1;
+  }
+  base::AtExitManager at_exit_manager;  // Used by base::Thread.
+  ClientDelegate client_delegate;
+  ServerDelegate daemon_delegate;
+  const char kLogFilePath[] = "";  // Log to logcat.
+  Daemon daemon(kLogFilePath, kDaemonIdentifier, &client_delegate,
+                &daemon_delegate, &GetExitNotifierFD);
+
+  if (kill_server)
+    return !daemon.Kill();
+
+  if (!daemon.SpawnIfNeeded())
+    return 1;
+  return client_delegate.has_failed();
+}
+
+}  // namespace
+}  // namespace forwarder2
+
+int main(int argc, char** argv) {
+  return forwarder2::RunDeviceForwarder(argc, argv);
+}
diff --git a/tools/android/forwarder2/device_listener.cc b/tools/android/forwarder2/device_listener.cc
new file mode 100644
index 0000000..b48a746
--- /dev/null
+++ b/tools/android/forwarder2/device_listener.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/device_listener.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+#include "tools/android/forwarder2/command.h"
+#include "tools/android/forwarder2/forwarder.h"
+#include "tools/android/forwarder2/socket.h"
+
+namespace forwarder2 {
+
+// static
+scoped_ptr<DeviceListener> DeviceListener::Create(
+    scoped_ptr<Socket> host_socket,
+    int listener_port,
+    const ErrorCallback& error_callback) {
+  scoped_ptr<Socket> listener_socket(new Socket());
+  scoped_ptr<DeviceListener> device_listener;
+  if (!listener_socket->BindTcp("", listener_port)) {
+    LOG(ERROR) << "Device could not bind and listen to local port "
+               << listener_port;
+    SendCommand(command::BIND_ERROR, listener_port, host_socket.get());
+    return device_listener.Pass();
+  }
+  // In case the |listener_port_| was zero, GetPort() will return the
+  // currently (non-zero) allocated port for this socket.
+  listener_port = listener_socket->GetPort();
+  SendCommand(command::BIND_SUCCESS, listener_port, host_socket.get());
+  device_listener.reset(
+      new DeviceListener(listener_socket.Pass(), host_socket.Pass(),
+                         listener_port, error_callback));
+  return device_listener.Pass();
+}
+
+DeviceListener::~DeviceListener() {
+  DCHECK(deletion_task_runner_->RunsTasksOnCurrentThread());
+  deletion_notifier_.Notify();
+}
+
+void DeviceListener::Start() {
+  thread_.Start();
+  AcceptNextClientSoon();
+}
+
+void DeviceListener::SetAdbDataSocket(scoped_ptr<Socket> adb_data_socket) {
+  thread_.message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(&DeviceListener::OnAdbDataSocketReceivedOnInternalThread,
+                 base::Unretained(this), base::Passed(&adb_data_socket)));
+}
+
+DeviceListener::DeviceListener(scoped_ptr<Socket> listener_socket,
+                               scoped_ptr<Socket> host_socket,
+                               int port,
+                               const ErrorCallback& error_callback)
+    : self_deleter_helper_(this, error_callback),
+      listener_socket_(listener_socket.Pass()),
+      host_socket_(host_socket.Pass()),
+      listener_port_(port),
+      deletion_task_runner_(base::MessageLoopProxy::current()),
+      thread_("DeviceListener") {
+  CHECK(host_socket_.get());
+  DCHECK(deletion_task_runner_.get());
+  host_socket_->AddEventFd(deletion_notifier_.receiver_fd());
+  listener_socket_->AddEventFd(deletion_notifier_.receiver_fd());
+}
+
+void DeviceListener::AcceptNextClientSoon() {
+  thread_.message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(&DeviceListener::AcceptClientOnInternalThread,
+                 base::Unretained(this)));
+}
+
+void DeviceListener::AcceptClientOnInternalThread() {
+  device_data_socket_.reset(new Socket());
+  if (!listener_socket_->Accept(device_data_socket_.get())) {
+    if (listener_socket_->DidReceiveEvent()) {
+      LOG(INFO) << "Received exit notification, stopped accepting clients.";
+      OnInternalThreadError();
+      return;
+    }
+    LOG(WARNING) << "Could not Accept in ListenerSocket.";
+    SendCommand(command::ACCEPT_ERROR, listener_port_, host_socket_.get());
+    OnInternalThreadError();
+    return;
+  }
+  SendCommand(command::ACCEPT_SUCCESS, listener_port_, host_socket_.get());
+  if (!ReceivedCommand(command::HOST_SERVER_SUCCESS,
+                       host_socket_.get())) {
+    SendCommand(command::ACK, listener_port_, host_socket_.get());
+    LOG(ERROR) << "Host could not connect to server.";
+    device_data_socket_->Close();
+    if (host_socket_->has_error()) {
+      LOG(ERROR) << "Adb Control connection lost. "
+                 << "Listener port: " << listener_port_;
+      OnInternalThreadError();
+      return;
+    }
+    // It can continue if the host forwarder could not connect to the host
+    // server but the control connection is still alive (no errors). The device
+    // acknowledged that (above), and it can re-try later.
+    AcceptNextClientSoon();
+    return;
+  }
+}
+
+void DeviceListener::OnAdbDataSocketReceivedOnInternalThread(
+    scoped_ptr<Socket> adb_data_socket) {
+  DCHECK(adb_data_socket);
+  SendCommand(command::ADB_DATA_SOCKET_SUCCESS, listener_port_,
+              host_socket_.get());
+  forwarders_manager_.CreateAndStartNewForwarder(
+      device_data_socket_.Pass(), adb_data_socket.Pass());
+  AcceptNextClientSoon();
+}
+
+void DeviceListener::OnInternalThreadError() {
+  self_deleter_helper_.MaybeSelfDeleteSoon();
+}
+
+}  // namespace forwarder
diff --git a/tools/android/forwarder2/device_listener.h b/tools/android/forwarder2/device_listener.h
new file mode 100644
index 0000000..c7724f4
--- /dev/null
+++ b/tools/android/forwarder2/device_listener.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_DEVICE_LISTENER_H_
+#define TOOLS_ANDROID_FORWARDER2_DEVICE_LISTENER_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "tools/android/forwarder2/forwarders_manager.h"
+#include "tools/android/forwarder2/pipe_notifier.h"
+#include "tools/android/forwarder2/self_deleter_helper.h"
+#include "tools/android/forwarder2/socket.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace forwarder2 {
+
+class Forwarder;
+
+// A DeviceListener instance is used in the device_forwarder program to bind to
+// a specific device-side |port| and wait for client connections. When a
+// connection happens, it informs the corresponding HostController instance
+// running on the host, through |host_socket|. Then the class expects a call to
+// its SetAdbDataSocket() method (performed by the device controller) once the
+// host opened a new connection to the device. When this happens, a new internal
+// Forwarder instance is started.
+// Note that instances of this class are owned by the device controller which
+// creates and destroys them on the same thread. In case an internal error
+// happens on the DeviceListener's internal thread, the DeviceListener
+// can also self-delete by executing the user-provided callback on the thread
+// the DeviceListener was created on.
+// Note that the DeviceListener's destructor joins its internal thread (i.e.
+// waits for its completion) which means that the internal thread is guaranteed
+// not to be running anymore once the object is deleted.
+class DeviceListener {
+ public:
+  // Callback that is used for self-deletion on error to let the device
+  // controller perform some additional cleanup work (e.g. removing the device
+  // listener instance from its internal map before deleting it).
+  typedef base::Callback<void (scoped_ptr<DeviceListener>)> ErrorCallback;
+
+  static scoped_ptr<DeviceListener> Create(scoped_ptr<Socket> host_socket,
+                                           int port,
+                                           const ErrorCallback& error_callback);
+
+  ~DeviceListener();
+
+  void Start();
+
+  void SetAdbDataSocket(scoped_ptr<Socket> adb_data_socket);
+
+  int listener_port() const { return listener_port_; }
+
+ private:
+  DeviceListener(scoped_ptr<Socket> listener_socket,
+                 scoped_ptr<Socket> host_socket,
+                 int port,
+                 const ErrorCallback& error_callback);
+
+  // Pushes an AcceptClientOnInternalThread() task to the internal thread's
+  // message queue in order to wait for a new client soon.
+  void AcceptNextClientSoon();
+
+  void AcceptClientOnInternalThread();
+
+  void OnAdbDataSocketReceivedOnInternalThread(
+      scoped_ptr<Socket> adb_data_socket);
+
+  void OnInternalThreadError();
+
+  SelfDeleterHelper<DeviceListener> self_deleter_helper_;
+  // Used for the listener thread to be notified on destruction. We have one
+  // notifier per Listener thread since each Listener thread may be requested to
+  // exit for different reasons independently from each other and independent
+  // from the main program, ex. when the host requests to forward/listen the
+  // same port again.  Both the |host_socket_| and |listener_socket_| must share
+  // the same receiver file descriptor from |deletion_notifier_| and it is set
+  // in the constructor.
+  PipeNotifier deletion_notifier_;
+  // The local device listener socket for accepting connections from the local
+  // port (listener_port_).
+  const scoped_ptr<Socket> listener_socket_;
+  // The listener socket for sending control commands.
+  const scoped_ptr<Socket> host_socket_;
+  scoped_ptr<Socket> device_data_socket_;
+  const int listener_port_;
+  // Task runner used for deletion set at construction time (i.e. the object is
+  // deleted on the same thread it is created on).
+  scoped_refptr<base::SingleThreadTaskRunner> deletion_task_runner_;
+  base::Thread thread_;
+  ForwardersManager forwarders_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceListener);
+};
+
+}  // namespace forwarder
+
+#endif  // TOOLS_ANDROID_FORWARDER2_DEVICE_LISTENER_H_
diff --git a/tools/android/forwarder2/forwarder.cc b/tools/android/forwarder2/forwarder.cc
new file mode 100644
index 0000000..1e0bcd0
--- /dev/null
+++ b/tools/android/forwarder2/forwarder.cc
@@ -0,0 +1,255 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/forwarder.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "tools/android/forwarder2/socket.h"
+
+namespace forwarder2 {
+namespace {
+
+const int kBufferSize = 32 * 1024;
+
+}  // namespace
+
+
+// Helper class to buffer reads and writes from one socket to another.
+// Each implements a small buffer connected two one input socket, and
+// one output socket.
+//
+//   socket_from_ ---> [BufferedCopier] ---> socket_to_
+//
+// These objects are used in a pair to handle duplex traffic, as in:
+//
+//                    ------> [BufferedCopier_1] --->
+//                  /                                \
+//      socket_1   *                                  * socket_2
+//                  \                                /
+//                   <------ [BufferedCopier_2] <----
+//
+// When a BufferedCopier is in the READING state (see below), it only listens
+// to events on its input socket, and won't detect when its output socket
+// disconnects. To work around this, its peer will call its Close() method
+// when that happens.
+
+class Forwarder::BufferedCopier {
+ public:
+  // Possible states:
+  //    READING - Empty buffer and Waiting for input.
+  //    WRITING - Data in buffer, and waiting for output.
+  //    CLOSING - Like WRITING, but do not try to read after that.
+  //    CLOSED  - Completely closed.
+  //
+  // State transitions are:
+  //
+  //   T01:  READING ---[receive data]---> WRITING
+  //   T02:  READING ---[error on input socket]---> CLOSED
+  //   T03:  READING ---[Close() call]---> CLOSED
+  //
+  //   T04:  WRITING ---[write partial data]---> WRITING
+  //   T05:  WRITING ---[write all data]----> READING
+  //   T06:  WRITING ---[error on output socket]----> CLOSED
+  //   T07:  WRITING ---[Close() call]---> CLOSING
+  //
+  //   T08:  CLOSING ---[write partial data]---> CLOSING
+  //   T09:  CLOSING ---[write all data]----> CLOSED
+  //   T10:  CLOSING ---[Close() call]---> CLOSING
+  //   T11:  CLOSING ---[error on output socket] ---> CLOSED
+  //
+  enum State {
+    STATE_READING = 0,
+    STATE_WRITING = 1,
+    STATE_CLOSING = 2,
+    STATE_CLOSED = 3,
+  };
+
+  // Does NOT own the pointers.
+  BufferedCopier(Socket* socket_from, Socket* socket_to)
+      : socket_from_(socket_from),
+        socket_to_(socket_to),
+        bytes_read_(0),
+        write_offset_(0),
+        peer_(NULL),
+        state_(STATE_READING) {}
+
+  // Sets the 'peer_' field pointing to the other BufferedCopier in a pair.
+  void SetPeer(BufferedCopier* peer) {
+    DCHECK(!peer_);
+    peer_ = peer;
+  }
+
+  bool is_closed() const { return state_ == STATE_CLOSED; }
+
+  // Gently asks to close a buffer. Called either by the peer or the forwarder.
+  void Close() {
+    switch (state_) {
+      case STATE_READING:
+        state_ = STATE_CLOSED;  // T03
+        break;
+      case STATE_WRITING:
+        state_ = STATE_CLOSING;  // T07
+        break;
+      case STATE_CLOSING:
+        break;  // T10
+      case STATE_CLOSED:
+        ;
+    }
+  }
+
+  // Call this before select(). This updates |read_fds|,
+  // |write_fds| and |max_fd| appropriately *if* the buffer isn't closed.
+  void PrepareSelect(fd_set* read_fds, fd_set* write_fds, int* max_fd) {
+    int fd;
+    switch (state_) {
+      case STATE_READING:
+        DCHECK(bytes_read_ == 0);
+        DCHECK(write_offset_ == 0);
+        fd = socket_from_->fd();
+        if (fd < 0) {
+          ForceClose();  // T02
+          return;
+        }
+        FD_SET(fd, read_fds);
+        break;
+
+      case STATE_WRITING:
+      case STATE_CLOSING:
+        DCHECK(bytes_read_ > 0);
+        DCHECK(write_offset_ < bytes_read_);
+        fd = socket_to_->fd();
+        if (fd < 0) {
+          ForceClose();  // T06
+          return;
+        }
+        FD_SET(fd, write_fds);
+        break;
+
+      case STATE_CLOSED:
+        return;
+    }
+    *max_fd = std::max(*max_fd, fd);
+  }
+
+  // Call this after a select() call to operate over the buffer.
+  void ProcessSelect(const fd_set& read_fds, const fd_set& write_fds) {
+    int fd, ret;
+    switch (state_) {
+      case STATE_READING:
+        fd = socket_from_->fd();
+        if (fd < 0) {
+          state_ = STATE_CLOSED;  // T02
+          return;
+        }
+        if (!FD_ISSET(fd, &read_fds))
+          return;
+
+        ret = socket_from_->NonBlockingRead(buffer_, kBufferSize);
+        if (ret <= 0) {
+          ForceClose();  // T02
+          return;
+        }
+        bytes_read_ = ret;
+        write_offset_ = 0;
+        state_ = STATE_WRITING;  // T01
+        break;
+
+      case STATE_WRITING:
+      case STATE_CLOSING:
+        fd = socket_to_->fd();
+        if (fd < 0) {
+          ForceClose();  // T06 + T11
+          return;
+        }
+        if (!FD_ISSET(fd, &write_fds))
+          return;
+
+        ret = socket_to_->NonBlockingWrite(buffer_ + write_offset_,
+                                           bytes_read_ - write_offset_);
+        if (ret <= 0) {
+          ForceClose();  // T06 + T11
+          return;
+        }
+
+        write_offset_ += ret;
+        if (write_offset_ < bytes_read_)
+          return;  // T08 + T04
+
+        write_offset_ = 0;
+        bytes_read_ = 0;
+        if (state_ == STATE_CLOSING) {
+          ForceClose();  // T09
+          return;
+        }
+        state_ = STATE_READING;  // T05
+        break;
+
+      case STATE_CLOSED:
+        ;
+    }
+  }
+
+ private:
+  // Internal method used to close the buffer and notify the peer, if any.
+  void ForceClose() {
+    if (peer_) {
+      peer_->Close();
+      peer_ = NULL;
+    }
+    state_ = STATE_CLOSED;
+  }
+
+  // Not owned.
+  Socket* socket_from_;
+  Socket* socket_to_;
+
+  int bytes_read_;
+  int write_offset_;
+  BufferedCopier* peer_;
+  State state_;
+  char buffer_[kBufferSize];
+
+  DISALLOW_COPY_AND_ASSIGN(BufferedCopier);
+};
+
+Forwarder::Forwarder(scoped_ptr<Socket> socket1,
+                     scoped_ptr<Socket> socket2)
+    : socket1_(socket1.Pass()),
+      socket2_(socket2.Pass()),
+      buffer1_(new BufferedCopier(socket1_.get(), socket2_.get())),
+      buffer2_(new BufferedCopier(socket2_.get(), socket1_.get())) {
+  buffer1_->SetPeer(buffer2_.get());
+  buffer2_->SetPeer(buffer1_.get());
+}
+
+Forwarder::~Forwarder() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void Forwarder::RegisterFDs(fd_set* read_fds, fd_set* write_fds, int* max_fd) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  buffer1_->PrepareSelect(read_fds, write_fds, max_fd);
+  buffer2_->PrepareSelect(read_fds, write_fds, max_fd);
+}
+
+void Forwarder::ProcessEvents(const fd_set& read_fds, const fd_set& write_fds) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  buffer1_->ProcessSelect(read_fds, write_fds);
+  buffer2_->ProcessSelect(read_fds, write_fds);
+}
+
+bool Forwarder::IsClosed() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return buffer1_->is_closed() && buffer2_->is_closed();
+}
+
+void Forwarder::Shutdown() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  buffer1_->Close();
+  buffer2_->Close();
+}
+
+}  // namespace forwarder2
diff --git a/tools/android/forwarder2/forwarder.gyp b/tools/android/forwarder2/forwarder.gyp
new file mode 100644
index 0000000..fbf5eba
--- /dev/null
+++ b/tools/android/forwarder2/forwarder.gyp
@@ -0,0 +1,70 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'forwarder2',
+      'type': 'none',
+      'dependencies': [
+        'device_forwarder',
+        'host_forwarder#host',
+      ],
+      # For the component build, ensure dependent shared libraries are stripped
+      # and put alongside forwarder to simplify pushing to the device.
+      'variables': {
+         'output_dir': '<(PRODUCT_DIR)/forwarder_dist/',
+         'native_binary': '<(PRODUCT_DIR)/device_forwarder',
+      },
+      'includes': ['../../../build/android/native_app_dependencies.gypi'],
+    },
+    {
+      'target_name': 'device_forwarder',
+      'type': 'executable',
+      'toolsets': ['target'],
+      'dependencies': [
+        '../../../base/base.gyp:base',
+        '../common/common.gyp:android_tools_common',
+      ],
+      'include_dirs': [
+        '../../..',
+      ],
+      'sources': [
+        'command.cc',
+        'common.cc',
+        'daemon.cc',
+        'device_controller.cc',
+        'device_forwarder_main.cc',
+        'device_listener.cc',
+        'forwarder.cc',
+        'forwarders_manager.cc',
+        'pipe_notifier.cc',
+        'socket.cc',
+      ],
+    },
+    {
+      'target_name': 'host_forwarder',
+      'type': 'executable',
+      'toolsets': ['host'],
+      'dependencies': [
+        '../../../base/base.gyp:base',
+        '../common/common.gyp:android_tools_common',
+      ],
+      'include_dirs': [
+        '../../..',
+      ],
+      'sources': [
+        'command.cc',
+        'common.cc',
+        'daemon.cc',
+        'forwarder.cc',
+        'forwarders_manager.cc',
+        'host_controller.cc',
+        'host_forwarder_main.cc',
+        'pipe_notifier.cc',
+        'socket.cc',
+      ],
+    },
+  ],
+}
diff --git a/tools/android/forwarder2/forwarder.h b/tools/android/forwarder2/forwarder.h
new file mode 100644
index 0000000..0be86fc
--- /dev/null
+++ b/tools/android/forwarder2/forwarder.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_FORWARDER_H_
+#define TOOLS_ANDROID_FORWARDER2_FORWARDER_H_
+
+#include <sys/select.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+
+namespace forwarder2 {
+
+class Socket;
+
+// Internal class that forwards traffic between |socket1| and |socket2|. Note
+// that this class is not thread-safe.
+class Forwarder {
+ public:
+  Forwarder(scoped_ptr<Socket> socket1, scoped_ptr<Socket> socket2);
+
+  ~Forwarder();
+
+  void RegisterFDs(fd_set* read_fds, fd_set* write_fds, int* max_fd);
+
+  void ProcessEvents(const fd_set& read_fds, const fd_set& write_fds);
+
+  bool IsClosed() const;
+
+  void Shutdown();
+
+ private:
+  class BufferedCopier;
+
+  base::ThreadChecker thread_checker_;
+  const scoped_ptr<Socket> socket1_;
+  const scoped_ptr<Socket> socket2_;
+  // Copies data from socket1 to socket2.
+  const scoped_ptr<BufferedCopier> buffer1_;
+  // Copies data from socket2 to socket1.
+  const scoped_ptr<BufferedCopier> buffer2_;
+};
+
+}  // namespace forwarder2
+
+#endif  // TOOLS_ANDROID_FORWARDER2_FORWARDER_H_
diff --git a/tools/android/forwarder2/forwarders_manager.cc b/tools/android/forwarder2/forwarders_manager.cc
new file mode 100644
index 0000000..1795cb5
--- /dev/null
+++ b/tools/android/forwarder2/forwarders_manager.cc
@@ -0,0 +1,132 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/forwarders_manager.h"
+
+#include <sys/select.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/posix/eintr_wrapper.h"
+#include "tools/android/forwarder2/forwarder.h"
+#include "tools/android/forwarder2/socket.h"
+
+namespace forwarder2 {
+
+ForwardersManager::ForwardersManager() : thread_("ForwardersManagerThread") {
+  thread_.Start();
+  WaitForEventsOnInternalThreadSoon();
+}
+
+
+ForwardersManager::~ForwardersManager() {
+  deletion_notifier_.Notify();
+}
+
+void ForwardersManager::CreateAndStartNewForwarder(scoped_ptr<Socket> socket1,
+                                                   scoped_ptr<Socket> socket2) {
+  // Note that the internal Forwarder vector is populated on the internal thread
+  // which is the only thread from which it's accessed.
+  thread_.message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(&ForwardersManager::CreateNewForwarderOnInternalThread,
+                 base::Unretained(this), base::Passed(&socket1),
+                 base::Passed(&socket2)));
+
+  // Guarantees that the CreateNewForwarderOnInternalThread callback posted to
+  // the internal thread gets executed immediately.
+  wakeup_notifier_.Notify();
+}
+
+void ForwardersManager::CreateNewForwarderOnInternalThread(
+    scoped_ptr<Socket> socket1,
+    scoped_ptr<Socket> socket2) {
+  DCHECK(thread_.message_loop_proxy()->RunsTasksOnCurrentThread());
+  forwarders_.push_back(new Forwarder(socket1.Pass(), socket2.Pass()));
+}
+
+void ForwardersManager::WaitForEventsOnInternalThreadSoon() {
+  thread_.message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(&ForwardersManager::WaitForEventsOnInternalThread,
+                 base::Unretained(this)));
+}
+
+void ForwardersManager::WaitForEventsOnInternalThread() {
+  DCHECK(thread_.message_loop_proxy()->RunsTasksOnCurrentThread());
+  fd_set read_fds;
+  fd_set write_fds;
+
+  FD_ZERO(&read_fds);
+  FD_ZERO(&write_fds);
+
+  // Populate the file descriptor sets.
+  int max_fd = -1;
+  for (ScopedVector<Forwarder>::iterator it = forwarders_.begin();
+       it != forwarders_.end(); ++it) {
+    Forwarder* const forwarder = *it;
+    forwarder->RegisterFDs(&read_fds, &write_fds, &max_fd);
+  }
+
+  const int notifier_fds[] = {
+    wakeup_notifier_.receiver_fd(),
+    deletion_notifier_.receiver_fd(),
+  };
+
+  for (int i = 0; i < arraysize(notifier_fds); ++i) {
+    const int notifier_fd = notifier_fds[i];
+    DCHECK_GT(notifier_fd, -1);
+    FD_SET(notifier_fd, &read_fds);
+    max_fd = std::max(max_fd, notifier_fd);
+  }
+
+  const int ret = HANDLE_EINTR(
+      select(max_fd + 1, &read_fds, &write_fds, NULL, NULL));
+  if (ret < 0) {
+    PLOG(ERROR) << "select";
+    return;
+  }
+
+  const bool must_shutdown = FD_ISSET(
+      deletion_notifier_.receiver_fd(), &read_fds);
+  if (must_shutdown && forwarders_.empty())
+    return;
+
+  base::ScopedClosureRunner wait_for_events_soon(
+      base::Bind(&ForwardersManager::WaitForEventsOnInternalThreadSoon,
+                 base::Unretained(this)));
+
+  if (FD_ISSET(wakeup_notifier_.receiver_fd(), &read_fds)) {
+    // Note that the events on FDs other than the wakeup notifier one, if any,
+    // will be processed upon the next select().
+    wakeup_notifier_.Reset();
+    return;
+  }
+
+  // Notify the Forwarder instances and remove the ones that are closed.
+  for (size_t i = 0; i < forwarders_.size(); ) {
+    Forwarder* const forwarder = forwarders_[i];
+    forwarder->ProcessEvents(read_fds, write_fds);
+
+    if (must_shutdown)
+      forwarder->Shutdown();
+
+    if (!forwarder->IsClosed()) {
+      ++i;
+      continue;
+    }
+
+    std::swap(forwarders_[i], forwarders_.back());
+    forwarders_.pop_back();
+  }
+}
+
+}  // namespace forwarder2
diff --git a/tools/android/forwarder2/forwarders_manager.h b/tools/android/forwarder2/forwarders_manager.h
new file mode 100644
index 0000000..4c6dea6
--- /dev/null
+++ b/tools/android/forwarder2/forwarders_manager.h
@@ -0,0 +1,45 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_FORWARDERS_MANAGER_H_
+#define TOOLS_ANDROID_FORWARDER2_FORWARDERS_MANAGER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/threading/thread.h"
+#include "tools/android/forwarder2/pipe_notifier.h"
+
+namespace forwarder2 {
+
+class Forwarder;
+class Socket;
+
+// Creates, owns and notifies Forwarder instances on its own internal thread.
+class ForwardersManager {
+ public:
+  ForwardersManager();
+
+  // Must be called on the thread the constructor was called on.
+  ~ForwardersManager();
+
+  // Can be called on any thread.
+  void CreateAndStartNewForwarder(scoped_ptr<Socket> socket1,
+                                  scoped_ptr<Socket> socket2);
+
+ private:
+  void CreateNewForwarderOnInternalThread(scoped_ptr<Socket> socket1,
+                                          scoped_ptr<Socket> socket2);
+
+  void WaitForEventsOnInternalThreadSoon();
+  void WaitForEventsOnInternalThread();
+
+  ScopedVector<Forwarder> forwarders_;
+  PipeNotifier deletion_notifier_;
+  PipeNotifier wakeup_notifier_;
+  base::Thread thread_;
+};
+
+}  // namespace forwarder2
+
+#endif  // TOOLS_ANDROID_FORWARDER2_FORWARDERS_MANAGER_H_
diff --git a/tools/android/forwarder2/host_controller.cc b/tools/android/forwarder2/host_controller.cc
new file mode 100644
index 0000000..94e63ec
--- /dev/null
+++ b/tools/android/forwarder2/host_controller.cc
@@ -0,0 +1,170 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/host_controller.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/android/forwarder2/command.h"
+#include "tools/android/forwarder2/forwarder.h"
+#include "tools/android/forwarder2/socket.h"
+
+namespace forwarder2 {
+
+// static
+scoped_ptr<HostController> HostController::Create(
+    int device_port,
+    int host_port,
+    int adb_port,
+    int exit_notifier_fd,
+    const ErrorCallback& error_callback) {
+  scoped_ptr<HostController> host_controller;
+  scoped_ptr<PipeNotifier> delete_controller_notifier(new PipeNotifier());
+  scoped_ptr<Socket> adb_control_socket(new Socket());
+  adb_control_socket->AddEventFd(exit_notifier_fd);
+  adb_control_socket->AddEventFd(delete_controller_notifier->receiver_fd());
+  if (!adb_control_socket->ConnectTcp(std::string(), adb_port)) {
+    LOG(ERROR) << "Could not connect HostController socket on port: "
+               << adb_port;
+    return host_controller.Pass();
+  }
+  // Send the command to the device start listening to the "device_forward_port"
+  bool send_command_success = SendCommand(
+      command::LISTEN, device_port, adb_control_socket.get());
+  CHECK(send_command_success);
+  int device_port_allocated;
+  command::Type command;
+  if (!ReadCommand(
+          adb_control_socket.get(), &device_port_allocated, &command) ||
+      command != command::BIND_SUCCESS) {
+    LOG(ERROR) << "Device binding error using port " << device_port;
+    return host_controller.Pass();
+  }
+  host_controller.reset(
+      new HostController(
+          device_port_allocated, host_port, adb_port, exit_notifier_fd,
+          error_callback, adb_control_socket.Pass(),
+          delete_controller_notifier.Pass()));
+  return host_controller.Pass();
+}
+
+HostController::~HostController() {
+  DCHECK(deletion_task_runner_->RunsTasksOnCurrentThread());
+  delete_controller_notifier_->Notify();
+}
+
+void HostController::Start() {
+  thread_.Start();
+  ReadNextCommandSoon();
+}
+
+HostController::HostController(
+    int device_port,
+    int host_port,
+    int adb_port,
+    int exit_notifier_fd,
+    const ErrorCallback& error_callback,
+    scoped_ptr<Socket> adb_control_socket,
+    scoped_ptr<PipeNotifier> delete_controller_notifier)
+    : self_deleter_helper_(this, error_callback),
+      device_port_(device_port),
+      host_port_(host_port),
+      adb_port_(adb_port),
+      global_exit_notifier_fd_(exit_notifier_fd),
+      adb_control_socket_(adb_control_socket.Pass()),
+      delete_controller_notifier_(delete_controller_notifier.Pass()),
+      deletion_task_runner_(base::MessageLoopProxy::current()),
+      thread_("HostControllerThread") {
+}
+
+void HostController::ReadNextCommandSoon() {
+  thread_.message_loop_proxy()->PostTask(
+      FROM_HERE,
+      base::Bind(&HostController::ReadCommandOnInternalThread,
+                 base::Unretained(this)));
+}
+
+void HostController::ReadCommandOnInternalThread() {
+  if (!ReceivedCommand(command::ACCEPT_SUCCESS, adb_control_socket_.get())) {
+    LOG(ERROR) << "Did not receive ACCEPT_SUCCESS for port: "
+               << host_port_;
+    OnInternalThreadError();
+    return;
+  }
+  // Try to connect to host server.
+  scoped_ptr<Socket> host_server_data_socket(new Socket());
+  if (!host_server_data_socket->ConnectTcp(std::string(), host_port_)) {
+    LOG(ERROR) << "Could not Connect HostServerData socket on port: "
+               << host_port_;
+    SendCommand(
+        command::HOST_SERVER_ERROR, device_port_, adb_control_socket_.get());
+    if (ReceivedCommand(command::ACK, adb_control_socket_.get())) {
+      // It can continue if the host forwarder could not connect to the host
+      // server but the device acknowledged that, so that the device could
+      // re-try later.
+      ReadNextCommandSoon();
+      return;
+    }
+    OnInternalThreadError();
+    return;
+  }
+  LOG(INFO) << "Will send HOST_SERVER_SUCCESS: " << host_port_;
+  SendCommand(
+      command::HOST_SERVER_SUCCESS, device_port_, adb_control_socket_.get());
+  StartForwarder(host_server_data_socket.Pass());
+  ReadNextCommandSoon();
+}
+
+void HostController::StartForwarder(
+    scoped_ptr<Socket> host_server_data_socket) {
+  scoped_ptr<Socket> adb_data_socket(new Socket());
+  if (!adb_data_socket->ConnectTcp("", adb_port_)) {
+    LOG(ERROR) << "Could not connect AdbDataSocket on port: " << adb_port_;
+    OnInternalThreadError();
+    return;
+  }
+  // Open the Adb data connection, and send a command with the
+  // |device_forward_port| as a way for the device to identify the connection.
+  SendCommand(command::DATA_CONNECTION, device_port_, adb_data_socket.get());
+
+  // Check that the device received the new Adb Data Connection. Note that this
+  // check is done through the |adb_control_socket_| that is handled in the
+  // DeviceListener thread just after the call to WaitForAdbDataSocket().
+  if (!ReceivedCommand(command::ADB_DATA_SOCKET_SUCCESS,
+                       adb_control_socket_.get())) {
+    LOG(ERROR) << "Device could not handle the new Adb Data Connection.";
+    OnInternalThreadError();
+    return;
+  }
+  forwarders_manager_.CreateAndStartNewForwarder(
+      host_server_data_socket.Pass(), adb_data_socket.Pass());
+}
+
+void HostController::OnInternalThreadError() {
+  UnmapPortOnDevice();
+  self_deleter_helper_.MaybeSelfDeleteSoon();
+}
+
+void HostController::UnmapPortOnDevice() {
+  Socket socket;
+  if (!socket.ConnectTcp("", adb_port_)) {
+    LOG(ERROR) << "Could not connect to device on port " << adb_port_;
+    return;
+  }
+  if (!SendCommand(command::UNLISTEN, device_port_, &socket)) {
+    LOG(ERROR) << "Could not send unmap command for port " << device_port_;
+    return;
+  }
+  if (!ReceivedCommand(command::UNLISTEN_SUCCESS, &socket)) {
+    LOG(ERROR) << "Unamp command failed for port " << device_port_;
+    return;
+  }
+}
+
+}  // namespace forwarder2
diff --git a/tools/android/forwarder2/host_controller.h b/tools/android/forwarder2/host_controller.h
new file mode 100644
index 0000000..d228bcc
--- /dev/null
+++ b/tools/android/forwarder2/host_controller.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_HOST_CONTROLLER_H_
+#define TOOLS_ANDROID_FORWARDER2_HOST_CONTROLLER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread.h"
+#include "tools/android/forwarder2/forwarders_manager.h"
+#include "tools/android/forwarder2/pipe_notifier.h"
+#include "tools/android/forwarder2/self_deleter_helper.h"
+#include "tools/android/forwarder2/socket.h"
+
+namespace forwarder2 {
+
+// This class partners with DeviceController and has the same lifetime and
+// threading characteristics as DeviceListener. In a nutshell, this class
+// operates on its own thread and is destroyed on the thread it was constructed
+// on. The class' deletion can happen in two different ways:
+// - Its destructor was called by its owner (HostControllersManager).
+// - Its internal thread requested self-deletion after an error happened. In
+//   this case the owner (HostControllersManager) is notified on the
+//   construction thread through the provided ErrorCallback invoked with the
+//   HostController instance. When this callback is invoked, it's up to the
+//   owner to delete the instance.
+class HostController {
+ public:
+  // Callback used for self-deletion when an error happens so that the client
+  // can perform some cleanup work before deleting the HostController instance.
+  typedef base::Callback<void (scoped_ptr<HostController>)> ErrorCallback;
+
+  // If |device_port| is zero then a dynamic port is allocated (and retrievable
+  // through device_port() below).
+  static scoped_ptr<HostController> Create(int device_port,
+                                           int host_port,
+                                           int adb_port,
+                                           int exit_notifier_fd,
+                                           const ErrorCallback& error_callback);
+
+  ~HostController();
+
+  // Starts the internal controller thread.
+  void Start();
+
+  int adb_port() const { return adb_port_; }
+
+  int device_port() const { return device_port_; }
+
+ private:
+  HostController(int device_port,
+                 int host_port,
+                 int adb_port,
+                 int exit_notifier_fd,
+                 const ErrorCallback& error_callback,
+                 scoped_ptr<Socket> adb_control_socket,
+                 scoped_ptr<PipeNotifier> delete_controller_notifier);
+
+  void ReadNextCommandSoon();
+  void ReadCommandOnInternalThread();
+
+  void StartForwarder(scoped_ptr<Socket> host_server_data_socket);
+
+  // Note that this gets also called when ~HostController() is invoked.
+  void OnInternalThreadError();
+
+  void UnmapPortOnDevice();
+
+  SelfDeleterHelper<HostController> self_deleter_helper_;
+  const int device_port_;
+  const int host_port_;
+  const int adb_port_;
+  // Used to notify the controller when the process is killed.
+  const int global_exit_notifier_fd_;
+  scoped_ptr<Socket> adb_control_socket_;
+  // Used to cancel the pending blocking IO operations when the host controller
+  // instance is deleted.
+  scoped_ptr<PipeNotifier> delete_controller_notifier_;
+  // Task runner used for deletion set at deletion time (i.e. the object is
+  // deleted on the same thread it is created on).
+  const scoped_refptr<base::SingleThreadTaskRunner> deletion_task_runner_;
+  base::Thread thread_;
+  ForwardersManager forwarders_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(HostController);
+};
+
+}  // namespace forwarder2
+
+#endif  // TOOLS_ANDROID_FORWARDER2_HOST_CONTROLLER_H_
diff --git a/tools/android/forwarder2/host_forwarder_main.cc b/tools/android/forwarder2/host_forwarder_main.cc
new file mode 100644
index 0000000..59571b6
--- /dev/null
+++ b/tools/android/forwarder2/host_forwarder_main.cc
@@ -0,0 +1,460 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <iostream>
+#include <limits>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/at_exit.h"
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/pickle.h"
+#include "base/safe_strerror_posix.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/task_runner.h"
+#include "base/threading/thread.h"
+#include "tools/android/forwarder2/common.h"
+#include "tools/android/forwarder2/daemon.h"
+#include "tools/android/forwarder2/host_controller.h"
+#include "tools/android/forwarder2/pipe_notifier.h"
+#include "tools/android/forwarder2/socket.h"
+#include "tools/android/forwarder2/util.h"
+
+namespace forwarder2 {
+namespace {
+
+const char kLogFilePath[] = "/tmp/host_forwarder_log";
+const char kDaemonIdentifier[] = "chrome_host_forwarder_daemon";
+
+const char kKillServerCommand[] = "kill-server";
+const char kForwardCommand[] = "forward";
+
+const int kBufSize = 256;
+
+// Needs to be global to be able to be accessed from the signal handler.
+PipeNotifier* g_notifier = NULL;
+
+// Lets the daemon fetch the exit notifier file descriptor.
+int GetExitNotifierFD() {
+  DCHECK(g_notifier);
+  return g_notifier->receiver_fd();
+}
+
+void KillHandler(int signal_number) {
+  char buf[kBufSize];
+  if (signal_number != SIGTERM && signal_number != SIGINT) {
+    snprintf(buf, sizeof(buf), "Ignoring unexpected signal %d.", signal_number);
+    SIGNAL_SAFE_LOG(WARNING, buf);
+    return;
+  }
+  snprintf(buf, sizeof(buf), "Received signal %d.", signal_number);
+  SIGNAL_SAFE_LOG(WARNING, buf);
+  static int s_kill_handler_count = 0;
+  CHECK(g_notifier);
+  // If for some reason the forwarder get stuck in any socket waiting forever,
+  // we can send a SIGKILL or SIGINT three times to force it die
+  // (non-nicely). This is useful when debugging.
+  ++s_kill_handler_count;
+  if (!g_notifier->Notify() || s_kill_handler_count > 2)
+    exit(1);
+}
+
+// Manages HostController instances. There is one HostController instance for
+// each connection being forwarded. Note that forwarding can happen with many
+// devices (identified with a serial id).
+class HostControllersManager {
+ public:
+  HostControllersManager()
+      : weak_ptr_factory_(this),
+        controllers_(new HostControllerMap()),
+        has_failed_(false) {
+  }
+
+  ~HostControllersManager() {
+    if (!thread_.get())
+      return;
+    // Delete the controllers on the thread they were created on.
+    thread_->message_loop_proxy()->DeleteSoon(
+        FROM_HERE, controllers_.release());
+  }
+
+  void HandleRequest(const std::string& device_serial,
+                     int device_port,
+                     int host_port,
+                     scoped_ptr<Socket> client_socket) {
+    // Lazy initialize so that the CLI process doesn't get this thread created.
+    InitOnce();
+    thread_->message_loop_proxy()->PostTask(
+        FROM_HERE,
+        base::Bind(
+            &HostControllersManager::HandleRequestOnInternalThread,
+            base::Unretained(this), device_serial, device_port, host_port,
+            base::Passed(&client_socket)));
+  }
+
+  bool has_failed() const { return has_failed_; }
+
+ private:
+  typedef base::hash_map<
+      std::string, linked_ptr<HostController> > HostControllerMap;
+
+  static std::string MakeHostControllerMapKey(int adb_port, int device_port) {
+    return base::StringPrintf("%d:%d", adb_port, device_port);
+  }
+
+  void InitOnce() {
+    if (thread_.get())
+      return;
+    at_exit_manager_.reset(new base::AtExitManager());
+    thread_.reset(new base::Thread("HostControllersManagerThread"));
+    thread_->Start();
+  }
+
+  // Invoked when a HostController instance reports an error (e.g. due to a
+  // device connectivity issue). Note that this could be called after the
+  // controller manager was destroyed which is why a weak pointer is used.
+  static void DeleteHostController(
+      const base::WeakPtr<HostControllersManager>& manager_ptr,
+      scoped_ptr<HostController> host_controller) {
+    HostController* const controller = host_controller.release();
+    HostControllersManager* const manager = manager_ptr.get();
+    if (!manager) {
+      // Note that |controller| is not leaked in this case since the host
+      // controllers manager owns the controllers. If the manager was deleted
+      // then all the controllers (including |controller|) were also deleted.
+      return;
+    }
+    DCHECK(manager->thread_->message_loop_proxy()->RunsTasksOnCurrentThread());
+    // Note that this will delete |controller| which is owned by the map.
+    DeleteRefCountedValueInMap(
+        MakeHostControllerMapKey(
+            controller->adb_port(), controller->device_port()),
+        manager->controllers_.get());
+  }
+
+  void HandleRequestOnInternalThread(const std::string& device_serial,
+                                     int device_port,
+                                     int host_port,
+                                     scoped_ptr<Socket> client_socket) {
+    const int adb_port = GetAdbPortForDevice(device_serial);
+    if (adb_port < 0) {
+      SendMessage(
+          "ERROR: could not get adb port for device. You might need to add "
+          "'adb' to your PATH or provide the device serial id.",
+          client_socket.get());
+      return;
+    }
+    if (device_port < 0) {
+      // Remove the previously created host controller.
+      const std::string controller_key = MakeHostControllerMapKey(
+          adb_port, -device_port);
+      const bool controller_did_exist = DeleteRefCountedValueInMap(
+          controller_key, controllers_.get());
+      SendMessage(
+          !controller_did_exist ? "ERROR: could not unmap port" : "OK",
+          client_socket.get());
+
+      RemoveAdbPortForDeviceIfNeeded(device_serial);
+      return;
+    }
+    if (host_port < 0) {
+      SendMessage("ERROR: missing host port", client_socket.get());
+      return;
+    }
+    const bool use_dynamic_port_allocation = device_port == 0;
+    if (!use_dynamic_port_allocation) {
+      const std::string controller_key = MakeHostControllerMapKey(
+          adb_port, device_port);
+      if (controllers_->find(controller_key) != controllers_->end()) {
+        LOG(INFO) << "Already forwarding device port " << device_port
+                  << " to host port " << host_port;
+        SendMessage(base::StringPrintf("%d:%d", device_port, host_port),
+                    client_socket.get());
+        return;
+      }
+    }
+    // Create a new host controller.
+    scoped_ptr<HostController> host_controller(
+        HostController::Create(
+            device_port, host_port, adb_port, GetExitNotifierFD(),
+            base::Bind(&HostControllersManager::DeleteHostController,
+                       weak_ptr_factory_.GetWeakPtr())));
+    if (!host_controller.get()) {
+      has_failed_ = true;
+      SendMessage("ERROR: Connection to device failed.", client_socket.get());
+      return;
+    }
+    // Get the current allocated port.
+    device_port = host_controller->device_port();
+    LOG(INFO) << "Forwarding device port " << device_port << " to host port "
+              << host_port;
+    const std::string msg = base::StringPrintf("%d:%d", device_port, host_port);
+    if (!SendMessage(msg, client_socket.get()))
+      return;
+    host_controller->Start();
+    controllers_->insert(
+        std::make_pair(MakeHostControllerMapKey(adb_port, device_port),
+                       linked_ptr<HostController>(host_controller.release())));
+  }
+
+  void RemoveAdbPortForDeviceIfNeeded(const std::string& device_serial) {
+    base::hash_map<std::string, int>::const_iterator it =
+        device_serial_to_adb_port_map_.find(device_serial);
+    if (it == device_serial_to_adb_port_map_.end())
+      return;
+
+    int port = it->second;
+    const std::string prefix = base::StringPrintf("%d:", port);
+    for (HostControllerMap::const_iterator others = controllers_->begin();
+         others != controllers_->end(); ++others) {
+      if (others->first.find(prefix) == 0U)
+        return;
+    }
+    // No other port is being forwarded to this device:
+    // - Remove it from our internal serial -> adb port map.
+    // - Remove from "adb forward" command.
+    LOG(INFO) << "Device " << device_serial << " has no more ports.";
+    device_serial_to_adb_port_map_.erase(device_serial);
+    const std::string serial_part = device_serial.empty() ?
+        std::string() : std::string("-s ") + device_serial;
+    const std::string command = base::StringPrintf(
+        "adb %s forward --remove tcp:%d",
+        serial_part.c_str(),
+        port);
+    const int ret = system(command.c_str());
+    LOG(INFO) << command << " ret: " << ret;
+    // Wait for the socket to be fully unmapped.
+    const std::string port_mapped_cmd = base::StringPrintf(
+        "lsof -nPi:%d",
+        port);
+    const int poll_interval_us = 500 * 1000;
+    int retries = 3;
+    while (retries) {
+      const int port_unmapped = system(port_mapped_cmd.c_str());
+      LOG(INFO) << "Device " << device_serial << " port " << port << " unmap "
+                << port_unmapped;
+      if (port_unmapped)
+        break;
+      --retries;
+      usleep(poll_interval_us);
+    }
+  }
+
+  int GetAdbPortForDevice(const std::string& device_serial) {
+    base::hash_map<std::string, int>::const_iterator it =
+        device_serial_to_adb_port_map_.find(device_serial);
+    if (it != device_serial_to_adb_port_map_.end())
+      return it->second;
+    Socket bind_socket;
+    CHECK(bind_socket.BindTcp("127.0.0.1", 0));
+    const int port = bind_socket.GetPort();
+    bind_socket.Close();
+    const std::string serial_part = device_serial.empty() ?
+        std::string() : std::string("-s ") + device_serial;
+    const std::string command = base::StringPrintf(
+        "adb %s forward tcp:%d localabstract:chrome_device_forwarder",
+        serial_part.c_str(),
+        port);
+    LOG(INFO) << command;
+    const int ret = system(command.c_str());
+    if (ret < 0 || !WIFEXITED(ret) || WEXITSTATUS(ret) != 0)
+      return -1;
+    device_serial_to_adb_port_map_[device_serial] = port;
+    return port;
+  }
+
+  bool SendMessage(const std::string& msg, Socket* client_socket) {
+    bool result = client_socket->WriteString(msg);
+    DCHECK(result);
+    if (!result)
+      has_failed_ = true;
+    return result;
+  }
+
+  base::WeakPtrFactory<HostControllersManager> weak_ptr_factory_;
+  base::hash_map<std::string, int> device_serial_to_adb_port_map_;
+  scoped_ptr<HostControllerMap> controllers_;
+  bool has_failed_;
+  scoped_ptr<base::AtExitManager> at_exit_manager_;  // Needed by base::Thread.
+  scoped_ptr<base::Thread> thread_;
+};
+
+class ServerDelegate : public Daemon::ServerDelegate {
+ public:
+  ServerDelegate() : has_failed_(false) {}
+
+  bool has_failed() const {
+    return has_failed_ || controllers_manager_.has_failed();
+  }
+
+  // Daemon::ServerDelegate:
+  virtual void Init() OVERRIDE {
+    LOG(INFO) << "Starting host process daemon (pid=" << getpid() << ")";
+    DCHECK(!g_notifier);
+    g_notifier = new PipeNotifier();
+    signal(SIGTERM, KillHandler);
+    signal(SIGINT, KillHandler);
+  }
+
+  virtual void OnClientConnected(scoped_ptr<Socket> client_socket) OVERRIDE {
+    char buf[kBufSize];
+    const int bytes_read = client_socket->Read(buf, sizeof(buf));
+    if (bytes_read <= 0) {
+      if (client_socket->DidReceiveEvent())
+        return;
+      PError("Read()");
+      has_failed_ = true;
+      return;
+    }
+    const Pickle command_pickle(buf, bytes_read);
+    PickleIterator pickle_it(command_pickle);
+    std::string device_serial;
+    CHECK(pickle_it.ReadString(&device_serial));
+    int device_port;
+    if (!pickle_it.ReadInt(&device_port)) {
+      client_socket->WriteString("ERROR: missing device port");
+      return;
+    }
+    int host_port;
+    if (!pickle_it.ReadInt(&host_port))
+      host_port = -1;
+    controllers_manager_.HandleRequest(
+        device_serial, device_port, host_port, client_socket.Pass());
+  }
+
+ private:
+  bool has_failed_;
+  HostControllersManager controllers_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServerDelegate);
+};
+
+class ClientDelegate : public Daemon::ClientDelegate {
+ public:
+  ClientDelegate(const Pickle& command_pickle)
+      : command_pickle_(command_pickle),
+        has_failed_(false) {
+  }
+
+  bool has_failed() const { return has_failed_; }
+
+  // Daemon::ClientDelegate:
+  virtual void OnDaemonReady(Socket* daemon_socket) OVERRIDE {
+    // Send the forward command to the daemon.
+    CHECK_EQ(command_pickle_.size(),
+             daemon_socket->WriteNumBytes(command_pickle_.data(),
+                                          command_pickle_.size()));
+    char buf[kBufSize];
+    const int bytes_read = daemon_socket->Read(
+        buf, sizeof(buf) - 1 /* leave space for null terminator */);
+    CHECK_GT(bytes_read, 0);
+    DCHECK(bytes_read < sizeof(buf));
+    buf[bytes_read] = 0;
+    base::StringPiece msg(buf, bytes_read);
+    if (msg.starts_with("ERROR")) {
+      LOG(ERROR) << msg;
+      has_failed_ = true;
+      return;
+    }
+    printf("%s\n", buf);
+  }
+
+ private:
+  const Pickle command_pickle_;
+  bool has_failed_;
+};
+
+void ExitWithUsage() {
+  std::cerr << "Usage: host_forwarder [options]\n\n"
+               "Options:\n"
+               "  --serial-id=[0-9A-Z]{16}]\n"
+               "  --map DEVICE_PORT HOST_PORT\n"
+               "  --unmap DEVICE_PORT\n"
+               "  --kill-server\n";
+  exit(1);
+}
+
+int PortToInt(const std::string& s) {
+  int value;
+  // Note that 0 is a valid port (used for dynamic port allocation).
+  if (!base::StringToInt(s, &value) || value < 0 ||
+      value > std::numeric_limits<uint16>::max()) {
+    LOG(ERROR) << "Could not convert string " << s << " to port";
+    ExitWithUsage();
+  }
+  return value;
+}
+
+int RunHostForwarder(int argc, char** argv) {
+  CommandLine::Init(argc, argv);
+  const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
+  bool kill_server = false;
+
+  Pickle pickle;
+  pickle.WriteString(
+      cmd_line.HasSwitch("serial-id") ?
+          cmd_line.GetSwitchValueASCII("serial-id") : std::string());
+
+  const std::vector<std::string> args = cmd_line.GetArgs();
+  if (cmd_line.HasSwitch("kill-server")) {
+    kill_server = true;
+  } else if (cmd_line.HasSwitch("unmap")) {
+    if (args.size() != 1)
+      ExitWithUsage();
+    // Note the minus sign below.
+    pickle.WriteInt(-PortToInt(args[0]));
+  } else if (cmd_line.HasSwitch("map")) {
+    if (args.size() != 2)
+      ExitWithUsage();
+    pickle.WriteInt(PortToInt(args[0]));
+    pickle.WriteInt(PortToInt(args[1]));
+  } else {
+    ExitWithUsage();
+  }
+
+  if (kill_server && args.size() > 0)
+    ExitWithUsage();
+
+  ClientDelegate client_delegate(pickle);
+  ServerDelegate daemon_delegate;
+  Daemon daemon(
+      kLogFilePath, kDaemonIdentifier, &client_delegate, &daemon_delegate,
+      &GetExitNotifierFD);
+
+  if (kill_server)
+    return !daemon.Kill();
+  if (!daemon.SpawnIfNeeded())
+    return 1;
+
+  return client_delegate.has_failed() || daemon_delegate.has_failed();
+}
+
+}  // namespace
+}  // namespace forwarder2
+
+int main(int argc, char** argv) {
+  return forwarder2::RunHostForwarder(argc, argv);
+}
diff --git a/tools/android/forwarder2/pipe_notifier.cc b/tools/android/forwarder2/pipe_notifier.cc
new file mode 100644
index 0000000..02842bd
--- /dev/null
+++ b/tools/android/forwarder2/pipe_notifier.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/pipe_notifier.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/safe_strerror_posix.h"
+
+namespace forwarder2 {
+
+PipeNotifier::PipeNotifier() {
+  int pipe_fd[2];
+  int ret = pipe(pipe_fd);
+  CHECK_EQ(0, ret);
+  receiver_fd_ = pipe_fd[0];
+  sender_fd_ = pipe_fd[1];
+  fcntl(sender_fd_, F_SETFL, O_NONBLOCK);
+}
+
+PipeNotifier::~PipeNotifier() {
+  close(receiver_fd_);
+  close(sender_fd_);
+}
+
+bool PipeNotifier::Notify() {
+  CHECK_NE(-1, sender_fd_);
+  errno = 0;
+  int ret = HANDLE_EINTR(write(sender_fd_, "1", 1));
+  if (ret < 0) {
+    PLOG(ERROR) << "write";
+    return false;
+  }
+  return true;
+}
+
+void PipeNotifier::Reset() {
+  char c;
+  int ret = HANDLE_EINTR(read(receiver_fd_, &c, 1));
+  if (ret < 0) {
+    PLOG(ERROR) << "read";
+    return;
+  }
+  DCHECK_EQ(1, ret);
+  DCHECK_EQ('1', c);
+}
+
+}  // namespace forwarder
diff --git a/tools/android/forwarder2/pipe_notifier.h b/tools/android/forwarder2/pipe_notifier.h
new file mode 100644
index 0000000..aadb269
--- /dev/null
+++ b/tools/android/forwarder2/pipe_notifier.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_PIPE_NOTIFIER_H_
+#define TOOLS_ANDROID_FORWARDER2_PIPE_NOTIFIER_H_
+
+#include "base/basictypes.h"
+
+namespace forwarder2 {
+
+// Helper class used to create a unix pipe that sends notifications to the
+// |receiver_fd_| file descriptor when called |Notify()|.  This should be used
+// by the main thread to notify other threads that it must exit.
+// The |receiver_fd_| can be put into a fd_set and used in a select together
+// with a socket waiting to accept or read.
+class PipeNotifier {
+ public:
+  PipeNotifier();
+  ~PipeNotifier();
+
+  bool Notify();
+
+  int receiver_fd() const { return receiver_fd_; }
+
+  void Reset();
+
+ private:
+  int sender_fd_;
+  int receiver_fd_;
+
+  DISALLOW_COPY_AND_ASSIGN(PipeNotifier);
+};
+
+}  // namespace forwarder
+
+#endif  // TOOLS_ANDROID_FORWARDER2_PIPE_NOTIFIER_H_
diff --git a/tools/android/forwarder2/self_deleter_helper.h b/tools/android/forwarder2/self_deleter_helper.h
new file mode 100644
index 0000000..d96903d
--- /dev/null
+++ b/tools/android/forwarder2/self_deleter_helper.h
@@ -0,0 +1,141 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_SELF_DELETER_HELPER_H_
+#define TOOLS_ANDROID_FORWARDER2_SELF_DELETER_HELPER_H_
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+
+namespace base {
+
+class SingleThreadTaskRunner;
+
+}  // namespace base
+
+namespace forwarder2 {
+
+// Helper template class to be used in the following case:
+//   * T is the type of an object that implements some work through an internal
+//     or worker thread.
+//   * T wants the internal thread to invoke deletion of its own instance, on
+//     the thread where the instance was created.
+//
+// To make this easier, do something like:
+//   1) Add a SelfDeleteHelper<T> member to your class T, and default-initialize
+//      it in its constructor.
+//   2) In the internal thread, to trigger self-deletion, call the
+//      MaybeDeleteSoon() method on this member.
+//
+// MaybeDeleteSoon() posts a task on the message loop where the T instance was
+// created to delete it. The task will be safely ignored if the instance is
+// otherwise deleted.
+//
+// Usage example:
+// class Object {
+//  public:
+//   typedef base::Callback<void (scoped_ptr<Object>)> ErrorCallback;
+//
+//   Object(const ErrorCallback& error_callback)
+//       : self_deleter_helper_(this, error_callback) {
+//   }
+//
+//   void StartWork() {
+//     // Post a callback to DoSomethingOnWorkerThread() below to another
+//     // thread.
+//   }
+//
+//   void DoSomethingOnWorkerThread() {
+//     ...
+//     if (error_happened)
+//       self_deleter_helper_.MaybeDeleteSoon();
+//   }
+//
+//  private:
+//   SelfDeleterHelper<MySelfDeletingClass> self_deleter_helper_;
+// };
+//
+// class ObjectOwner {
+//  public:
+//   ObjectOwner()
+//      : object_(new Object(base::Bind(&ObjectOwner::DeleteObjectOnError,
+//                                      base::Unretained(this))) {
+//      // To keep this example simple base::Unretained(this) is used above but
+//      // note that in a real world scenario the client would have to make sure
+//      // that the ObjectOwner instance is still alive when
+//      // DeleteObjectOnError() gets called below. This can be achieved by
+//      // using a WeakPtr<ObjectOwner> for instance.
+//   }
+//
+//   void StartWork() {
+//     object_->StartWork();
+//   }
+//
+//  private:
+//   void DeleteObjectOnError(scoped_ptr<Object> object) {
+//     DCHECK(thread_checker_.CalledOnValidThread());
+//     DCHECK_EQ(object_, object);
+//     // Do some extra work with |object| before it gets deleted...
+//     object_.reset();
+//     ignore_result(object.release());
+//   }
+//
+//   base::ThreadChecker thread_checker_;
+//   scoped_ptr<Object> object_;
+// };
+//
+template <typename T>
+class SelfDeleterHelper {
+ public:
+  typedef base::Callback<void (scoped_ptr<T>)> DeletionCallback;
+
+  SelfDeleterHelper(T* self_deleting_object,
+                    const DeletionCallback& deletion_callback)
+      : construction_runner_(base::MessageLoopProxy::current()),
+        self_deleting_object_(self_deleting_object),
+        deletion_callback_(deletion_callback),
+        weak_ptr_factory_(this) {
+  }
+
+  ~SelfDeleterHelper() {
+    DCHECK(construction_runner_->RunsTasksOnCurrentThread());
+  }
+
+  void MaybeSelfDeleteSoon() {
+    DCHECK(!construction_runner_->RunsTasksOnCurrentThread());
+    construction_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&SelfDeleterHelper::SelfDelete,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
+ private:
+  void SelfDelete() {
+    DCHECK(construction_runner_->RunsTasksOnCurrentThread());
+    deletion_callback_.Run(make_scoped_ptr(self_deleting_object_));
+  }
+
+  const scoped_refptr<base::SingleThreadTaskRunner> construction_runner_;
+  T* const self_deleting_object_;
+  const DeletionCallback deletion_callback_;
+
+  //WeakPtrFactory's documentation says:
+  // 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.
+  base::WeakPtrFactory<SelfDeleterHelper<T> > weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SelfDeleterHelper);
+};
+
+}  // namespace forwarder2
+
+#endif  // TOOLS_ANDROID_FORWARDER2_SELF_DELETER_HELPER_H_
diff --git a/tools/android/forwarder2/socket.cc b/tools/android/forwarder2/socket.cc
new file mode 100644
index 0000000..9feac84
--- /dev/null
+++ b/tools/android/forwarder2/socket.cc
@@ -0,0 +1,448 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/socket.h"
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/safe_strerror_posix.h"
+#include "tools/android/common/net.h"
+#include "tools/android/forwarder2/common.h"
+
+namespace {
+const int kNoTimeout = -1;
+const int kConnectTimeOut = 10;  // Seconds.
+
+bool FamilyIsTCP(int family) {
+  return family == AF_INET || family == AF_INET6;
+}
+}  // namespace
+
+namespace forwarder2 {
+
+bool Socket::BindUnix(const std::string& path) {
+  errno = 0;
+  if (!InitUnixSocket(path) || !BindAndListen()) {
+    Close();
+    return false;
+  }
+  return true;
+}
+
+bool Socket::BindTcp(const std::string& host, int port) {
+  errno = 0;
+  if (!InitTcpSocket(host, port) || !BindAndListen()) {
+    Close();
+    return false;
+  }
+  return true;
+}
+
+bool Socket::ConnectUnix(const std::string& path) {
+  errno = 0;
+  if (!InitUnixSocket(path) || !Connect()) {
+    Close();
+    return false;
+  }
+  return true;
+}
+
+bool Socket::ConnectTcp(const std::string& host, int port) {
+  errno = 0;
+  if (!InitTcpSocket(host, port) || !Connect()) {
+    Close();
+    return false;
+  }
+  return true;
+}
+
+Socket::Socket()
+    : socket_(-1),
+      port_(0),
+      socket_error_(false),
+      family_(AF_INET),
+      addr_ptr_(reinterpret_cast<sockaddr*>(&addr_.addr4)),
+      addr_len_(sizeof(sockaddr)) {
+  memset(&addr_, 0, sizeof(addr_));
+}
+
+Socket::~Socket() {
+  Close();
+}
+
+void Socket::Shutdown() {
+  if (!IsClosed()) {
+    PRESERVE_ERRNO_HANDLE_EINTR(shutdown(socket_, SHUT_RDWR));
+  }
+}
+
+void Socket::Close() {
+  if (!IsClosed()) {
+    CloseFD(socket_);
+    socket_ = -1;
+  }
+}
+
+bool Socket::InitSocketInternal() {
+  socket_ = socket(family_, SOCK_STREAM, 0);
+  if (socket_ < 0) {
+    PLOG(ERROR) << "socket";
+    return false;
+  }
+  tools::DisableNagle(socket_);
+  int reuse_addr = 1;
+  setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,
+             sizeof(reuse_addr));
+  if (!SetNonBlocking())
+    return false;
+  return true;
+}
+
+bool Socket::SetNonBlocking() {
+  const int flags = fcntl(socket_, F_GETFL);
+  if (flags < 0) {
+    PLOG(ERROR) << "fcntl";
+    return false;
+  }
+  if (flags & O_NONBLOCK)
+    return true;
+  if (fcntl(socket_, F_SETFL, flags | O_NONBLOCK) < 0) {
+    PLOG(ERROR) << "fcntl";
+    return false;
+  }
+  return true;
+}
+
+bool Socket::InitUnixSocket(const std::string& path) {
+  static const size_t kPathMax = sizeof(addr_.addr_un.sun_path);
+  // For abstract sockets we need one extra byte for the leading zero.
+  if (path.size() + 2 /* '\0' */ > kPathMax) {
+    LOG(ERROR) << "The provided path is too big to create a unix "
+               << "domain socket: " << path;
+    return false;
+  }
+  family_ = PF_UNIX;
+  addr_.addr_un.sun_family = family_;
+  // Copied from net/socket/unix_domain_socket_posix.cc
+  // Convert the path given into abstract socket name. It must start with
+  // the '\0' character, so we are adding it. |addr_len| must specify the
+  // length of the structure exactly, as potentially the socket name may
+  // have '\0' characters embedded (although we don't support this).
+  // Note that addr_.addr_un.sun_path is already zero initialized.
+  memcpy(addr_.addr_un.sun_path + 1, path.c_str(), path.size());
+  addr_len_ = path.size() + offsetof(struct sockaddr_un, sun_path) + 1;
+  addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr_un);
+  return InitSocketInternal();
+}
+
+bool Socket::InitTcpSocket(const std::string& host, int port) {
+  port_ = port;
+  if (host.empty()) {
+    // Use localhost: INADDR_LOOPBACK
+    family_ = AF_INET;
+    addr_.addr4.sin_family = family_;
+    addr_.addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+  } else if (!Resolve(host)) {
+    return false;
+  }
+  CHECK(FamilyIsTCP(family_)) << "Invalid socket family.";
+  if (family_ == AF_INET) {
+    addr_.addr4.sin_port = htons(port_);
+    addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr4);
+    addr_len_ = sizeof(addr_.addr4);
+  } else if (family_ == AF_INET6) {
+    addr_.addr6.sin6_port = htons(port_);
+    addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr6);
+    addr_len_ = sizeof(addr_.addr6);
+  }
+  return InitSocketInternal();
+}
+
+bool Socket::BindAndListen() {
+  errno = 0;
+  if (HANDLE_EINTR(bind(socket_, addr_ptr_, addr_len_)) < 0 ||
+      HANDLE_EINTR(listen(socket_, SOMAXCONN)) < 0) {
+    PLOG(ERROR) << "bind/listen";
+    SetSocketError();
+    return false;
+  }
+  if (port_ == 0 && FamilyIsTCP(family_)) {
+    SockAddr addr;
+    memset(&addr, 0, sizeof(addr));
+    socklen_t addrlen = 0;
+    sockaddr* addr_ptr = NULL;
+    uint16* port_ptr = NULL;
+    if (family_ == AF_INET) {
+      addr_ptr = reinterpret_cast<sockaddr*>(&addr.addr4);
+      port_ptr = &addr.addr4.sin_port;
+      addrlen = sizeof(addr.addr4);
+    } else if (family_ == AF_INET6) {
+      addr_ptr = reinterpret_cast<sockaddr*>(&addr.addr6);
+      port_ptr = &addr.addr6.sin6_port;
+      addrlen = sizeof(addr.addr6);
+    }
+    errno = 0;
+    if (getsockname(socket_, addr_ptr, &addrlen) != 0) {
+      PLOG(ERROR) << "getsockname";
+      SetSocketError();
+      return false;
+    }
+    port_ = ntohs(*port_ptr);
+  }
+  return true;
+}
+
+bool Socket::Accept(Socket* new_socket) {
+  DCHECK(new_socket != NULL);
+  if (!WaitForEvent(READ, kNoTimeout)) {
+    SetSocketError();
+    return false;
+  }
+  errno = 0;
+  int new_socket_fd = HANDLE_EINTR(accept(socket_, NULL, NULL));
+  if (new_socket_fd < 0) {
+    SetSocketError();
+    return false;
+  }
+  tools::DisableNagle(new_socket_fd);
+  new_socket->socket_ = new_socket_fd;
+  if (!new_socket->SetNonBlocking())
+    return false;
+  return true;
+}
+
+bool Socket::Connect() {
+  DCHECK(fcntl(socket_, F_GETFL) & O_NONBLOCK);
+  errno = 0;
+  if (HANDLE_EINTR(connect(socket_, addr_ptr_, addr_len_)) < 0 &&
+      errno != EINPROGRESS) {
+    SetSocketError();
+    return false;
+  }
+  // Wait for connection to complete, or receive a notification.
+  if (!WaitForEvent(WRITE, kConnectTimeOut)) {
+    SetSocketError();
+    return false;
+  }
+  int socket_errno;
+  socklen_t opt_len = sizeof(socket_errno);
+  if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &socket_errno, &opt_len) < 0) {
+    PLOG(ERROR) << "getsockopt()";
+    SetSocketError();
+    return false;
+  }
+  if (socket_errno != 0) {
+    LOG(ERROR) << "Could not connect to host: " << safe_strerror(socket_errno);
+    SetSocketError();
+    return false;
+  }
+  return true;
+}
+
+bool Socket::Resolve(const std::string& host) {
+  struct addrinfo hints;
+  struct addrinfo* res;
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags |= AI_CANONNAME;
+
+  int errcode = getaddrinfo(host.c_str(), NULL, &hints, &res);
+  if (errcode != 0) {
+    errno = 0;
+    SetSocketError();
+    freeaddrinfo(res);
+    return false;
+  }
+  family_ = res->ai_family;
+  switch (res->ai_family) {
+    case AF_INET:
+      memcpy(&addr_.addr4,
+             reinterpret_cast<sockaddr_in*>(res->ai_addr),
+             sizeof(sockaddr_in));
+      break;
+    case AF_INET6:
+      memcpy(&addr_.addr6,
+             reinterpret_cast<sockaddr_in6*>(res->ai_addr),
+             sizeof(sockaddr_in6));
+      break;
+  }
+  freeaddrinfo(res);
+  return true;
+}
+
+int Socket::GetPort() {
+  if (!FamilyIsTCP(family_)) {
+    LOG(ERROR) << "Can't call GetPort() on an unix domain socket.";
+    return 0;
+  }
+  return port_;
+}
+
+int Socket::ReadNumBytes(void* buffer, size_t num_bytes) {
+  int bytes_read = 0;
+  int ret = 1;
+  while (bytes_read < num_bytes && ret > 0) {
+    ret = Read(static_cast<char*>(buffer) + bytes_read, num_bytes - bytes_read);
+    if (ret >= 0)
+      bytes_read += ret;
+  }
+  return bytes_read;
+}
+
+void Socket::SetSocketError() {
+  socket_error_ = true;
+  DCHECK_NE(EAGAIN, errno);
+  DCHECK_NE(EWOULDBLOCK, errno);
+  Close();
+}
+
+int Socket::Read(void* buffer, size_t buffer_size) {
+  if (!WaitForEvent(READ, kNoTimeout)) {
+    SetSocketError();
+    return 0;
+  }
+  int ret = HANDLE_EINTR(read(socket_, buffer, buffer_size));
+  if (ret < 0) {
+    PLOG(ERROR) << "read";
+    SetSocketError();
+  }
+  return ret;
+}
+
+int Socket::NonBlockingRead(void* buffer, size_t buffer_size) {
+  DCHECK(fcntl(socket_, F_GETFL) & O_NONBLOCK);
+  int ret = HANDLE_EINTR(read(socket_, buffer, buffer_size));
+  if (ret < 0) {
+    PLOG(ERROR) << "read";
+    SetSocketError();
+  }
+  return ret;
+}
+
+int Socket::Write(const void* buffer, size_t count) {
+  if (!WaitForEvent(WRITE, kNoTimeout)) {
+    SetSocketError();
+    return 0;
+  }
+  int ret = HANDLE_EINTR(send(socket_, buffer, count, MSG_NOSIGNAL));
+  if (ret < 0) {
+    PLOG(ERROR) << "send";
+    SetSocketError();
+  }
+  return ret;
+}
+
+int Socket::NonBlockingWrite(const void* buffer, size_t count) {
+  DCHECK(fcntl(socket_, F_GETFL) & O_NONBLOCK);
+  int ret = HANDLE_EINTR(send(socket_, buffer, count, MSG_NOSIGNAL));
+  if (ret < 0) {
+    PLOG(ERROR) << "send";
+    SetSocketError();
+  }
+  return ret;
+}
+
+int Socket::WriteString(const std::string& buffer) {
+  return WriteNumBytes(buffer.c_str(), buffer.size());
+}
+
+void Socket::AddEventFd(int event_fd) {
+  Event event;
+  event.fd = event_fd;
+  event.was_fired = false;
+  events_.push_back(event);
+}
+
+bool Socket::DidReceiveEventOnFd(int fd) const {
+  for (size_t i = 0; i < events_.size(); ++i)
+    if (events_[i].fd == fd)
+      return events_[i].was_fired;
+  return false;
+}
+
+bool Socket::DidReceiveEvent() const {
+  for (size_t i = 0; i < events_.size(); ++i)
+    if (events_[i].was_fired)
+      return true;
+  return false;
+}
+
+int Socket::WriteNumBytes(const void* buffer, size_t num_bytes) {
+  int bytes_written = 0;
+  int ret = 1;
+  while (bytes_written < num_bytes && ret > 0) {
+    ret = Write(static_cast<const char*>(buffer) + bytes_written,
+                num_bytes - bytes_written);
+    if (ret >= 0)
+      bytes_written += ret;
+  }
+  return bytes_written;
+}
+
+bool Socket::WaitForEvent(EventType type, int timeout_secs) {
+  if (socket_ == -1)
+    return true;
+  DCHECK(fcntl(socket_, F_GETFL) & O_NONBLOCK);
+  fd_set read_fds;
+  fd_set write_fds;
+  FD_ZERO(&read_fds);
+  FD_ZERO(&write_fds);
+  if (type == READ)
+    FD_SET(socket_, &read_fds);
+  else
+    FD_SET(socket_, &write_fds);
+  for (size_t i = 0; i < events_.size(); ++i)
+    FD_SET(events_[i].fd, &read_fds);
+  timeval tv = {};
+  timeval* tv_ptr = NULL;
+  if (timeout_secs > 0) {
+    tv.tv_sec = timeout_secs;
+    tv.tv_usec = 0;
+    tv_ptr = &tv;
+  }
+  int max_fd = socket_;
+  for (size_t i = 0; i < events_.size(); ++i)
+    if (events_[i].fd > max_fd)
+      max_fd = events_[i].fd;
+  if (HANDLE_EINTR(
+          select(max_fd + 1, &read_fds, &write_fds, NULL, tv_ptr)) <= 0) {
+    PLOG(ERROR) << "select";
+    return false;
+  }
+  bool event_was_fired = false;
+  for (size_t i = 0; i < events_.size(); ++i) {
+    if (FD_ISSET(events_[i].fd, &read_fds)) {
+      events_[i].was_fired = true;
+      event_was_fired = true;
+    }
+  }
+  return !event_was_fired;
+}
+
+// static
+pid_t Socket::GetUnixDomainSocketProcessOwner(const std::string& path) {
+  Socket socket;
+  if (!socket.ConnectUnix(path))
+    return -1;
+  ucred ucred;
+  socklen_t len = sizeof(ucred);
+  if (getsockopt(socket.socket_, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
+    CHECK_NE(ENOPROTOOPT, errno);
+    return -1;
+  }
+  return ucred.pid;
+}
+
+}  // namespace forwarder2
diff --git a/tools/android/forwarder2/socket.h b/tools/android/forwarder2/socket.h
new file mode 100644
index 0000000..6047a1c
--- /dev/null
+++ b/tools/android/forwarder2/socket.h
@@ -0,0 +1,152 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_SOCKET_H_
+#define TOOLS_ANDROID_FORWARDER2_SOCKET_H_
+
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace forwarder2 {
+
+// Wrapper class around unix socket api.  Can be used to create, bind or
+// connect to both Unix domain sockets and TCP sockets.
+// TODO(pliard): Split this class into TCPSocket and UnixDomainSocket.
+class Socket {
+ public:
+  Socket();
+  ~Socket();
+
+  bool BindUnix(const std::string& path);
+  bool BindTcp(const std::string& host, int port);
+  bool ConnectUnix(const std::string& path);
+  bool ConnectTcp(const std::string& host, int port);
+
+  // Just a wrapper around unix socket shutdown(), see man 2 shutdown.
+  void Shutdown();
+
+  // Just a wrapper around unix socket close(), see man 2 close.
+  void Close();
+  bool IsClosed() const { return socket_ < 0; }
+
+  int fd() const { return socket_; }
+
+  bool Accept(Socket* new_socket);
+
+  // Returns the port allocated to this socket or zero on error.
+  int GetPort();
+
+  // Just a wrapper around unix read() function.
+  // Reads up to buffer_size, but may read less then buffer_size.
+  // Returns the number of bytes read.
+  int Read(void* buffer, size_t buffer_size);
+
+  // Non-blocking version of Read() above. This must be called after a
+  // successful call to select(). The socket must also be in non-blocking mode
+  // before calling this method.
+  int NonBlockingRead(void* buffer, size_t buffer_size);
+
+  // Wrapper around send().
+  int Write(const void* buffer, size_t count);
+
+  // Same as NonBlockingRead() but for writing.
+  int NonBlockingWrite(const void* buffer, size_t count);
+
+  // Calls Read() multiple times until num_bytes is written to the provided
+  // buffer. No bounds checking is performed.
+  // Returns number of bytes read, which can be different from num_bytes in case
+  // of errror.
+  int ReadNumBytes(void* buffer, size_t num_bytes);
+
+  // Calls Write() multiple times until num_bytes is written. No bounds checking
+  // is performed. Returns number of bytes written, which can be different from
+  // num_bytes in case of errror.
+  int WriteNumBytes(const void* buffer, size_t num_bytes);
+
+  // Calls WriteNumBytes for the given std::string. Note that the null
+  // terminator is not written to the socket.
+  int WriteString(const std::string& buffer);
+
+  bool has_error() const { return socket_error_; }
+
+  // |event_fd| must be a valid pipe file descriptor created from the
+  // PipeNotifier and must live (not be closed) at least as long as this socket
+  // is alive.
+  void AddEventFd(int event_fd);
+
+  // Returns whether Accept() or Connect() was interrupted because the socket
+  // received an external event fired through the provided fd.
+  bool DidReceiveEventOnFd(int fd) const;
+
+  bool DidReceiveEvent() const;
+
+  static pid_t GetUnixDomainSocketProcessOwner(const std::string& path);
+
+ private:
+  enum EventType {
+    READ,
+    WRITE
+  };
+
+  union SockAddr {
+    // IPv4 sockaddr
+    sockaddr_in addr4;
+    // IPv6 sockaddr
+    sockaddr_in6 addr6;
+    // Unix Domain sockaddr
+    sockaddr_un addr_un;
+  };
+
+  struct Event {
+    int fd;
+    bool was_fired;
+  };
+
+  bool SetNonBlocking();
+
+  // If |host| is empty, use localhost.
+  bool InitTcpSocket(const std::string& host, int port);
+  bool InitUnixSocket(const std::string& path);
+  bool BindAndListen();
+  bool Connect();
+
+  bool Resolve(const std::string& host);
+  bool InitSocketInternal();
+  void SetSocketError();
+
+  // Waits until either the Socket or the |exit_notifier_fd_| has received an
+  // event.
+  bool WaitForEvent(EventType type, int timeout_secs);
+
+  int socket_;
+  int port_;
+  bool socket_error_;
+
+  // Family of the socket (PF_INET, PF_INET6 or PF_UNIX).
+  int family_;
+
+  SockAddr addr_;
+
+  // Points to one of the members of the above union depending on the family.
+  sockaddr* addr_ptr_;
+  // Length of one of the members of the above union depending on the family.
+  socklen_t addr_len_;
+
+  // Used to listen for external events (e.g. process received a SIGTERM) while
+  // blocking on I/O operations.
+  std::vector<Event> events_;
+
+  DISALLOW_COPY_AND_ASSIGN(Socket);
+};
+
+}  // namespace forwarder
+
+#endif  // TOOLS_ANDROID_FORWARDER2_SOCKET_H_
diff --git a/tools/android/forwarder2/util.h b/tools/android/forwarder2/util.h
new file mode 100644
index 0000000..9947628
--- /dev/null
+++ b/tools/android/forwarder2/util.h
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_FORWARDER2_UTIL_H_
+#define TOOLS_ANDROID_FORWARDER2_UTIL_H_
+
+#include "base/logging.h"
+
+namespace forwarder2 {
+
+// Safely deletes a ref-counted value in a provided map by unlinking the object
+// from the map before deleting it in case its destructor would access the map.
+// Deletion will only happen by definition if the object's refcount is set to 1
+// before this function gets called. Returns whether the element could be found
+// in the map.
+template <typename Map, typename K>
+bool DeleteRefCountedValueInMap(const K& key, Map* map) {
+  const typename Map::iterator it = map->find(key);
+  if (it == map->end())
+    return false;
+  DeleteRefCountedValueInMapFromIterator(it, map);
+  return true;
+}
+
+// See DeleteRefCountedValuetInMap() above.
+template <typename Map, typename Iterator>
+void DeleteRefCountedValueInMapFromIterator(Iterator it, Map* map) {
+  DCHECK(it != map->end());
+  const typename Map::value_type::second_type shared_ptr_copy = it->second;
+  map->erase(it);
+}
+
+}  // namespace forwarder2
+
+#endif  // TOOLS_ANDROID_FORWARDER2_UTIL_H_
diff --git a/tools/android/heap_profiler/DEPS b/tools/android/heap_profiler/DEPS
new file mode 100644
index 0000000..458e541
--- /dev/null
+++ b/tools/android/heap_profiler/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/bsdtrees",
+]
diff --git a/tools/android/heap_profiler/heap_dump.c b/tools/android/heap_profiler/heap_dump.c
new file mode 100644
index 0000000..5d468da
--- /dev/null
+++ b/tools/android/heap_profiler/heap_dump.c
@@ -0,0 +1,350 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The client dump tool for libheap_profiler. It attaches to a process (given
+// its pid) and dumps all the libheap_profiler tracking information in JSON.
+// The JSON output looks like this:
+// {
+//   "total_allocated": 908748493,   # Total bytes allocated and not freed.
+//   "num_allocs":      37542,       # Number of allocations.
+//   "num_stacks":      3723,        # Number of allocation call-sites.
+//   "allocs":                       # Optional. Printed only with the -x arg.
+//   {
+//     "beef1234": {"l": 17, "f": 1, "s": "1a"},
+//      ^            ^        ^       ^ Index of the corresponding entry in the
+//      |            |        |         next "stacks" section. Essentially a ref
+//      |            |        |         to the call site that created the alloc.
+//      |            |        |
+//      |            |        +-------> Flags (last arg of heap_profiler_alloc).
+//      |            +----------------> Length of the Alloc.
+//      +-----------------------------> Start address of the Alloc (hex).
+//   },
+//   "stacks":
+//   {
+//      "1a": {"l": 17, "f": [1074792772, 1100849864, 1100850688, ...]},
+//       ^      ^        ^
+//       |      |        +-----> Stack frames (absolute virtual addresses).
+//       |      +--------------> Bytes allocated and not freed by the call site.
+//       +---------------------> Index of the entry (as for "allocs" xref).
+//                               Indexes are hex and might not be monotonic.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+
+#include "tools/android/heap_profiler/heap_profiler.h"
+
+
+static void lseek_abs(int fd, size_t off);
+static void read_proc_cmdline(char* cmdline, int size);
+static ssize_t read_safe(int fd, void* buf, size_t count);
+
+static int pid;
+
+
+static int dump_process_heap(
+    int mem_fd,
+    FILE* fmaps,
+    bool dump_also_allocs,
+    bool pedantic,  // Enable pedantic consistency checks on memory counters.
+    char* comment) {
+  HeapStats stats;
+  time_t tm;
+  char cmdline[512];
+
+  tm = time(NULL);
+  read_proc_cmdline(cmdline, sizeof(cmdline));
+
+  // Look for the mmap which contains the HeapStats in the target process vmem.
+  // On Linux/Android, the libheap_profiler mmaps explicitly /dev/zero. The
+  // region furthermore starts with a magic marker to disambiguate.
+  bool stats_mmap_found = false;
+  for (;;) {
+    char line[1024];
+    if (fgets(line, sizeof(line), fmaps) == NULL)
+      break;
+
+    uintptr_t start;
+    uintptr_t end;
+    char map_file[32];
+    int ret = sscanf(line, "%"SCNxPTR"-%"SCNxPTR" rw-p %*s %*s %*s %31s",
+                     &start, &end, map_file);
+    const size_t size = end - start + 1;
+    if (ret != 3 || strcmp(map_file, "/dev/zero") != 0 || size < sizeof(stats))
+      continue;
+
+    // The mmap looks promising. Let's check for the magic marker.
+    lseek_abs(mem_fd, start);
+    ssize_t rsize = read_safe(mem_fd, &stats, sizeof(stats));
+
+    if (rsize == -1) {
+      perror("read");
+      return -1;
+    }
+
+    if (rsize < sizeof(stats))
+      continue;
+
+    if (stats.magic_start == HEAP_PROFILER_MAGIC_MARKER) {
+      stats_mmap_found = true;
+      break;
+    }
+  }
+
+  if (!stats_mmap_found) {
+    fprintf(stderr, "Could not find the HeapStats area. "
+                    "It looks like libheap_profiler is not loaded.\n");
+    return -1;
+  }
+
+  // Print JSON-formatted output.
+  printf("{\n");
+  printf("  \"pid\":             %d,\n", pid);
+  printf("  \"time\":            %ld,\n", tm);
+  printf("  \"comment\":         \"%s\",\n", comment);
+  printf("  \"cmdline\":         \"%s\",\n", cmdline);
+  printf("  \"pagesize\":        %d,\n", getpagesize());
+  printf("  \"total_allocated\": %zu,\n", stats.total_alloc_bytes);
+  printf("  \"num_allocs\":      %"PRIu32",\n", stats.num_allocs);
+  printf("  \"num_stacks\":      %"PRIu32",\n", stats.num_stack_traces);
+
+  uint32_t dbg_counted_allocs = 0;
+  size_t dbg_counted_total_alloc_bytes = 0;
+  bool prepend_trailing_comma = false;  // JSON syntax, I hate you.
+  uint32_t i;
+
+  // Dump the optional allocation table.
+  if (dump_also_allocs) {
+    printf("  \"allocs\": {");
+    lseek_abs(mem_fd, (uintptr_t) stats.allocs);
+    for (i = 0; i < stats.max_allocs; ++i) {
+      Alloc alloc;
+      if (read_safe(mem_fd, &alloc, sizeof(alloc)) != sizeof(alloc)) {
+        fprintf(stderr, "ERROR: cannot read allocation table\n");
+        perror("read");
+        return -1;
+      }
+
+      // Skip empty (i.e. freed) entries.
+      if (alloc.start == 0 && alloc.end == 0)
+        continue;
+
+      if (alloc.end < alloc.start) {
+        fprintf(stderr, "ERROR: found inconsistent alloc.\n");
+        return -1;
+      }
+
+      size_t alloc_size = alloc.end - alloc.start + 1;
+      size_t stack_idx = (
+          (uintptr_t) alloc.st - (uintptr_t) stats.stack_traces) /
+          sizeof(StacktraceEntry);
+      dbg_counted_total_alloc_bytes += alloc_size;
+      ++dbg_counted_allocs;
+
+      if (prepend_trailing_comma)
+        printf(",");
+      prepend_trailing_comma = true;
+      printf("\"%"PRIxPTR"\": {\"l\": %zu, \"f\": %"PRIu32", \"s\": \"%zx\"}",
+             alloc.start, alloc_size, alloc.flags, stack_idx);
+    }
+    printf("},\n");
+
+    if (pedantic && dbg_counted_allocs != stats.num_allocs) {
+      fprintf(stderr,
+              "ERROR: inconsistent alloc count (%"PRIu32" vs %"PRIu32").\n",
+              dbg_counted_allocs, stats.num_allocs);
+      return -1;
+    }
+
+    if (pedantic && dbg_counted_total_alloc_bytes != stats.total_alloc_bytes) {
+      fprintf(stderr, "ERROR: inconsistent alloc totals (%zu vs %zu).\n",
+              dbg_counted_total_alloc_bytes, stats.total_alloc_bytes);
+      return -1;
+    }
+  }
+
+  // Dump the distinct stack traces.
+  printf("  \"stacks\": {");
+  prepend_trailing_comma = false;
+  dbg_counted_total_alloc_bytes = 0;
+  lseek_abs(mem_fd, (uintptr_t) stats.stack_traces);
+  for (i = 0; i < stats.max_stack_traces; ++i) {
+    StacktraceEntry st;
+    if (read_safe(mem_fd, &st, sizeof(st)) != sizeof(st)) {
+      fprintf(stderr, "ERROR: cannot read stack trace table\n");
+      perror("read");
+      return -1;
+    }
+
+    // Skip empty (i.e. freed) entries.
+    if (st.alloc_bytes == 0)
+      continue;
+
+    dbg_counted_total_alloc_bytes += st.alloc_bytes;
+
+    if (prepend_trailing_comma)
+      printf(",");
+    prepend_trailing_comma = true;
+
+    printf("\"%"PRIx32"\":{\"l\": %zu, \"f\": [", i, st.alloc_bytes);
+    size_t n = 0;
+    for (;;) {
+      printf("%" PRIuPTR, st.frames[n]);
+      ++n;
+      if (n == HEAP_PROFILER_MAX_DEPTH || st.frames[n] == 0)
+        break;
+      else
+        printf(",");
+    }
+    printf("]}");
+  }
+  printf("}\n}\n");
+
+  if (pedantic && dbg_counted_total_alloc_bytes != stats.total_alloc_bytes) {
+    fprintf(stderr, "ERROR: inconsistent stacks totals (%zu vs %zu).\n",
+            dbg_counted_total_alloc_bytes, stats.total_alloc_bytes);
+    return -1;
+  }
+
+  fflush(stdout);
+  return 0;
+}
+
+// Unfortunately lseek takes a *signed* offset, which is unsuitable for large
+// files like /proc/X/mem on 64-bit.
+static void lseek_abs(int fd, size_t off) {
+#define OFF_T_MAX ((off_t) ~(((uint64_t) 1) << (8 * sizeof(off_t) - 1)))
+  if (off <= OFF_T_MAX) {
+    lseek(fd, (off_t) off, SEEK_SET);
+    return;
+  }
+  lseek(fd, (off_t) OFF_T_MAX, SEEK_SET);
+  lseek(fd, (off_t) (off - OFF_T_MAX), SEEK_CUR);
+}
+
+static ssize_t read_safe(int fd, void* buf, size_t count) {
+  ssize_t res;
+  size_t bytes_read = 0;
+  if (count < 0)
+    return -1;
+  do {
+    do {
+      res = read(fd, buf + bytes_read, count - bytes_read);
+    } while (res == -1  && errno == EINTR);
+    if (res <= 0)
+      break;
+    bytes_read += res;
+  } while (bytes_read < count);
+  return bytes_read ? bytes_read : res;
+}
+
+static int open_proc_mem_fd() {
+  char path[64];
+  snprintf(path, sizeof(path), "/proc/%d/mem", pid);
+  int mem_fd = open(path, O_RDONLY);
+  if (mem_fd < 0) {
+    fprintf(stderr, "Could not attach to target process virtual memory.\n");
+    perror("open");
+  }
+  return mem_fd;
+}
+
+static FILE* open_proc_maps() {
+  char path[64];
+  snprintf(path, sizeof(path), "/proc/%d/maps", pid);
+  FILE* fmaps = fopen(path, "r");
+  if (fmaps == NULL) {
+    fprintf(stderr, "Could not open %s.\n", path);
+    perror("fopen");
+  }
+  return fmaps;
+}
+
+static void read_proc_cmdline(char* cmdline, int size) {
+  char path[64];
+  snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+  int cmdline_fd = open(path, O_RDONLY);
+  if (cmdline_fd < 0) {
+    fprintf(stderr, "Could not open %s.\n", path);
+    perror("open");
+    cmdline[0] = '\0';
+    return;
+  }
+  int length = read_safe(cmdline_fd, cmdline, size);
+  if (length < 0) {
+    fprintf(stderr, "Could not read %s.\n", path);
+    perror("read");
+    length = 0;
+  }
+  close(cmdline_fd);
+  cmdline[length] = '\0';
+}
+
+int main(int argc, char** argv) {
+  char c;
+  int ret = 0;
+  bool dump_also_allocs = false;
+  bool pedantic = true;
+  char comment[1024] = { '\0' };
+
+  while (((c = getopt(argc, argv, "xnc:")) & 0x80) == 0) {
+   switch (c) {
+      case 'x':
+        dump_also_allocs = true;
+        break;
+      case 'n':
+        pedantic = false;
+        break;
+      case 'c':
+        strlcpy(comment, optarg, sizeof(comment));
+        break;
+     }
+  }
+
+  if (optind >= argc) {
+    printf("Usage: %s [-n] [-x] [-c comment] pid\n"
+           "  -n: Skip pedantic checks on dump consistency.\n"
+           "  -x: Extended dump, includes individual allocations.\n"
+           "  -c: Appends the given comment to the JSON dump.\n",
+           argv[0]);
+    return -1;
+  }
+
+  pid = atoi(argv[optind]);
+
+  if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
+    perror("ptrace");
+    return -1;
+  }
+
+  // Wait for the process to actually freeze.
+  waitpid(pid, NULL, 0);
+
+  int mem_fd = open_proc_mem_fd();
+  if (mem_fd < 0)
+    ret = -1;
+
+  FILE* fmaps = open_proc_maps();
+  if (fmaps == NULL)
+    ret = -1;
+
+  if (ret == 0)
+    ret = dump_process_heap(mem_fd, fmaps, dump_also_allocs, pedantic, comment);
+
+  ptrace(PTRACE_DETACH, pid, NULL, NULL);
+
+  // Cleanup.
+  fflush(stdout);
+  close(mem_fd);
+  fclose(fmaps);
+  return ret;
+}
diff --git a/tools/android/heap_profiler/heap_profiler.c b/tools/android/heap_profiler/heap_profiler.c
new file mode 100644
index 0000000..aef63ba
--- /dev/null
+++ b/tools/android/heap_profiler/heap_profiler.c
@@ -0,0 +1,397 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a OS-independent* module which purpose is tracking allocations and
+// their call sites (stack traces). It is able to deal with hole punching
+// (read: munmap). Also, it has low overhead and its presence in the system its
+// barely noticeable, even if tracing *all* the processes.
+// This module does NOT know how to deal with stack unwinding. The caller must
+// do that and pass the addresses of the unwound stack.
+// * (Modulo three lines for mutexes.)
+//
+// Exposed API:
+//   void heap_profiler_init(HeapStats*);
+//   void heap_profiler_alloc(addr, size, stack_frames, depth, flags);
+//   void heap_profiler_free(addr, size);  (size == 0 means free entire region).
+//
+// The profiling information is tracked into two data structures:
+// 1) A RB-Tree of non-overlapping VM regions (allocs) sorted by their start
+//    addr. Each entry tracks the start-end addresses and points to the stack
+//    trace which created that allocation (see below).
+// 2) A (hash) table of stack traces. In general the #allocations >> #call sites
+//    which create those allocations. In order to avoid duplicating the latter,
+//    they are stored distinctly in this hash table and used by reference.
+//
+//   /  Process virtual address space  \
+//   +------+      +------+      +------+
+//   |Alloc1|      |Alloc2|      |Alloc3|    <- Allocs (a RB-Tree underneath)
+//   +------+      +------+      +------+
+//    Len: 12       Len: 4        Len: 4
+//       |            |             |                     stack_traces
+//       |            |             |              +-----------+--------------+
+//       |            |             |              | Alloc tot | stack frames +
+//       |            |             |              +-----------+--------------+
+//       +------------|-------------+------------> |    16     | 0x1234 ....  |
+//                    |                            +-----------+--------------+
+//                    +--------------------------> |     4     | 0x5678 ....  |
+//                                                 +-----------+--------------+
+//                                                   (A hash-table underneath)
+//
+// Final note: the memory for both 1) and 2) entries is carved out from two
+// static pools (i.e. stack_traces and allocs). The pools are treated as
+// a sbrk essentially, and are kept compact by reusing freed elements (hence
+// having a freelist for each of them).
+//
+// All the internal (static) functions here assume that the |lock| is held.
+
+#include <assert.h>
+#include <string.h>
+
+// Platform-dependent mutex boilerplate.
+#if defined(__linux__) || defined(__ANDROID__)
+#include <pthread.h>
+#define DEFINE_MUTEX(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER
+#define LOCK_MUTEX(x) pthread_mutex_lock(&x)
+#define UNLOCK_MUTEX(x) pthread_mutex_unlock(&x)
+#else
+#error OS not supported.
+#endif
+
+#include "tools/android/heap_profiler/heap_profiler.h"
+
+
+static DEFINE_MUTEX(lock);
+
+// |stats| contains the global tracking metadata and is the entry point which
+// is read by the heap_dump tool.
+static HeapStats* stats;
+
+// +---------------------------------------------------------------------------+
+// + Stack traces hash-table                                                   +
+// +---------------------------------------------------------------------------+
+#define ST_ENTRIES_MAX (64 * 1024)
+#define ST_HASHTABLE_BUCKETS (64 * 1024) /* Must be a power of 2. */
+
+static StacktraceEntry stack_traces[ST_ENTRIES_MAX];
+static StacktraceEntry* stack_traces_freelist;
+static StacktraceEntry* stack_traces_ht[ST_HASHTABLE_BUCKETS];
+
+// Looks up a stack trace from the stack frames. Creates a new one if necessary.
+static StacktraceEntry* record_stacktrace(uintptr_t* frames, uint32_t depth) {
+  if (depth == 0)
+    return NULL;
+
+  if (depth > HEAP_PROFILER_MAX_DEPTH)
+    depth = HEAP_PROFILER_MAX_DEPTH;
+
+  uint32_t i;
+  uintptr_t hash = 0;
+  for (i = 0; i < depth; ++i)
+    hash = (hash << 1) ^ (frames[i]);
+  const uint32_t slot = hash & (ST_HASHTABLE_BUCKETS - 1);
+  StacktraceEntry* st = stack_traces_ht[slot];
+
+  // Look for an existing entry in the hash-table.
+  const size_t frames_length = depth * sizeof(uintptr_t);
+  while (st != NULL && st->hash != hash &&
+         memcmp(frames, st->frames, frames_length) != 0) {
+    st = st->next;
+  }
+
+  // If not found, create a new one from the stack_traces array and add it to
+  // the hash-table.
+  if (st == NULL) {
+    // Get a free element either from the freelist or from the pool.
+    if (stack_traces_freelist != NULL) {
+      st = stack_traces_freelist;
+      stack_traces_freelist = stack_traces_freelist->next;
+    } else if (stats->max_stack_traces < ST_ENTRIES_MAX) {
+      st = &stack_traces[stats->max_stack_traces];
+      ++stats->max_stack_traces;
+    } else {
+      return NULL;
+    }
+
+    memset(st, 0, sizeof(*st));
+    memcpy(st->frames, frames, frames_length);
+    st->hash = hash;
+    st->next = stack_traces_ht[slot];
+    stack_traces_ht[slot] = st;
+    ++stats->num_stack_traces;
+  }
+
+  return st;
+}
+
+// Frees up a stack trace and appends it to the corresponding freelist.
+static void free_stacktrace(StacktraceEntry* st) {
+  assert(st->alloc_bytes == 0);
+  const uint32_t slot = st->hash & (ST_HASHTABLE_BUCKETS - 1);
+
+  // The expected load factor of the hash-table is very low. Frees should be
+  // pretty rare. Hence don't bother with a doubly linked list, might cost more.
+  StacktraceEntry** prev = &stack_traces_ht[slot];
+  while (*prev != st)
+    prev = &((*prev)->next);
+
+  // Remove from the hash-table bucket.
+  assert(*prev == st);
+  *prev = st->next;
+
+  // Add to the freelist.
+  st->next = stack_traces_freelist;
+  stack_traces_freelist = st;
+  --stats->num_stack_traces;
+}
+
+// +---------------------------------------------------------------------------+
+// + Allocs RB-tree                                                            +
+// +---------------------------------------------------------------------------+
+#define ALLOCS_ENTRIES_MAX (256 * 1024)
+
+static Alloc allocs[ALLOCS_ENTRIES_MAX];
+static Alloc* allocs_freelist;
+static RB_HEAD(HeapEntriesTree, Alloc) allocs_tree =
+    RB_INITIALIZER(&allocs_tree);
+
+// Comparator used by the RB-Tree (mind the overflow, avoid arith on addresses).
+static int allocs_tree_cmp(Alloc *alloc_1, Alloc *alloc_2) {
+  if (alloc_1->start < alloc_2->start)
+    return -1;
+  if (alloc_1->start > alloc_2->start)
+    return 1;
+  return 0;
+}
+
+RB_PROTOTYPE(HeapEntriesTree, Alloc, rb_node, allocs_tree_cmp);
+RB_GENERATE(HeapEntriesTree, Alloc, rb_node, allocs_tree_cmp);
+
+// Allocates a new Alloc and inserts it in the tree.
+static Alloc* insert_alloc(
+    uintptr_t start, uintptr_t end, StacktraceEntry* st, uint32_t flags) {
+  Alloc* alloc = NULL;
+
+  // First of all, get a free element either from the freelist or from the pool.
+  if (allocs_freelist != NULL) {
+    alloc = allocs_freelist;
+    allocs_freelist = alloc->next_free;
+  } else if (stats->max_allocs < ALLOCS_ENTRIES_MAX) {
+    alloc = &allocs[stats->max_allocs];
+    ++stats->max_allocs;
+  } else {
+    return NULL;  // OOM.
+  }
+
+  alloc->start = start;
+  alloc->end = end;
+  alloc->st = st;
+  alloc->flags = flags;
+  alloc->next_free = NULL;
+  RB_INSERT(HeapEntriesTree, &allocs_tree, alloc);
+  ++stats->num_allocs;
+  return alloc;
+}
+
+// Deletes all the allocs in the range [addr, addr+size[ dealing with partial
+// frees and hole punching. Note that in the general case this function might
+// need to deal with very unfortunate cases, as below:
+//
+// Alloc tree begin: [Alloc 1]----[Alloc 2]-------[Alloc 3][Alloc 4]---[Alloc 5]
+// Deletion range:                      [xxxxxxxxxxxxxxxxxxxx]
+// Alloc tree end:   [Alloc 1]----[Al.2]----------------------[Al.4]---[Alloc 5]
+//                   Alloc3 has to be deleted and Alloc 2,4 shrunk.
+static uint32_t delete_allocs_in_range(void* addr, size_t size) {
+  uintptr_t del_start = (uintptr_t) addr;
+  uintptr_t del_end = del_start + size - 1;
+  uint32_t flags = 0;
+
+  Alloc* alloc = NULL;
+  Alloc* next_alloc = RB_ROOT(&allocs_tree);
+
+  // Lookup the first (by address) relevant Alloc to initiate the deletion walk.
+  // At the end of the loop next_alloc is either:
+  // - the closest alloc starting before (or exactly at) the start of the
+  //   deletion range (i.e. addr == del_start).
+  // - the first alloc inside the deletion range.
+  // - the first alloc after the deletion range iff the range was already empty
+  //   (in this case the next loop will just bail out doing nothing).
+  // - NULL: iff the entire tree is empty (as above).
+  while (next_alloc != NULL) {
+    alloc = next_alloc;
+    if (alloc->start > del_start) {
+      next_alloc = RB_LEFT(alloc, rb_node);
+    } else if (alloc->end < del_start) {
+      next_alloc = RB_RIGHT(alloc, rb_node);
+    } else {  // alloc->start <= del_start && alloc->end >= del_start
+      break;
+    }
+  }
+
+  // Now scan the allocs linearly deleting chunks (or eventually whole allocs)
+  // until passing the end of the deleting region.
+  next_alloc = alloc;
+  while (next_alloc != NULL) {
+    alloc = next_alloc;
+    next_alloc = RB_NEXT(HeapEntriesTree, &allocs_tree, alloc);
+
+    if (size != 0) {
+      // In the general case we stop passed the end of the deletion range.
+      if (alloc->start > del_end)
+        break;
+
+      // This deals with the case of the first Alloc laying before the range.
+      if (alloc->end < del_start)
+        continue;
+    } else {
+      // size == 0 is a special case. It means deleting only the alloc which
+      // starts exactly at |del_start| if any (for dealing with free(ptr)).
+      if (alloc->start > del_start)
+        break;
+      if (alloc->start < del_start)
+        continue;
+      del_end = alloc->end;
+    }
+
+    // Reached this point the Alloc must overlap (partially or completely) with
+    // the deletion range.
+    assert(!(alloc->start > del_end || alloc->end < del_start));
+
+    StacktraceEntry* st = alloc->st;
+    flags |= alloc->flags;
+    uintptr_t freed_bytes = 0;  // Bytes freed in this cycle.
+
+    if (del_start <= alloc->start) {
+      if (del_end >= alloc->end) {
+        // Complete overlap. Delete full Alloc. Note: the range might might
+        // still overlap with the next allocs.
+        // Begin:      ------[alloc.start    alloc.end]-[next alloc]
+        // Del range:      [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
+        // Result:     ---------------------------------[next alloc]
+        //             [next alloc] will be shrinked on the next iteration.
+        freed_bytes = alloc->end - alloc->start + 1;
+        RB_REMOVE(HeapEntriesTree, &allocs_tree, alloc);
+
+        // Clean-up, so heap_dump can tell this is a free entry and skip it.
+        alloc->start = alloc->end = 0;
+        alloc->st = NULL;
+
+        // Put in the freelist.
+        alloc->next_free = allocs_freelist;
+        allocs_freelist = alloc;
+        --stats->num_allocs;
+      } else {
+        // Partial overlap at beginning. Cut first part and shrink the alloc.
+        // Begin:      ------[alloc.start  alloc.end]-[next alloc]
+        // Del range:      [xxxxxx]
+        // Result:     ------------[start  alloc.end]-[next alloc]
+        freed_bytes = del_end - alloc->start + 1;
+        alloc->start = del_end + 1;
+        // No need to update the tree even if we changed the key. The keys are
+        // still monotonic (because the ranges are guaranteed to not overlap).
+      }
+    } else {
+      if (del_end >= alloc->end) {
+        // Partial overlap at end. Cut last part and shrink the alloc left.
+        // Begin:      ------[alloc.start     alloc.end]-[next alloc]
+        // Del range:                               [xxxxxxxx]
+        // Result:     ------[alloc.start alloc.end]-----[next alloc]
+        //             [next alloc] will be shrinked on the next iteration.
+        freed_bytes = alloc->end - del_start + 1;
+        alloc->end = del_start - 1;
+      } else {
+        // Hole punching. Requires creating an extra alloc.
+        // Begin:      ------[alloc.start     alloc.end]-[next alloc]
+        // Del range:                   [xxx]
+        // Result:     ------[ alloc 1 ]-----[ alloc 2 ]-[next alloc]
+        freed_bytes = del_end - del_start + 1;
+        const uintptr_t old_end = alloc->end;
+        alloc->end = del_start - 1;
+
+        // In case of OOM, don't count the 2nd alloc we failed to allocate.
+        if (insert_alloc(del_end + 1, old_end, st, alloc->flags) == NULL)
+          freed_bytes += (old_end - del_end);
+      }
+    }
+    // Now update the StackTraceEntry the Alloc was pointing to, eventually
+    // freeing it up.
+    assert(st->alloc_bytes >= freed_bytes);
+    st->alloc_bytes -= freed_bytes;
+    if (st->alloc_bytes == 0)
+      free_stacktrace(st);
+    stats->total_alloc_bytes -= freed_bytes;
+  }
+  return flags;
+}
+
+// +---------------------------------------------------------------------------+
+// + Library entry points (refer to heap_profiler.h for API doc).              +
+// +---------------------------------------------------------------------------+
+void heap_profiler_free(void* addr, size_t size, uint32_t* old_flags) {
+  assert(size == 0 || ((uintptr_t) addr + (size - 1)) >= (uintptr_t) addr);
+
+  LOCK_MUTEX(lock);
+  uint32_t flags = delete_allocs_in_range(addr, size);
+  UNLOCK_MUTEX(lock);
+
+  if (old_flags != NULL)
+    *old_flags = flags;
+}
+
+void heap_profiler_alloc(void* addr, size_t size, uintptr_t* frames,
+                         uint32_t depth, uint32_t flags) {
+  if (depth > HEAP_PROFILER_MAX_DEPTH)
+    depth = HEAP_PROFILER_MAX_DEPTH;
+
+  if (size == 0)  // Apps calling malloc(0), sometimes it happens.
+    return;
+
+  const uintptr_t start = (uintptr_t) addr;
+  const uintptr_t end = start + (size - 1);
+  assert(start <= end);
+
+  LOCK_MUTEX(lock);
+
+  delete_allocs_in_range(addr, size);
+
+  StacktraceEntry* st = record_stacktrace(frames, depth);
+  if (st != NULL) {
+    Alloc* alloc = insert_alloc(start, end, st, flags);
+    if (alloc != NULL) {
+      st->alloc_bytes += size;
+      stats->total_alloc_bytes += size;
+    }
+  }
+
+  UNLOCK_MUTEX(lock);
+}
+
+void heap_profiler_init(HeapStats* heap_stats) {
+  LOCK_MUTEX(lock);
+
+  assert(stats == NULL);
+  stats = heap_stats;
+  memset(stats, 0, sizeof(HeapStats));
+  stats->magic_start = HEAP_PROFILER_MAGIC_MARKER;
+  stats->allocs = &allocs[0];
+  stats->stack_traces = &stack_traces[0];
+
+  UNLOCK_MUTEX(lock);
+}
+
+void heap_profiler_cleanup(void) {
+  LOCK_MUTEX(lock);
+
+  assert(stats != NULL);
+  memset(stack_traces, 0, sizeof(StacktraceEntry) * stats->max_stack_traces);
+  memset(stack_traces_ht, 0, sizeof(stack_traces_ht));
+  stack_traces_freelist = NULL;
+
+  memset(allocs, 0, sizeof(Alloc) * stats->max_allocs);
+  allocs_freelist = NULL;
+  RB_INIT(&allocs_tree);
+
+  stats = NULL;
+
+  UNLOCK_MUTEX(lock);
+}
diff --git a/tools/android/heap_profiler/heap_profiler.gyp b/tools/android/heap_profiler/heap_profiler.gyp
new file mode 100644
index 0000000..50e6797
--- /dev/null
+++ b/tools/android/heap_profiler/heap_profiler.gyp
@@ -0,0 +1,75 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      # libheap_profiler is the library that will be preloaded in the Android
+      # Zygote and contains the black magic to hook malloc/mmap calls.
+      'target_name': 'heap_profiler',
+      'type': 'shared_library',
+      'include_dirs': [ '../../..' ],
+      'sources': [ 'heap_profiler_hooks_android.c' ],
+      'dependencies': [ 'heap_profiler_core' ],
+    },
+    {
+      # heap_profiler_core contains only the tracking metadata code without any
+      # hooks. It is required by both the hprof library itself and the unittest.
+      'target_name': 'heap_profiler_core',
+      'type': 'static_library',
+      'sources': [
+        'heap_profiler.c',
+        'heap_profiler.h',
+      ],
+      'include_dirs': [ '../../..' ],
+    },
+    {
+      'target_name': 'heap_dump',
+      'type': 'executable',
+      'sources': [ 'heap_dump.c' ],
+      'include_dirs': [ '../../..' ],
+    },
+    {
+      'target_name': 'heap_profiler_unittests',
+      'type': '<(gtest_target_type)',
+      'sources': [ 'heap_profiler_unittest.cc' ],
+      'dependencies': [
+        'heap_profiler_core',
+        '../../../testing/android/native_test.gyp:native_test_native_code',
+        '../../../testing/gtest.gyp:gtest',
+        '../../../testing/gtest.gyp:gtest_main',
+      ],
+      'include_dirs': [ '../../..' ],
+    },
+    {
+      'target_name': 'heap_profiler_unittests_apk',
+      'type': 'none',
+      'dependencies': [
+        'heap_profiler_unittests',
+      ],
+      'variables': {
+        'test_suite_name': 'heap_profiler_unittests',
+      },
+      'includes': [ '../../../build/apk_test.gypi' ],
+    },
+    {
+      'target_name': 'heap_profiler_integrationtest',
+      'type': 'executable',
+      'sources': [ 'heap_profiler_integrationtest.cc' ],
+      'dependencies': [ '../../../testing/gtest.gyp:gtest' ],
+      'include_dirs': [ '../../..' ],
+    },
+    {
+      'target_name': 'heap_profiler_integrationtest_stripped',
+      'type': 'none',
+      'dependencies': [ 'heap_profiler_integrationtest' ],
+      'actions': [{
+        'action_name': 'strip heap_profiler_integrationtest',
+        'inputs': [ '<(PRODUCT_DIR)/heap_profiler_integrationtest' ],
+        'outputs': [ '<(PRODUCT_DIR)/heap_profiler_integrationtest_stripped' ],
+        'action': [ '<(android_strip)', '<@(_inputs)', '-o', '<@(_outputs)' ],
+      }],
+    },
+  ],
+}
diff --git a/tools/android/heap_profiler/heap_profiler.h b/tools/android/heap_profiler/heap_profiler.h
new file mode 100644
index 0000000..491081d
--- /dev/null
+++ b/tools/android/heap_profiler/heap_profiler.h
@@ -0,0 +1,90 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_ANDROID_HEAP_PROFILER_HEAP_PROFILER_H_
+#define TOOLS_ANDROID_HEAP_PROFILER_HEAP_PROFILER_H_
+
+#include <stdint.h>
+#include "third_party/bsdtrees/tree.h"
+
+#define HEAP_PROFILER_MAGIC_MARKER 0x42beef42L
+#define HEAP_PROFILER_MAX_DEPTH 12
+
+// The allocation is a result of a system malloc() invocation.
+#define HEAP_PROFILER_FLAGS_MALLOC 1
+
+// The allocation is a result of a mmap() invocation.
+#define HEAP_PROFILER_FLAGS_MMAP 2  // Allocation performed through mmap.
+
+// Only in the case of FLAGS_MMAP: The mmap is not anonymous (i.e. file backed).
+#define HEAP_PROFILER_FLAGS_MMAP_FILE 4
+
+// Android only: allocation made by the Zygote (before forking).
+#define HEAP_PROFILER_FLAGS_IN_ZYGOTE 8
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct StacktraceEntry {
+  uintptr_t frames[HEAP_PROFILER_MAX_DEPTH];  // Absolute addrs of stack frames.
+  uint32_t hash;  // H(frames), used to keep these entries in a hashtable.
+
+  // Total number of bytes allocated through this code path. It is equal to the
+  // sum of Alloc instances' length which .bt == this.
+  size_t alloc_bytes;
+
+  // |next| has a dual purpose. When the entry is used (hence in the hashtable),
+  // this is a ptr to the next item in the same bucket. When the entry is free,
+  // this is a ptr to the next entry in the freelist.
+  struct StacktraceEntry* next;
+} StacktraceEntry;
+
+// Represents a contiguous range of virtual memory which has been allocated by
+// a give code path (identified by the corresponding StacktraceEntry).
+typedef struct Alloc {
+  RB_ENTRY(Alloc) rb_node;  // Anchor for the RB-tree;
+  uintptr_t start;
+  uintptr_t end;
+  uint32_t flags;       // See HEAP_PROFILER_FLAGS_*.
+  StacktraceEntry* st;  // NULL == free entry.
+  struct Alloc* next_free;
+} Alloc;
+
+typedef struct {
+  uint32_t magic_start;       // The magic marker used to locate the stats mmap.
+  uint32_t num_allocs;        // The total number of allocation entries present.
+  uint32_t max_allocs;        // The max number of items in |allocs|.
+  uint32_t num_stack_traces;  // The total number of stack traces present.
+  uint32_t max_stack_traces;  // The max number of items in |stack_traces|.
+  size_t total_alloc_bytes;   // Total allocation bytes tracked.
+  Alloc* allocs;              // Start of the the Alloc pool.
+  StacktraceEntry* stack_traces;  // Start of the StacktraceEntry pool.
+} HeapStats;
+
+// Initialize the heap_profiler. The caller has to allocate the HeapStats
+// "superblock", since the way it is mapped is platform-specific.
+void heap_profiler_init(HeapStats* heap_stats);
+
+// Records and allocation. The caller must unwind the stack and pass the
+// frames array. Flags are optionals and don't affect the behavior of the
+// library (they're just kept along and dumped).
+void heap_profiler_alloc(void* addr,
+                         size_t size,
+                         uintptr_t* frames,
+                         uint32_t depth,
+                         uint32_t flags);
+
+// Frees any allocation (even partial) overlapping with the given range.
+// If old_flags != NULL, it will be filled with the flags of the deleted allocs.
+void heap_profiler_free(void* addr, size_t size, uint32_t* old_flags);
+
+// Cleans up the HeapStats and all the internal data structures.
+void heap_profiler_cleanup(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // TOOLS_ANDROID_HEAP_PROFILER_HEAP_PROFILER_H_
diff --git a/tools/android/heap_profiler/heap_profiler_hooks_android.c b/tools/android/heap_profiler/heap_profiler_hooks_android.c
new file mode 100644
index 0000000..1480780
--- /dev/null
+++ b/tools/android/heap_profiler/heap_profiler_hooks_android.c
@@ -0,0 +1,209 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <unwind.h>
+
+#include "tools/android/heap_profiler/heap_profiler.h"
+
+#define HEAP_PROFILER_EXPORT __attribute__((visibility("default")))
+
+
+static inline __attribute__((always_inline))
+uint32_t get_backtrace(uintptr_t* frames, uint32_t max_depth);
+
+// Function pointers typedefs for the hooked symbols.
+typedef void* (*mmap_t)(void*, size_t, int, int, int, off_t);
+typedef void* (*mmap2_t)(void*, size_t, int, int, int, off_t);
+typedef void* (*mmap64_t)(void*, size_t, int, int, int, off64_t);
+typedef void* (*mremap_t)(void*, size_t, size_t, unsigned long);
+typedef int (*munmap_t)(void*, size_t);
+typedef void* (*malloc_t)(size_t);
+typedef void* (*calloc_t)(size_t, size_t);
+typedef void* (*realloc_t)(void*, size_t);
+typedef void (*free_t)(void*);
+
+// And their actual definitions.
+static mmap_t real_mmap;
+static mmap2_t real_mmap2;
+static mmap64_t real_mmap64;
+static mremap_t real_mremap;
+static munmap_t real_munmap;
+static malloc_t real_malloc;
+static calloc_t real_calloc;
+static realloc_t real_realloc;
+static free_t real_free;
+static int* has_forked_off_zygote;
+
+HEAP_PROFILER_EXPORT const HeapStats* heap_profiler_stats_for_tests;
+
+// +---------------------------------------------------------------------------+
+// + Initialization of heap_profiler and lookup of hooks' addresses            +
+// +---------------------------------------------------------------------------+
+__attribute__((constructor))
+static void initialize() {
+  real_mmap = (mmap_t) dlsym(RTLD_NEXT, "mmap");
+  real_mmap2 = (mmap_t) dlsym(RTLD_NEXT, "mmap2");
+  real_mmap64 = (mmap64_t) dlsym(RTLD_NEXT, "mmap64");
+  real_mremap = (mremap_t) dlsym(RTLD_NEXT, "mremap");
+  real_munmap = (munmap_t) dlsym(RTLD_NEXT, "munmap");
+  real_malloc = (malloc_t) dlsym(RTLD_NEXT, "malloc");
+  real_calloc = (calloc_t) dlsym(RTLD_NEXT, "calloc");
+  real_realloc = (realloc_t) dlsym(RTLD_NEXT, "realloc");
+  real_free = (free_t) dlsym(RTLD_NEXT, "free");
+
+  // gMallocLeakZygoteChild is an extra useful piece of information to have.
+  // When available, it tells whether we're in the zygote (=0) or forked (=1)
+  // a child off it. In the worst case it will be NULL and we'll just ignore it.
+  has_forked_off_zygote = (int*) dlsym(RTLD_NEXT, "gMallocLeakZygoteChild");
+
+  // Allocate room for the HeapStats area and initialize the heap profiler.
+  // Make an explicit map of /dev/zero (instead of MAP_ANONYMOUS), so that the
+  // heap_dump tool can easily spot the mapping in the target process.
+  int fd = open("/dev/zero", O_RDONLY);
+  if (fd < 0) {
+    abort();  // This world has gone wrong. Good night Vienna.
+  }
+
+  HeapStats* stats = (HeapStats*) real_mmap(
+      0, sizeof(HeapStats), PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+  heap_profiler_stats_for_tests = stats;
+  heap_profiler_init(stats);
+}
+
+static inline __attribute__((always_inline)) void unwind_and_record_alloc(
+    void* start, size_t size, uint32_t flags) {
+  const int errno_save = errno;
+  uintptr_t frames[HEAP_PROFILER_MAX_DEPTH];
+  const uint32_t depth = get_backtrace(frames, HEAP_PROFILER_MAX_DEPTH);
+  if (has_forked_off_zygote != NULL && *has_forked_off_zygote == 0)
+    flags |= HEAP_PROFILER_FLAGS_IN_ZYGOTE;
+  heap_profiler_alloc(start, size, frames, depth, flags);
+  errno = errno_save;
+}
+
+static inline __attribute__((always_inline)) void discard_alloc(
+    void* start, size_t size, uint32_t* old_flags) {
+  const int errno_save = errno;
+  heap_profiler_free(start, size, old_flags);
+  errno = errno_save;
+}
+
+// Flags are non-functional extra decorators that are made available to the
+// final heap_dump tool, to get more details about the source of the allocation.
+static uint32_t get_flags_for_mmap(int fd) {
+  return HEAP_PROFILER_FLAGS_MMAP | (fd ? HEAP_PROFILER_FLAGS_MMAP_FILE : 0);
+}
+
+// +---------------------------------------------------------------------------+
+// + Actual mmap/malloc hooks                                                  +
+// +---------------------------------------------------------------------------+
+HEAP_PROFILER_EXPORT void* mmap(
+    void* addr, size_t size, int prot, int flags, int fd, off_t offset)  {
+  void* ret = real_mmap(addr, size, prot, flags, fd, offset);
+  if (ret != MAP_FAILED)
+    unwind_and_record_alloc(ret, size, get_flags_for_mmap(fd));
+  return ret;
+}
+
+HEAP_PROFILER_EXPORT void* mmap2(
+    void* addr, size_t size, int prot, int flags, int fd, off_t pgoffset)  {
+  void* ret = real_mmap2(addr, size, prot, flags, fd, pgoffset);
+  if (ret != MAP_FAILED)
+    unwind_and_record_alloc(ret, size, get_flags_for_mmap(fd));
+  return ret;
+}
+
+HEAP_PROFILER_EXPORT void* mmap64(
+    void* addr, size_t size, int prot, int flags, int fd, off64_t offset) {
+  void* ret = real_mmap64(addr, size, prot, flags, fd, offset);
+  if (ret != MAP_FAILED)
+    unwind_and_record_alloc(ret, size, get_flags_for_mmap(fd));
+  return ret;
+}
+
+HEAP_PROFILER_EXPORT void* mremap(
+    void* addr, size_t oldlen, size_t newlen, unsigned long flags) {
+  void* ret = real_mremap(addr, oldlen, newlen, flags);
+  if (ret != MAP_FAILED) {
+    uint32_t flags = 0;
+    if (addr)
+      discard_alloc(addr, oldlen, &flags);
+    if (newlen > 0)
+      unwind_and_record_alloc(ret, newlen, flags);
+  }
+  return ret;
+}
+
+HEAP_PROFILER_EXPORT int munmap(void* ptr, size_t size) {
+  int ret = real_munmap(ptr, size);
+  discard_alloc(ptr, size, /*old_flags=*/NULL);
+  return ret;
+}
+
+HEAP_PROFILER_EXPORT void* malloc(size_t byte_count) {
+  void* ret = real_malloc(byte_count);
+  if (ret != NULL)
+    unwind_and_record_alloc(ret, byte_count, HEAP_PROFILER_FLAGS_MALLOC);
+  return ret;
+}
+
+HEAP_PROFILER_EXPORT void* calloc(size_t nmemb, size_t size) {
+  void* ret = real_calloc(nmemb, size);
+  if (ret != NULL)
+    unwind_and_record_alloc(ret, nmemb * size, HEAP_PROFILER_FLAGS_MALLOC);
+  return ret;
+}
+
+HEAP_PROFILER_EXPORT void* realloc(void* ptr, size_t size) {
+  void* ret = real_realloc(ptr, size);
+  uint32_t flags = 0;
+  if (ptr)
+    discard_alloc(ptr, 0, &flags);
+  if (ret != NULL)
+    unwind_and_record_alloc(ret, size, flags | HEAP_PROFILER_FLAGS_MALLOC);
+  return ret;
+}
+
+HEAP_PROFILER_EXPORT void free(void* ptr) {
+  real_free(ptr);
+  discard_alloc(ptr, 0, /*old_flags=*/NULL);
+}
+
+// +---------------------------------------------------------------------------+
+// + Stack unwinder                                                            +
+// +---------------------------------------------------------------------------+
+typedef struct {
+  uintptr_t* frames;
+  uint32_t frame_count;
+  uint32_t max_depth;
+  bool have_skipped_self;
+} stack_crawl_state_t;
+
+static _Unwind_Reason_Code unwind_fn(struct _Unwind_Context* ctx, void* arg) {
+  stack_crawl_state_t* state = (stack_crawl_state_t*) arg;
+  uintptr_t ip = _Unwind_GetIP(ctx);
+
+  if (ip != 0 && !state->have_skipped_self) {
+    state->have_skipped_self = true;
+    return _URC_NO_REASON;
+  }
+
+  state->frames[state->frame_count++] = ip;
+  return (state->frame_count >= state->max_depth) ?
+          _URC_END_OF_STACK : _URC_NO_REASON;
+}
+
+static uint32_t get_backtrace(uintptr_t* frames, uint32_t max_depth) {
+  stack_crawl_state_t state = {.frames = frames, .max_depth = max_depth};
+  _Unwind_Backtrace(unwind_fn, &state);
+  return state.frame_count;
+}
diff --git a/tools/android/heap_profiler/heap_profiler_integrationtest.cc b/tools/android/heap_profiler/heap_profiler_integrationtest.cc
new file mode 100644
index 0000000..ba7ebea
--- /dev/null
+++ b/tools/android/heap_profiler/heap_profiler_integrationtest.cc
@@ -0,0 +1,179 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <map>
+
+#include "base/compiler_specific.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/android/heap_profiler/heap_profiler.h"
+
+namespace {
+
+typedef void* (*AllocatorFn)(size_t);
+typedef int (*FreeFn)(void*, size_t);
+
+const size_t kSize1 = 499 * PAGE_SIZE;
+const size_t kSize2 = 503 * PAGE_SIZE;
+const size_t kSize3 = 509 * PAGE_SIZE;
+
+// The purpose of the four functions below is to create watermarked allocations,
+// so the test fixture can ascertain that the hooks work end-to-end.
+__attribute__((noinline)) void* MallocInner(size_t size) {
+  void* ptr = malloc(size);
+  // The memset below is to avoid tail-call elimination optimizations and ensure
+  // that this function will be part of the stack trace.
+  memset(ptr, 0, size);
+  return ptr;
+}
+
+__attribute__((noinline)) void* MallocOuter(size_t size) {
+  void* ptr = MallocInner(size);
+  memset(ptr, 0, size);
+  return ptr;
+}
+
+__attribute__((noinline)) void* DoMmap(size_t size) {
+  return mmap(
+      0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+}
+
+__attribute__((noinline)) void* MmapInner(size_t size) {
+  void* ptr = DoMmap(size);
+  memset(ptr, 0, size);
+  return ptr;
+}
+
+__attribute__((noinline)) void* MmapOuter(size_t size) {
+  void* ptr = MmapInner(size);
+  memset(ptr, 0, size);
+  return ptr;
+}
+
+const HeapStats* GetHeapStats() {
+  HeapStats* const* stats_ptr = reinterpret_cast<HeapStats* const*>(
+      dlsym(RTLD_DEFAULT, "heap_profiler_stats_for_tests"));
+  EXPECT_TRUE(stats_ptr != NULL);
+  const HeapStats* stats = *stats_ptr;
+  EXPECT_TRUE(stats != NULL);
+  EXPECT_EQ(HEAP_PROFILER_MAGIC_MARKER, stats->magic_start);
+  return stats;
+}
+
+bool StackTraceContains(const StacktraceEntry* s, AllocatorFn fn) {
+  // kExpectedFnLen is a gross estimation of the watermark functions' size.
+  // It tries to address the following problem: the addrs in the unwound stack
+  // stack frames will NOT point to the beginning of the functions, but to the
+  // PC after the call to malloc/mmap.
+  const size_t kExpectedFnLen = 16;
+  const uintptr_t fn_addr = reinterpret_cast<uintptr_t>(fn);
+  for (size_t i = 0; i < HEAP_PROFILER_MAX_DEPTH; ++i) {
+    if (s->frames[i] >= fn_addr && s->frames[i] <= fn_addr + kExpectedFnLen)
+      return true;
+  }
+  return false;
+}
+
+const StacktraceEntry* LookupStackTrace(size_t size, AllocatorFn fn) {
+  const HeapStats* stats = GetHeapStats();
+  for (size_t i = 0; i < stats->max_stack_traces; ++i) {
+    const StacktraceEntry* st = &stats->stack_traces[i];
+    if (st->alloc_bytes == size && StackTraceContains(st, fn))
+      return st;
+  }
+  return NULL;
+}
+
+int DoFree(void* addr, size_t /*size, ignored.*/) {
+  free(addr);
+  return 0;
+}
+
+void TestStackTracesWithParams(AllocatorFn outer_fn,
+                               AllocatorFn inner_fn,
+                               FreeFn free_fn) {
+  const HeapStats* stats = GetHeapStats();
+
+  void* m1 = outer_fn(kSize1);
+  void* m2 = inner_fn(kSize2);
+  void* m3 = inner_fn(kSize3);
+  free_fn(m3, kSize3);
+
+  const StacktraceEntry* st1 = LookupStackTrace(kSize1, inner_fn);
+  const StacktraceEntry* st2 = LookupStackTrace(kSize2, inner_fn);
+  const StacktraceEntry* st3 = LookupStackTrace(kSize3, inner_fn);
+
+  EXPECT_TRUE(st1 != NULL);
+  EXPECT_TRUE(StackTraceContains(st1, outer_fn));
+  EXPECT_TRUE(StackTraceContains(st1, inner_fn));
+
+  EXPECT_TRUE(st2 != NULL);
+  EXPECT_FALSE(StackTraceContains(st2, outer_fn));
+  EXPECT_TRUE(StackTraceContains(st2, inner_fn));
+
+  EXPECT_EQ(NULL, st3);
+
+  const size_t total_alloc_start = stats->total_alloc_bytes;
+  const size_t num_stack_traces_start = stats->num_stack_traces;
+
+  free_fn(m1, kSize1);
+  free_fn(m2, kSize2);
+
+  const size_t total_alloc_end = stats->total_alloc_bytes;
+  const size_t num_stack_traces_end = stats->num_stack_traces;
+
+  EXPECT_EQ(kSize1 + kSize2, total_alloc_start - total_alloc_end);
+  EXPECT_EQ(2, num_stack_traces_start - num_stack_traces_end);
+  EXPECT_EQ(NULL, LookupStackTrace(kSize1, inner_fn));
+  EXPECT_EQ(NULL, LookupStackTrace(kSize2, inner_fn));
+  EXPECT_EQ(NULL, LookupStackTrace(kSize3, inner_fn));
+}
+
+TEST(HeapProfilerIntegrationTest, TestMallocStackTraces) {
+  TestStackTracesWithParams(&MallocOuter, &MallocInner, &DoFree);
+}
+
+TEST(HeapProfilerIntegrationTest, TestMmapStackTraces) {
+  TestStackTracesWithParams(&MmapOuter, &MmapInner, &munmap);
+}
+
+// Returns the path of the directory containing the current executable.
+std::string GetExePath() {
+  char buf[1024];
+  ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
+  if (len == -1)
+    return std::string();
+  std::string path(buf, len);
+  size_t sep = path.find_last_of('/');
+  if (sep == std::string::npos)
+    return std::string();
+  path.erase(sep);
+  return path;
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  // Re-launch the process itself forcing the preload of the libheap_profiler.
+  char* ld_preload = getenv("LD_PRELOAD");
+  if (ld_preload == NULL || strstr(ld_preload, "libheap_profiler.so") == NULL) {
+    char env_ld_lib_path[256];
+    strlcpy(env_ld_lib_path, "LD_LIBRARY_PATH=", sizeof(env_ld_lib_path));
+    strlcat(env_ld_lib_path, GetExePath().c_str(), sizeof(env_ld_lib_path));
+    char env_ld_preload[] = "LD_PRELOAD=libheap_profiler.so";
+    char* const env[] = {env_ld_preload, env_ld_lib_path, 0};
+    execve("/proc/self/exe", argv, env);
+    // execve() never returns, unless something goes wrong.
+    perror("execve");
+    assert(false);
+  }
+
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/tools/android/heap_profiler/heap_profiler_unittest.cc b/tools/android/heap_profiler/heap_profiler_unittest.cc
new file mode 100644
index 0000000..65c2700
--- /dev/null
+++ b/tools/android/heap_profiler/heap_profiler_unittest.cc
@@ -0,0 +1,458 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+#include <string.h>
+#include <map>
+
+#include "base/compiler_specific.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/android/heap_profiler/heap_profiler.h"
+
+namespace {
+
+class HeapProfilerTest : public testing::Test {
+ public:
+  virtual void SetUp() OVERRIDE { heap_profiler_init(&stats_); }
+
+  virtual void TearDown() OVERRIDE {
+    CheckAllocVsStacktaceConsistency();
+    heap_profiler_cleanup();
+  }
+
+ protected:
+  struct StackTrace {
+    uintptr_t frames[HEAP_PROFILER_MAX_DEPTH];
+    size_t depth;
+  };
+
+  StackTrace GenStackTrace(size_t depth, uintptr_t base) {
+    assert(depth <= HEAP_PROFILER_MAX_DEPTH);
+    StackTrace st;
+    for (size_t i = 0; i < depth; ++i)
+      st.frames[i] = base + i * 0x10UL;
+    st.depth = depth;
+    return st;
+  }
+
+  void ExpectAlloc(uintptr_t start,
+                   uintptr_t end,
+                   const StackTrace& st,
+                   uint32_t flags) {
+    for (uint32_t i = 0; i < stats_.max_allocs; ++i) {
+      const Alloc& alloc = stats_.allocs[i];
+      if (alloc.start != start || alloc.end != end)
+        continue;
+      // Check that the stack trace match.
+      for (uint32_t j = 0; j < st.depth; ++j) {
+        EXPECT_EQ(st.frames[j], alloc.st->frames[j])
+            << "Stacktrace not matching @ depth " << j;
+      }
+      EXPECT_EQ(flags, alloc.flags);
+      return;
+    }
+
+    FAIL() << "Alloc not found [" << std::hex << start << "," << end << "]";
+  }
+
+  void CheckAllocVsStacktaceConsistency() {
+    uint32_t allocs_seen = 0;
+    uint32_t stack_traces_seen = 0;
+    std::map<StacktraceEntry*, uintptr_t> stacktrace_bytes_by_alloc;
+
+    for (uint32_t i = 0; i < stats_.max_allocs; ++i) {
+      Alloc* alloc = &stats_.allocs[i];
+      if (alloc->start == 0 && alloc->end == 0)
+        continue;
+      ++allocs_seen;
+      stacktrace_bytes_by_alloc[alloc->st] += alloc->end - alloc->start + 1;
+    }
+
+    for (uint32_t i = 0; i < stats_.max_stack_traces; ++i) {
+      StacktraceEntry* st = &stats_.stack_traces[i];
+      if (st->alloc_bytes == 0)
+        continue;
+      ++stack_traces_seen;
+      EXPECT_EQ(1, stacktrace_bytes_by_alloc.count(st));
+      EXPECT_EQ(stacktrace_bytes_by_alloc[st], st->alloc_bytes);
+    }
+
+    EXPECT_EQ(allocs_seen, stats_.num_allocs);
+    EXPECT_EQ(stack_traces_seen, stats_.num_stack_traces);
+  }
+
+  HeapStats stats_;
+};
+
+TEST_F(HeapProfilerTest, SimpleAlloc) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  heap_profiler_alloc((void*)0x1000, 1024, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 2048, st1.frames, st1.depth, 0);
+
+  EXPECT_EQ(2, stats_.num_allocs);
+  EXPECT_EQ(1, stats_.num_stack_traces);
+  EXPECT_EQ(1024 + 2048, stats_.total_alloc_bytes);
+  ExpectAlloc(0x1000, 0x13ff, st1, 0);
+  ExpectAlloc(0x2000, 0x27ff, st1, 0);
+}
+
+TEST_F(HeapProfilerTest, AllocMultipleStacks) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(4, 0x1000);
+  heap_profiler_alloc((void*)0x1000, 1024, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 2048, st2.frames, st2.depth, 0);
+  heap_profiler_alloc((void*)0x3000, 32, st1.frames, st1.depth, 0);
+
+  EXPECT_EQ(3, stats_.num_allocs);
+  EXPECT_EQ(2, stats_.num_stack_traces);
+  EXPECT_EQ(1024 + 2048 + 32, stats_.total_alloc_bytes);
+  ExpectAlloc(0x1000, 0x13ff, st1, 0);
+  ExpectAlloc(0x2000, 0x27ff, st2, 0);
+  ExpectAlloc(0x3000, 0x301f, st1, 0);
+}
+
+TEST_F(HeapProfilerTest, SimpleAllocAndFree) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  heap_profiler_alloc((void*)0x1000, 1024, st1.frames, st1.depth, 0);
+  heap_profiler_free((void*)0x1000, 1024, NULL);
+
+  EXPECT_EQ(0, stats_.num_allocs);
+  EXPECT_EQ(0, stats_.num_stack_traces);
+  EXPECT_EQ(0, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, Realloc) {
+  StackTrace st1 = GenStackTrace(8, 0);
+  heap_profiler_alloc((void*)0, 32, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0, 32, st1.frames, st1.depth, 0);
+}
+
+TEST_F(HeapProfilerTest, AllocAndFreeMultipleStacks) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(6, 0x1000);
+  heap_profiler_alloc((void*)0x1000, 1024, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 2048, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x3000, 32, st2.frames, st2.depth, 0);
+  heap_profiler_alloc((void*)0x4000, 64, st2.frames, st2.depth, 0);
+
+  heap_profiler_free((void*)0x1000, 1024, NULL);
+  heap_profiler_free((void*)0x3000, 32, NULL);
+
+  EXPECT_EQ(2, stats_.num_allocs);
+  EXPECT_EQ(2, stats_.num_stack_traces);
+  EXPECT_EQ(2048 + 64, stats_.total_alloc_bytes);
+  ExpectAlloc(0x2000, 0x27ff, st1, 0);
+  ExpectAlloc(0x4000, 0x403f, st2, 0);
+}
+
+TEST_F(HeapProfilerTest, AllocAndFreeAll) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(6, 0x1000);
+  heap_profiler_alloc((void*)0x1000, 1024, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 2048, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x3000, 32, st2.frames, st2.depth, 0);
+  heap_profiler_alloc((void*)0x4000, 64, st2.frames, st2.depth, 0);
+
+  heap_profiler_free((void*)0x1000, 1024, NULL);
+  heap_profiler_free((void*)0x2000, 2048, NULL);
+  heap_profiler_free((void*)0x3000, 32, NULL);
+  heap_profiler_free((void*)0x4000, 64, NULL);
+
+  EXPECT_EQ(0, stats_.num_allocs);
+  EXPECT_EQ(0, stats_.num_stack_traces);
+  EXPECT_EQ(0, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, AllocAndFreeWithZeroSize) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(6, 0x1000);
+  heap_profiler_alloc((void*)0x1000, 1024, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 2048, st2.frames, st2.depth, 0);
+
+  heap_profiler_free((void*)0x1000, 0, NULL);
+
+  EXPECT_EQ(1, stats_.num_allocs);
+  EXPECT_EQ(1, stats_.num_stack_traces);
+  EXPECT_EQ(2048, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, AllocAndFreeContiguous) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(6, 0x1000);
+  heap_profiler_alloc((void*)0x1000, 4096, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 4096, st2.frames, st2.depth, 0);
+
+  heap_profiler_free((void*)0x1000, 8192, NULL);
+
+  EXPECT_EQ(0, stats_.num_allocs);
+  EXPECT_EQ(0, stats_.num_stack_traces);
+  EXPECT_EQ(0, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, SparseAllocsOneLargeOuterFree) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(6, 0x1000);
+
+  heap_profiler_alloc((void*)0x1010, 1, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x1400, 2, st2.frames, st2.depth, 0);
+  heap_profiler_alloc((void*)0x1600, 5, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x9000, 4096, st2.frames, st2.depth, 0);
+
+  heap_profiler_free((void*)0x0800, 8192, NULL);
+  EXPECT_EQ(1, stats_.num_allocs);
+  EXPECT_EQ(1, stats_.num_stack_traces);
+  EXPECT_EQ(4096, stats_.total_alloc_bytes);
+  ExpectAlloc(0x9000, 0x9fff, st2, 0);
+}
+
+TEST_F(HeapProfilerTest, SparseAllocsOneLargePartiallyOverlappingFree) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(6, 0x1000);
+  StackTrace st3 = GenStackTrace(4, 0x2000);
+
+  // This will be untouched.
+  heap_profiler_alloc((void*)0x1000, 1024, st1.frames, st1.depth, 0);
+
+  // These will be partially freed in one shot (% 64 a bytes "margin").
+  heap_profiler_alloc((void*)0x2000, 128, st2.frames, st2.depth, 0);
+  heap_profiler_alloc((void*)0x2400, 128, st2.frames, st2.depth, 0);
+  heap_profiler_alloc((void*)0x2f80, 128, st2.frames, st2.depth, 0);
+
+  // This will be untouched.
+  heap_profiler_alloc((void*)0x3000, 1024, st3.frames, st3.depth, 0);
+
+  heap_profiler_free((void*)0x2040, 4096 - 64 - 64, NULL);
+  EXPECT_EQ(4, stats_.num_allocs);
+  EXPECT_EQ(3, stats_.num_stack_traces);
+  EXPECT_EQ(1024 + 64 + 64 + 1024, stats_.total_alloc_bytes);
+
+  ExpectAlloc(0x1000, 0x13ff, st1, 0);
+  ExpectAlloc(0x2000, 0x203f, st2, 0);
+  ExpectAlloc(0x2fc0, 0x2fff, st2, 0);
+  ExpectAlloc(0x3000, 0x33ff, st3, 0);
+}
+
+TEST_F(HeapProfilerTest, AllocAndFreeScattered) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  heap_profiler_alloc((void*)0x1000, 4096, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 4096, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x3000, 4096, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x4000, 4096, st1.frames, st1.depth, 0);
+
+  heap_profiler_free((void*)0x800, 4096, NULL);
+  EXPECT_EQ(4, stats_.num_allocs);
+  EXPECT_EQ(2048 + 4096 + 4096 + 4096, stats_.total_alloc_bytes);
+
+  heap_profiler_free((void*)0x1800, 4096, NULL);
+  EXPECT_EQ(3, stats_.num_allocs);
+  EXPECT_EQ(2048 + 4096 + 4096, stats_.total_alloc_bytes);
+
+  heap_profiler_free((void*)0x2800, 4096, NULL);
+  EXPECT_EQ(2, stats_.num_allocs);
+  EXPECT_EQ(2048 + 4096, stats_.total_alloc_bytes);
+
+  heap_profiler_free((void*)0x3800, 4096, NULL);
+  EXPECT_EQ(1, stats_.num_allocs);
+  EXPECT_EQ(2048, stats_.total_alloc_bytes);
+
+  heap_profiler_free((void*)0x4800, 4096, NULL);
+  EXPECT_EQ(0, stats_.num_allocs);
+  EXPECT_EQ(0, stats_.num_stack_traces);
+  EXPECT_EQ(0, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, AllocAndOverFreeContiguous) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(6, 0x1000);
+  heap_profiler_alloc((void*)0x1000, 4096, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 4096, st2.frames, st2.depth, 0);
+
+  heap_profiler_free((void*)0, 16834, NULL);
+
+  EXPECT_EQ(0, stats_.num_allocs);
+  EXPECT_EQ(0, stats_.num_stack_traces);
+  EXPECT_EQ(0, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, AllocContiguousAndPunchHole) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(6, 0x1000);
+  heap_profiler_alloc((void*)0x1000, 4096, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 4096, st2.frames, st2.depth, 0);
+
+  // Punch a 4k hole in the middle of the two contiguous 4k allocs.
+  heap_profiler_free((void*)0x1800, 4096, NULL);
+
+  EXPECT_EQ(2, stats_.num_allocs);
+  EXPECT_EQ(2, stats_.num_stack_traces);
+  EXPECT_EQ(4096, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, AllocAndPartialFree) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(6, 0x1000);
+  StackTrace st3 = GenStackTrace(7, 0x2000);
+  StackTrace st4 = GenStackTrace(9, 0x3000);
+  heap_profiler_alloc((void*)0x1000, 1024, st1.frames, st1.depth, 0);
+  heap_profiler_alloc((void*)0x2000, 1024, st2.frames, st2.depth, 0);
+  heap_profiler_alloc((void*)0x3000, 1024, st3.frames, st3.depth, 0);
+  heap_profiler_alloc((void*)0x4000, 1024, st4.frames, st4.depth, 0);
+
+  heap_profiler_free((void*)0x1000, 128, NULL);  // Shrink left by 128B.
+  heap_profiler_free((void*)0x2380, 128, NULL);  // Shrink right by 128B.
+  heap_profiler_free((void*)0x3100, 512, NULL);  // 512B hole in the middle.
+  heap_profiler_free((void*)0x4000, 512, NULL);  // Free up the 4th alloc...
+  heap_profiler_free((void*)0x4200, 512, NULL);  // ...but do it in two halves.
+
+  EXPECT_EQ(4, stats_.num_allocs);  // 1 + 2 + two sides around the hole 3.
+  EXPECT_EQ(3, stats_.num_stack_traces);  // st4 should be gone.
+  EXPECT_EQ(896 + 896 + 512, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, RandomIndividualAllocAndFrees) {
+  static const size_t NUM_ST = 128;
+  static const size_t NUM_OPS = 1000;
+
+  StackTrace st[NUM_ST];
+  for (uint32_t i = 0; i < NUM_ST; ++i)
+    st[i] = GenStackTrace((i % 10) + 2, i * 128);
+
+  for (size_t i = 0; i < NUM_OPS; ++i) {
+    uintptr_t start = ((i + 7) << 8) & (0xffffff);
+    size_t size = (start >> 16) & 0x0fff;
+    if (i & 1) {
+      StackTrace* s = &st[start % NUM_ST];
+      heap_profiler_alloc((void*)start, size, s->frames, s->depth, 0);
+    } else {
+      heap_profiler_free((void*)start, size, NULL);
+    }
+    CheckAllocVsStacktaceConsistency();
+  }
+}
+
+TEST_F(HeapProfilerTest, RandomAllocAndFreesBatches) {
+  static const size_t NUM_ST = 128;
+  static const size_t NUM_ALLOCS = 100;
+
+  StackTrace st[NUM_ST];
+  for (size_t i = 0; i < NUM_ST; ++i)
+    st[i] = GenStackTrace((i % 10) + 2, i * NUM_ST);
+
+  for (int repeat = 0; repeat < 5; ++repeat) {
+    for (size_t i = 0; i < NUM_ALLOCS; ++i) {
+      StackTrace* s = &st[i % NUM_ST];
+      heap_profiler_alloc(
+          (void*)(i * 4096), ((i + 1) * 32) % 4097, s->frames, s->depth, 0);
+      CheckAllocVsStacktaceConsistency();
+    }
+
+    for (size_t i = 0; i < NUM_ALLOCS; ++i) {
+      heap_profiler_free((void*)(i * 1024), ((i + 1) * 64) % 16000, NULL);
+      CheckAllocVsStacktaceConsistency();
+    }
+  }
+}
+
+TEST_F(HeapProfilerTest, UnwindStackTooLargeShouldSaturate) {
+  StackTrace st1 = GenStackTrace(HEAP_PROFILER_MAX_DEPTH, 0x0);
+  uintptr_t many_frames[100] = {};
+  memcpy(many_frames, st1.frames, sizeof(uintptr_t) * st1.depth);
+  heap_profiler_alloc((void*)0x1000, 1024, many_frames, 100, 0);
+  ExpectAlloc(0x1000, 0x13ff, st1, 0);
+}
+
+TEST_F(HeapProfilerTest, NoUnwindShouldNotCrashButNoop) {
+  heap_profiler_alloc((void*)0x1000, 1024, NULL, 0, 0);
+  EXPECT_EQ(0, stats_.num_allocs);
+  EXPECT_EQ(0, stats_.num_stack_traces);
+  EXPECT_EQ(0, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, FreeNonExisting) {
+  StackTrace st1 = GenStackTrace(5, 0x0);
+  heap_profiler_free((void*)0x1000, 1024, NULL);
+  heap_profiler_free((void*)0x1400, 1024, NULL);
+  EXPECT_EQ(0, stats_.num_allocs);
+  EXPECT_EQ(0, stats_.num_stack_traces);
+  EXPECT_EQ(0, stats_.total_alloc_bytes);
+  heap_profiler_alloc((void*)0x1000, 1024, st1.frames, st1.depth, 0);
+  EXPECT_EQ(1, stats_.num_allocs);
+  EXPECT_EQ(1024, stats_.total_alloc_bytes);
+}
+
+TEST_F(HeapProfilerTest, FlagsConsistency) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  uint32_t flags = 0;
+  heap_profiler_alloc((void*)0x1000, 4096, st1.frames, st1.depth, 42);
+  heap_profiler_alloc((void*)0x2000, 4096, st1.frames, st1.depth, 142);
+
+  ExpectAlloc(0x1000, 0x1fff, st1, 42);
+  ExpectAlloc(0x2000, 0x2fff, st1, 142);
+
+  // Punch a 4k hole in the middle of the two contiguous 4k allocs.
+  heap_profiler_free((void*)0x1800, 4096, NULL);
+
+  ExpectAlloc(0x1000, 0x17ff, st1, 42);
+  heap_profiler_free((void*)0x1000, 2048, &flags);
+  EXPECT_EQ(42, flags);
+
+  ExpectAlloc(0x2800, 0x2fff, st1, 142);
+  heap_profiler_free((void*)0x2800, 2048, &flags);
+  EXPECT_EQ(142, flags);
+}
+
+TEST_F(HeapProfilerTest, BeConsistentOnOOM) {
+  static const size_t NUM_ALLOCS = 512 * 1024;
+  uintptr_t frames[1];
+
+  for (uintptr_t i = 0; i < NUM_ALLOCS; ++i) {
+    frames[0] = i;
+    heap_profiler_alloc((void*)(i * 32), 32, frames, 1, 0);
+  }
+
+  CheckAllocVsStacktaceConsistency();
+  // Check that we're saturating, otherwise this entire test is pointless.
+  EXPECT_LT(stats_.num_allocs, NUM_ALLOCS);
+  EXPECT_LT(stats_.num_stack_traces, NUM_ALLOCS);
+
+  for (uintptr_t i = 0; i < NUM_ALLOCS; ++i)
+    heap_profiler_free((void*)(i * 32), 32, NULL);
+
+  EXPECT_EQ(0, stats_.num_allocs);
+  EXPECT_EQ(0, stats_.total_alloc_bytes);
+  EXPECT_EQ(0, stats_.num_stack_traces);
+}
+
+#ifdef __LP64__
+TEST_F(HeapProfilerTest, Test64Bit) {
+  StackTrace st1 = GenStackTrace(8, 0x0);
+  StackTrace st2 = GenStackTrace(10, 0x7fffffff70000000L);
+  StackTrace st3 = GenStackTrace(10, 0xffffffff70000000L);
+  heap_profiler_alloc((void*)0x1000, 4096, st1.frames, st1.depth, 0);
+  heap_profiler_alloc(
+      (void*)0x7ffffffffffff000L, 4096, st2.frames, st2.depth, 0);
+  heap_profiler_alloc(
+      (void*)0xfffffffffffff000L, 4096, st3.frames, st3.depth, 0);
+  EXPECT_EQ(3, stats_.num_allocs);
+  EXPECT_EQ(3, stats_.num_stack_traces);
+  EXPECT_EQ(4096 + 4096 + 4096, stats_.total_alloc_bytes);
+
+  heap_profiler_free((void*)0x1000, 4096, NULL);
+  EXPECT_EQ(2, stats_.num_allocs);
+  EXPECT_EQ(2, stats_.num_stack_traces);
+  EXPECT_EQ(4096 + 4096, stats_.total_alloc_bytes);
+
+  heap_profiler_free((void*)0x7ffffffffffff000L, 4096, NULL);
+  EXPECT_EQ(1, stats_.num_allocs);
+  EXPECT_EQ(1, stats_.num_stack_traces);
+  EXPECT_EQ(4096, stats_.total_alloc_bytes);
+
+  heap_profiler_free((void*)0xfffffffffffff000L, 4096, NULL);
+  EXPECT_EQ(0, stats_.num_allocs);
+  EXPECT_EQ(0, stats_.num_stack_traces);
+  EXPECT_EQ(0, stats_.total_alloc_bytes);
+}
+#endif
+
+}  // namespace
diff --git a/tools/android/md5sum/BUILD.gn b/tools/android/md5sum/BUILD.gn
new file mode 100644
index 0000000..5f91a88
--- /dev/null
+++ b/tools/android/md5sum/BUILD.gn
@@ -0,0 +1,53 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# GYP: //tools/android/md5sum/md5sum.gyp:md5sum
+group("md5sum") {
+  datadeps = [
+    ":md5sum_bin($host_toolchain)",
+    ":md5sum_bin($default_toolchain)",
+    ":md5sum_prepare_dist($default_toolchain)",
+    ":md5sum_copy_host($host_toolchain)",
+  ]
+  # TODO(cjhopman): Remove once group datadeps are fixed.
+  deps = datadeps
+}
+
+# GYP: //tools/android/md5sum/md5sum.gyp:md5sum_bin_device (and md5sum_bin_host)
+executable("md5sum_bin") {
+  sources = [
+    "md5sum.cc"
+  ]
+  deps = [
+    "//base"
+  ]
+
+  # TODO(GYP)
+  #'conditions': [
+  #[ 'order_profiling!=0 and OS=="android"', {
+  #'dependencies': [ '../../../tools/cygprofile/cygprofile.gyp:cygprofile', ],
+  #}],
+  #],
+}
+
+if (current_toolchain == default_toolchain) {
+  import("//build/config/android/rules.gni")
+
+  # GYP: //tools/android/md5sum/md5sum.gyp:md5sum_stripped_device_bin
+  create_native_executable_dist("md5sum_prepare_dist") {
+    dist_dir = "$root_build_dir/md5sum_dist"
+    binary = "$root_build_dir/exe.stripped/md5sum_bin"
+  }
+} else {
+  # GYP: //tools/android/md5sum/md5sum.gyp:md5sum_bin_host
+  copy("md5sum_copy_host") {
+    sources = [
+      "$root_out_dir/md5sum_bin"
+    ]
+    outputs = [
+      "$root_build_dir/md5sum_bin_host"
+    ]
+  }
+}
+
diff --git a/tools/android/md5sum/md5sum.cc b/tools/android/md5sum/md5sum.cc
new file mode 100644
index 0000000..07ce2c2
--- /dev/null
+++ b/tools/android/md5sum/md5sum.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Md5sum implementation for Android. This version handles files as well as
+// directories. Its output is sorted by file path.
+
+#include <fstream>
+#include <iostream>
+#include <set>
+#include <string>
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/md5.h"
+
+namespace {
+
+const int kBufferSize = 1024;
+
+// Returns whether |path|'s MD5 was successfully written to |digest_string|.
+bool MD5Sum(const char* path, std::string* digest_string) {
+  std::ifstream stream(path);
+  if (!stream.good()) {
+    LOG(ERROR) << "Could not open file " << path;
+    return false;
+  }
+  base::MD5Context ctx;
+  base::MD5Init(&ctx);
+  char buf[kBufferSize];
+  while (stream.good()) {
+    std::streamsize bytes_read = stream.readsome(buf, sizeof(buf));
+    if (bytes_read == 0)
+      break;
+    base::MD5Update(&ctx, base::StringPiece(buf, bytes_read));
+  }
+  if (stream.fail()) {
+    LOG(ERROR) << "Error reading file " << path;
+    return false;
+  }
+  base::MD5Digest digest;
+  base::MD5Final(&digest, &ctx);
+  *digest_string = base::MD5DigestToBase16(digest);
+  return true;
+}
+
+// Returns the set of all files contained in |files|. This handles directories
+// by walking them recursively. Excludes, .svn directories and file under them.
+std::set<std::string> MakeFileSet(const char** files) {
+  const std::string svn_dir_component = FILE_PATH_LITERAL("/.svn/");
+  std::set<std::string> file_set;
+  for (const char** file = files; *file; ++file) {
+    base::FilePath file_path(*file);
+    if (base::DirectoryExists(file_path)) {
+      base::FileEnumerator file_enumerator(
+          file_path, true /* recurse */, base::FileEnumerator::FILES);
+      for (base::FilePath child, empty;
+           (child = file_enumerator.Next()) != empty; ) {
+        // If the path contains /.svn/, ignore it.
+        if (child.value().find(svn_dir_component) == std::string::npos) {
+          child = base::MakeAbsoluteFilePath(child);
+          file_set.insert(child.value());
+        }
+      }
+    } else {
+      file_set.insert(*file);
+    }
+  }
+  return file_set;
+}
+
+}  // namespace
+
+int main(int argc, const char* argv[]) {
+  if (argc < 2) {
+    LOG(ERROR) << "Usage: md5sum <path/to/file_or_dir>...";
+    return 1;
+  }
+  const std::set<std::string> files = MakeFileSet(argv + 1);
+  bool failed = false;
+  std::string digest;
+  for (std::set<std::string>::const_iterator it = files.begin();
+       it != files.end(); ++it) {
+    if (!MD5Sum(it->c_str(), &digest))
+      failed = true;
+    base::FilePath file_path(*it);
+    std::cout << digest << "  "
+              << base::MakeAbsoluteFilePath(file_path).value() << std::endl;
+  }
+  return failed;
+}
diff --git a/tools/android/md5sum/md5sum.gyp b/tools/android/md5sum/md5sum.gyp
new file mode 100644
index 0000000..75d664e
--- /dev/null
+++ b/tools/android/md5sum/md5sum.gyp
@@ -0,0 +1,81 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      # GN: //tools/android/md5sum:md5sum
+      'target_name': 'md5sum',
+      'type': 'none',
+      'dependencies': [
+        'md5sum_stripped_device_bin',
+        'md5sum_bin_host#host',
+      ],
+      # For the component build, ensure dependent shared libraries are stripped
+      # and put alongside md5sum to simplify pushing to the device.
+      'variables': {
+         'output_dir': '<(PRODUCT_DIR)/md5sum_dist/',
+         'native_binary': '<(PRODUCT_DIR)/md5sum_bin',
+      },
+      'includes': ['../../../build/android/native_app_dependencies.gypi'],
+    },
+    {
+      # GN: //tools/android/md5sum:md5sum_bin($default_toolchain)
+      'target_name': 'md5sum_device_bin',
+      'type': 'executable',
+      'dependencies': [
+        '../../../base/base.gyp:base',
+      ],
+      'include_dirs': [
+        '../../..',
+      ],
+      'sources': [
+        'md5sum.cc',
+      ],
+      'conditions': [
+        [ 'order_profiling!=0 and OS=="android"', {
+            'dependencies': [ '../../../tools/cygprofile/cygprofile.gyp:cygprofile', ],
+        }],
+      ],
+    },
+    {
+      # GN: //tools/android/md5sum:md5sum_prepare_dist
+      'target_name': 'md5sum_stripped_device_bin',
+      'type': 'none',
+      'dependencies': [
+        'md5sum_device_bin',
+      ],
+      'actions': [
+        {
+          'action_name': 'strip_md5sum_device_bin',
+          'inputs': ['<(PRODUCT_DIR)/md5sum_device_bin'],
+          'outputs': ['<(PRODUCT_DIR)/md5sum_bin'],
+          'action': [
+            '<(android_strip)',
+            '--strip-unneeded',
+            '<@(_inputs)',
+            '-o',
+            '<@(_outputs)',
+          ],
+        },
+      ],
+    },
+    # Same binary but for the host rather than the device.
+    {
+      # GN: //tools/android/md5sum:md5sum_copy_host($default_toolchain)
+      'target_name': 'md5sum_bin_host',
+      'toolsets': ['host'],
+      'type': 'executable',
+      'dependencies': [
+        '../../../base/base.gyp:base',
+      ],
+      'include_dirs': [
+        '../../..',
+      ],
+      'sources': [
+        'md5sum.cc',
+      ],
+    },
+  ],
+}
diff --git a/tools/android/memconsumer/java/AndroidManifest.xml b/tools/android/memconsumer/java/AndroidManifest.xml
new file mode 100644
index 0000000..c7f12e4
--- /dev/null
+++ b/tools/android/memconsumer/java/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2013 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.chromium.memconsumer" android:versionCode="1"
+    android:versionName="1.0">
+
+    <application
+            android:label="MemConsumer">
+        <activity android:name=".MemConsumer" android:icon="@drawable/icon" android:launchMode="singleTop">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <service android:name="ResidentService" android:enabled="true" />
+    </application>
+
+</manifest>
diff --git a/tools/android/memconsumer/java/res/drawable/icon.png b/tools/android/memconsumer/java/res/drawable/icon.png
new file mode 100644
index 0000000..cb10c9b
--- /dev/null
+++ b/tools/android/memconsumer/java/res/drawable/icon.png
Binary files differ
diff --git a/tools/android/memconsumer/java/res/drawable/notification_icon.png b/tools/android/memconsumer/java/res/drawable/notification_icon.png
new file mode 100644
index 0000000..7fc92c3
--- /dev/null
+++ b/tools/android/memconsumer/java/res/drawable/notification_icon.png
Binary files differ
diff --git a/tools/android/memconsumer/java/src/org/chromium/memconsumer/MemConsumer.java b/tools/android/memconsumer/java/src/org/chromium/memconsumer/MemConsumer.java
new file mode 100644
index 0000000..17566f8
--- /dev/null
+++ b/tools/android/memconsumer/java/src/org/chromium/memconsumer/MemConsumer.java
@@ -0,0 +1,107 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.memconsumer;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.NumberPicker;
+import android.widget.TextView;
+
+public class MemConsumer extends Activity {
+    public static final String NOTIFICATION_ACTION =
+            MemConsumer.class.toString() + ".NOTIFICATION";
+
+    private ResidentService mResidentService;
+    private int mMemory = 0;
+    private NumberPicker mMemoryPicker;
+
+    private ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder binder) {
+            mResidentService = ((ResidentService.ServiceBinder) binder).getService();
+            mResidentService.useMemory(mMemory);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mResidentService = null;
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mMemoryPicker = new NumberPicker(this);
+        mMemoryPicker.setGravity(Gravity.CENTER);
+        mMemoryPicker.setMaxValue(Integer.MAX_VALUE);
+        mMemoryPicker.setMinValue(0);
+        mMemoryPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
+            @Override
+            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+                updateMemoryConsumption(picker.getValue());
+            }
+        });
+        for (int i = 0; i < mMemoryPicker.getChildCount(); i++) {
+            View child = mMemoryPicker.getChildAt(i);
+            if (child instanceof EditText) {
+                EditText editText = (EditText) child;
+                editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+                    @Override
+                    public boolean onEditorAction (TextView v, int actionId, KeyEvent event) {
+                        if (v.getText().length() > 0) {
+                            updateMemoryConsumption(Integer.parseInt(v.getText().toString()));
+                        }
+                        return false;
+                    }
+                });
+            }
+        }
+        setContentView(mMemoryPicker);
+        onNewIntent(getIntent());
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        if (intent.getAction() == NOTIFICATION_ACTION) {
+            updateMemoryConsumption(0);
+            return;
+        }
+        if (!intent.hasExtra("memory")) return;
+        updateMemoryConsumption(intent.getIntExtra("memory", 0));
+    }
+
+    void updateMemoryConsumption(int memory) {
+        if (memory == mMemory || memory < 0) return;
+        mMemory = memory;
+        mMemoryPicker.setValue(mMemory);
+        if (mResidentService == null) {
+            if (mMemory > 0) {
+                Intent resident = new Intent();
+                resident.setClass(this, ResidentService.class);
+                startService(resident);
+                bindService(new Intent(this, ResidentService.class),
+                            mServiceConnection,
+                            Context.BIND_AUTO_CREATE);
+            }
+        } else {
+            mResidentService.useMemory(mMemory);
+            if (mMemory == 0) {
+                unbindService(mServiceConnection);
+                stopService(new Intent(this, ResidentService.class));
+                mResidentService = null;
+            }
+        }
+    }
+}
diff --git a/tools/android/memconsumer/java/src/org/chromium/memconsumer/ResidentService.java b/tools/android/memconsumer/java/src/org/chromium/memconsumer/ResidentService.java
new file mode 100644
index 0000000..e40fbfe
--- /dev/null
+++ b/tools/android/memconsumer/java/src/org/chromium/memconsumer/ResidentService.java
@@ -0,0 +1,62 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.memconsumer;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+public class ResidentService extends Service {
+    static {
+        // Loading the native library.
+        System.loadLibrary("memconsumer");
+    }
+
+    public class ServiceBinder extends Binder {
+        ResidentService getService() {
+            return ResidentService.this;
+        }
+    }
+
+    private static final int RESIDENT_NOTIFICATION_ID = 1;
+
+    private final IBinder mBinder = new ServiceBinder();
+    private boolean mIsInForeground = false;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    public void useMemory(long memory) {
+        if (memory > 0) {
+            Intent notificationIntent = new Intent(this, MemConsumer.class);
+            notificationIntent.setAction(MemConsumer.NOTIFICATION_ACTION);
+            PendingIntent pendingIntent =
+                PendingIntent.getActivity(this, 0, notificationIntent, 0);
+            Notification notification =
+                new Notification.Builder(getApplicationContext()).
+                    setContentTitle("MC running (" + memory + "Mb)").
+                    setSmallIcon(R.drawable.notification_icon).
+                    setDeleteIntent(pendingIntent).
+                    setContentIntent(pendingIntent).
+                    build();
+            startForeground(RESIDENT_NOTIFICATION_ID, notification);
+            mIsInForeground = true;
+        }
+        if (mIsInForeground && memory == 0) {
+            stopForeground(true);
+            mIsInForeground = false;
+        }
+        nativeUseMemory(memory * 1024 * 1024);
+    }
+
+    // Allocate the amount of memory in native code. Otherwise the memory
+    // allocation is limited by the framework.
+    private native void nativeUseMemory(long memory);
+}
diff --git a/tools/android/memconsumer/memconsumer.gyp b/tools/android/memconsumer/memconsumer.gyp
new file mode 100644
index 0000000..f721fc1
--- /dev/null
+++ b/tools/android/memconsumer/memconsumer.gyp
@@ -0,0 +1,39 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'memconsumer',
+      'type': 'none',
+      'dependencies': [
+        'memconsumer_apk',
+      ],
+    },
+    {
+      'target_name': 'memconsumer_apk',
+      'type': 'none',
+      'variables': {
+        'apk_name': 'MemConsumer',
+        'java_in_dir': 'java',
+        'resource_dir': 'java/res',
+        'native_lib_target': 'libmemconsumer',
+      },
+      'dependencies': [
+        'libmemconsumer',
+      ],
+      'includes': [ '../../../build/java_apk.gypi' ],
+    },
+    {
+      'target_name': 'libmemconsumer',
+      'type': 'shared_library',
+      'sources': [
+        'memconsumer_hook.cc',
+      ],
+      'libraries': [
+        '-llog',
+      ],
+    },
+  ],
+}
diff --git a/tools/android/memconsumer/memconsumer_hook.cc b/tools/android/memconsumer/memconsumer_hook.cc
new file mode 100644
index 0000000..9ae0bc1
--- /dev/null
+++ b/tools/android/memconsumer/memconsumer_hook.cc
@@ -0,0 +1,55 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <android/log.h>
+#include <jni.h>
+#include <stdio.h>
+#include <string.h>
+
+extern "C" {
+JNIEXPORT void JNICALL
+    Java_org_chromium_memconsumer_ResidentService_nativeUseMemory(JNIEnv* env,
+                                                                  jobject clazz,
+                                                                  jlong memory);
+}
+
+namespace {
+
+uint32_t get_random() {
+  static uint32_t m_w = 1;
+  static uint32_t m_z = 1;
+  m_z = 36969 * (m_z & 65535) + (m_z >> 16);
+  m_w = 18000 * (m_w & 65535) + (m_w >> 16);
+  return (m_z << 16) + m_w;
+}
+
+}  // namespace
+
+JNIEXPORT void JNICALL
+    Java_org_chromium_memconsumer_ResidentService_nativeUseMemory(
+        JNIEnv* env,
+        jobject clazz,
+        jlong memory) {
+  static uint32_t* g_memory = NULL;
+  if (g_memory)
+    free(g_memory);
+  if (memory == 0) {
+    g_memory = NULL;
+    return;
+  }
+  g_memory = static_cast<uint32_t*>(malloc(memory));
+  if (!g_memory) {
+    __android_log_print(ANDROID_LOG_WARN,
+                        "MemConsumer",
+                        "Unable to allocate %ld bytes",
+                        memory);
+  }
+  // If memory allocation failed, try to allocate as much as possible.
+  while (!g_memory) {
+    memory /= 2;
+    g_memory = static_cast<uint32_t*>(malloc(memory));
+  }
+  for (int i = 0; i < memory / sizeof(uint32_t); ++i)
+    *(g_memory + i) = get_random();
+}
diff --git a/tools/android/memdump/memdump.cc b/tools/android/memdump/memdump.cc
new file mode 100644
index 0000000..cb4b05a
--- /dev/null
+++ b/tools/android/memdump/memdump.cc
@@ -0,0 +1,534 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/containers/hash_tables.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+
+const unsigned int kPageSize = getpagesize();
+
+namespace {
+
+class BitSet {
+ public:
+  void resize(size_t nbits) {
+    data_.resize((nbits + 7) / 8);
+  }
+
+  void set(uint32 bit) {
+    const uint32 byte_idx = bit / 8;
+    CHECK(byte_idx < data_.size());
+    data_[byte_idx] |= (1 << (bit & 7));
+  }
+
+  std::string AsB64String() const {
+    // Simple optimization: strip trailing zero bytes from the bitmap.
+    // For instance, if a region has 32 pages but only the first 9 are resident,
+    // The full bitmap would be 0xff 0x01 0x00 0x00, the stripped one 0xff 0x01.
+    // It can save up to some seconds when printing large mmaps, in particular
+    // in presence of large virtual address space reservations (where none of
+    // the pages are resident).
+    size_t end = data_.size();
+    while (end > 0 && data_[end - 1] == '\0')
+      --end;
+    std::string bits(&data_[0], end);
+    std::string b64_string;
+    base::Base64Encode(bits, &b64_string);
+    return b64_string;
+  }
+
+ private:
+  std::vector<char> data_;
+};
+
+// An entry in /proc/<pid>/pagemap.
+struct PageMapEntry {
+  uint64 page_frame_number : 55;
+  uint unused : 8;
+  uint present : 1;
+};
+
+// Describes a memory page.
+struct PageInfo {
+  int64 page_frame_number; // Physical page id, also known as PFN.
+  int64 flags;
+  int32 times_mapped;
+};
+
+struct PageCount {
+  PageCount() : total_count(0), unevictable_count(0) {}
+
+  int total_count;
+  int unevictable_count;
+};
+
+struct MemoryMap {
+  std::string name;
+  std::string flags;
+  uint64 start_address;
+  uint64 end_address;
+  uint64 offset;
+  PageCount private_pages;
+  // app_shared_pages[i] contains the number of pages mapped in i+2 processes
+  // (only among the processes that are being analyzed).
+  std::vector<PageCount> app_shared_pages;
+  PageCount other_shared_pages;
+  std::vector<PageInfo> committed_pages;
+  // committed_pages_bits is a bitset reflecting the present bit for all the
+  // virtual pages of the mapping.
+  BitSet committed_pages_bits;
+};
+
+struct ProcessMemory {
+  pid_t pid;
+  std::vector<MemoryMap> memory_maps;
+};
+
+bool PageIsUnevictable(const PageInfo& page_info) {
+  // These constants are taken from kernel-page-flags.h.
+  const int KPF_DIRTY = 4; // Note that only file-mapped pages can be DIRTY.
+  const int KPF_ANON = 12; // Anonymous pages are dirty per definition.
+  const int KPF_UNEVICTABLE = 18;
+  const int KPF_MLOCKED = 33;
+
+  return (page_info.flags & ((1ll << KPF_DIRTY) |
+                             (1ll << KPF_ANON) |
+                             (1ll << KPF_UNEVICTABLE) |
+                             (1ll << KPF_MLOCKED))) ?
+                             true : false;
+}
+
+// Number of times a physical page is mapped in a process.
+typedef base::hash_map<uint64, int> PFNMap;
+
+// Parses lines from /proc/<PID>/maps, e.g.:
+// 401e7000-401f5000 r-xp 00000000 103:02 158       /system/bin/linker
+bool ParseMemoryMapLine(const std::string& line,
+                        std::vector<std::string>* tokens,
+                        MemoryMap* memory_map) {
+  tokens->clear();
+  base::SplitString(line, ' ', tokens);
+  if (tokens->size() < 2)
+    return false;
+  const std::string& addr_range = tokens->at(0);
+  std::vector<std::string> range_tokens;
+  base::SplitString(addr_range, '-', &range_tokens);
+  const std::string& start_address_token = range_tokens.at(0);
+  if (!base::HexStringToUInt64(start_address_token,
+                               &memory_map->start_address)) {
+    return false;
+  }
+  const std::string& end_address_token = range_tokens.at(1);
+  if (!base::HexStringToUInt64(end_address_token, &memory_map->end_address)) {
+    return false;
+  }
+  if (tokens->at(1).size() != strlen("rwxp"))
+    return false;
+  memory_map->flags.swap(tokens->at(1));
+  if (!base::HexStringToUInt64(tokens->at(2), &memory_map->offset))
+    return false;
+  memory_map->committed_pages_bits.resize(
+      (memory_map->end_address - memory_map->start_address) / kPageSize);
+  const int map_name_index = 5;
+  if (tokens->size() >= map_name_index + 1) {
+    for (std::vector<std::string>::const_iterator it =
+             tokens->begin() + map_name_index; it != tokens->end(); ++it) {
+      if (!it->empty()) {
+        if (!memory_map->name.empty())
+          memory_map->name.append(" ");
+        memory_map->name.append(*it);
+      }
+    }
+  }
+  return true;
+}
+
+// Reads sizeof(T) bytes from file |fd| at |offset|.
+template <typename T>
+bool ReadFromFileAtOffset(int fd, off_t offset, T* value) {
+  if (lseek64(fd, offset * sizeof(*value), SEEK_SET) < 0) {
+    PLOG(ERROR) << "lseek";
+    return false;
+  }
+  ssize_t bytes = read(fd, value, sizeof(*value));
+  if (bytes != sizeof(*value) && bytes != 0) {
+    PLOG(ERROR) << "read";
+    return false;
+  }
+  return true;
+}
+
+// Fills |process_maps| in with the process memory maps identified by |pid|.
+bool GetProcessMaps(pid_t pid, std::vector<MemoryMap>* process_maps) {
+  std::ifstream maps_file(base::StringPrintf("/proc/%d/maps", pid).c_str());
+  if (!maps_file.good()) {
+    PLOG(ERROR) << "open";
+    return false;
+  }
+  std::string line;
+  std::vector<std::string> tokens;
+  while (std::getline(maps_file, line) && !line.empty()) {
+    MemoryMap memory_map = {};
+    if (!ParseMemoryMapLine(line, &tokens, &memory_map)) {
+      LOG(ERROR) << "Could not parse line: " << line;
+      return false;
+    }
+    process_maps->push_back(memory_map);
+  }
+  return true;
+}
+
+// Fills |committed_pages| in with the set of committed pages contained in the
+// provided memory map.
+bool GetPagesForMemoryMap(int pagemap_fd,
+                          const MemoryMap& memory_map,
+                          std::vector<PageInfo>* committed_pages,
+                          BitSet* committed_pages_bits) {
+  const off64_t offset = memory_map.start_address / kPageSize;
+  if (lseek64(pagemap_fd, offset * sizeof(PageMapEntry), SEEK_SET) < 0) {
+    PLOG(ERROR) << "lseek";
+    return false;
+  }
+  for (uint64 addr = memory_map.start_address, page_index = 0;
+       addr < memory_map.end_address;
+       addr += kPageSize, ++page_index) {
+    DCHECK_EQ(0, addr % kPageSize);
+    PageMapEntry page_map_entry = {};
+    COMPILE_ASSERT(sizeof(PageMapEntry) == sizeof(uint64), unexpected_size);
+    ssize_t bytes = read(pagemap_fd, &page_map_entry, sizeof(page_map_entry));
+    if (bytes != sizeof(PageMapEntry) && bytes != 0) {
+      PLOG(ERROR) << "read";
+      return false;
+    }
+    if (page_map_entry.present) {  // Ignore non-committed pages.
+      if (page_map_entry.page_frame_number == 0)
+        continue;
+      PageInfo page_info = {};
+      page_info.page_frame_number = page_map_entry.page_frame_number;
+      committed_pages->push_back(page_info);
+      committed_pages_bits->set(page_index);
+    }
+  }
+  return true;
+}
+
+// Fills |committed_pages| with mapping count and flags information gathered
+// looking-up /proc/kpagecount and /proc/kpageflags.
+bool SetPagesInfo(int pagecount_fd,
+                  int pageflags_fd,
+                  std::vector<PageInfo>* pages) {
+  for (std::vector<PageInfo>::iterator it = pages->begin();
+       it != pages->end(); ++it) {
+    PageInfo* const page_info = &*it;
+    int64 times_mapped;
+    if (!ReadFromFileAtOffset(
+            pagecount_fd, page_info->page_frame_number, &times_mapped)) {
+      return false;
+    }
+    DCHECK(times_mapped <= std::numeric_limits<int32_t>::max());
+    page_info->times_mapped = static_cast<int32>(times_mapped);
+
+    int64 page_flags;
+    if (!ReadFromFileAtOffset(
+            pageflags_fd, page_info->page_frame_number, &page_flags)) {
+      return false;
+    }
+    page_info->flags = page_flags;
+  }
+  return true;
+}
+
+// Fills in the provided vector of Page Frame Number maps. This lets
+// ClassifyPages() know how many times each page is mapped in the processes.
+void FillPFNMaps(const std::vector<ProcessMemory>& processes_memory,
+                 std::vector<PFNMap>* pfn_maps) {
+  int current_process_index = 0;
+  for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
+       it != processes_memory.end(); ++it, ++current_process_index) {
+    const std::vector<MemoryMap>& memory_maps = it->memory_maps;
+    for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
+         it != memory_maps.end(); ++it) {
+      const std::vector<PageInfo>& pages = it->committed_pages;
+      for (std::vector<PageInfo>::const_iterator it = pages.begin();
+           it != pages.end(); ++it) {
+        const PageInfo& page_info = *it;
+        PFNMap* const pfn_map = &(*pfn_maps)[current_process_index];
+        const std::pair<PFNMap::iterator, bool> result = pfn_map->insert(
+            std::make_pair(page_info.page_frame_number, 0));
+        ++result.first->second;
+      }
+    }
+  }
+}
+
+// Sets the private_count/app_shared_pages/other_shared_count fields of the
+// provided memory maps for each process.
+void ClassifyPages(std::vector<ProcessMemory>* processes_memory) {
+  std::vector<PFNMap> pfn_maps(processes_memory->size());
+  FillPFNMaps(*processes_memory, &pfn_maps);
+  // Hash set keeping track of the physical pages mapped in a single process so
+  // that they can be counted only once.
+  base::hash_set<uint64> physical_pages_mapped_in_process;
+
+  for (std::vector<ProcessMemory>::iterator it = processes_memory->begin();
+       it != processes_memory->end(); ++it) {
+    std::vector<MemoryMap>* const memory_maps = &it->memory_maps;
+    physical_pages_mapped_in_process.clear();
+    for (std::vector<MemoryMap>::iterator it = memory_maps->begin();
+         it != memory_maps->end(); ++it) {
+      MemoryMap* const memory_map = &*it;
+      const size_t processes_count = processes_memory->size();
+      memory_map->app_shared_pages.resize(processes_count - 1);
+      const std::vector<PageInfo>& pages = memory_map->committed_pages;
+      for (std::vector<PageInfo>::const_iterator it = pages.begin();
+           it != pages.end(); ++it) {
+        const PageInfo& page_info = *it;
+        if (page_info.times_mapped == 1) {
+          ++memory_map->private_pages.total_count;
+          if (PageIsUnevictable(page_info))
+            ++memory_map->private_pages.unevictable_count;
+          continue;
+        }
+        const uint64 page_frame_number = page_info.page_frame_number;
+        const std::pair<base::hash_set<uint64>::iterator, bool> result =
+            physical_pages_mapped_in_process.insert(page_frame_number);
+        const bool did_insert = result.second;
+        if (!did_insert) {
+          // This physical page (mapped multiple times in the same process) was
+          // already counted.
+          continue;
+        }
+        // See if the current physical page is also mapped in the other
+        // processes that are being analyzed.
+        int times_mapped = 0;
+        int mapped_in_processes_count = 0;
+        for (std::vector<PFNMap>::const_iterator it = pfn_maps.begin();
+             it != pfn_maps.end(); ++it) {
+          const PFNMap& pfn_map = *it;
+          const PFNMap::const_iterator found_it = pfn_map.find(
+              page_frame_number);
+          if (found_it == pfn_map.end())
+            continue;
+          ++mapped_in_processes_count;
+          times_mapped += found_it->second;
+        }
+        PageCount* page_count_to_update = NULL;
+        if (times_mapped == page_info.times_mapped) {
+          // The physical page is only mapped in the processes that are being
+          // analyzed.
+          if (mapped_in_processes_count > 1) {
+            // The physical page is mapped in multiple processes.
+            page_count_to_update =
+                &memory_map->app_shared_pages[mapped_in_processes_count - 2];
+          } else {
+            // The physical page is mapped multiple times in the same process.
+            page_count_to_update = &memory_map->private_pages;
+          }
+        } else {
+          page_count_to_update = &memory_map->other_shared_pages;
+        }
+        ++page_count_to_update->total_count;
+        if (PageIsUnevictable(page_info))
+          ++page_count_to_update->unevictable_count;
+      }
+    }
+  }
+}
+
+void AppendAppSharedField(const std::vector<PageCount>& app_shared_pages,
+                          std::string* out) {
+  out->append("[");
+  for (std::vector<PageCount>::const_iterator it = app_shared_pages.begin();
+       it != app_shared_pages.end(); ++it) {
+    out->append(base::IntToString(it->total_count * kPageSize));
+    out->append(":");
+    out->append(base::IntToString(it->unevictable_count * kPageSize));
+    if (it + 1 != app_shared_pages.end())
+      out->append(",");
+  }
+  out->append("]");
+}
+
+void DumpProcessesMemoryMapsInShortFormat(
+    const std::vector<ProcessMemory>& processes_memory) {
+  const int KB_PER_PAGE = kPageSize >> 10;
+  std::vector<int> totals_app_shared(processes_memory.size());
+  std::string buf;
+  std::cout << "pid\tprivate\t\tshared_app\tshared_other (KB)\n";
+  for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
+       it != processes_memory.end(); ++it) {
+    const ProcessMemory& process_memory = *it;
+    std::fill(totals_app_shared.begin(), totals_app_shared.end(), 0);
+    int total_private = 0, total_other_shared = 0;
+    const std::vector<MemoryMap>& memory_maps = process_memory.memory_maps;
+    for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
+         it != memory_maps.end(); ++it) {
+      const MemoryMap& memory_map = *it;
+      total_private += memory_map.private_pages.total_count;
+      for (size_t i = 0; i < memory_map.app_shared_pages.size(); ++i)
+        totals_app_shared[i] += memory_map.app_shared_pages[i].total_count;
+      total_other_shared += memory_map.other_shared_pages.total_count;
+    }
+    double total_app_shared = 0;
+    for (size_t i = 0; i < totals_app_shared.size(); ++i)
+      total_app_shared += static_cast<double>(totals_app_shared[i]) / (i + 2);
+    base::SStringPrintf(
+        &buf, "%d\t%d\t\t%d\t\t%d\n",
+        process_memory.pid,
+        total_private * KB_PER_PAGE,
+        static_cast<int>(total_app_shared) * KB_PER_PAGE,
+        total_other_shared * KB_PER_PAGE);
+    std::cout << buf;
+  }
+}
+
+void DumpProcessesMemoryMapsInExtendedFormat(
+    const std::vector<ProcessMemory>& processes_memory) {
+  std::string buf;
+  std::string app_shared_buf;
+  for (std::vector<ProcessMemory>::const_iterator it = processes_memory.begin();
+       it != processes_memory.end(); ++it) {
+    const ProcessMemory& process_memory = *it;
+    std::cout << "[ PID=" << process_memory.pid << "]" << '\n';
+    const std::vector<MemoryMap>& memory_maps = process_memory.memory_maps;
+    for (std::vector<MemoryMap>::const_iterator it = memory_maps.begin();
+         it != memory_maps.end(); ++it) {
+      const MemoryMap& memory_map = *it;
+      app_shared_buf.clear();
+      AppendAppSharedField(memory_map.app_shared_pages, &app_shared_buf);
+      base::SStringPrintf(
+          &buf,
+          "%"PRIx64"-%"PRIx64" %s %"PRIx64" private_unevictable=%d private=%d "
+          "shared_app=%s shared_other_unevictable=%d shared_other=%d "
+          "\"%s\" [%s]\n",
+          memory_map.start_address,
+          memory_map.end_address,
+          memory_map.flags.c_str(),
+          memory_map.offset,
+          memory_map.private_pages.unevictable_count * kPageSize,
+          memory_map.private_pages.total_count * kPageSize,
+          app_shared_buf.c_str(),
+          memory_map.other_shared_pages.unevictable_count * kPageSize,
+          memory_map.other_shared_pages.total_count * kPageSize,
+          memory_map.name.c_str(),
+          memory_map.committed_pages_bits.AsB64String().c_str());
+      std::cout << buf;
+    }
+  }
+}
+
+bool CollectProcessMemoryInformation(int page_count_fd,
+                                     int page_flags_fd,
+                                     ProcessMemory* process_memory) {
+  const pid_t pid = process_memory->pid;
+  base::ScopedFD pagemap_fd(HANDLE_EINTR(open(
+      base::StringPrintf("/proc/%d/pagemap", pid).c_str(), O_RDONLY)));
+  if (!pagemap_fd.is_valid()) {
+    PLOG(ERROR) << "open";
+    return false;
+  }
+  std::vector<MemoryMap>* const process_maps = &process_memory->memory_maps;
+  if (!GetProcessMaps(pid, process_maps))
+    return false;
+  for (std::vector<MemoryMap>::iterator it = process_maps->begin();
+       it != process_maps->end(); ++it) {
+    std::vector<PageInfo>* const committed_pages = &it->committed_pages;
+    BitSet* const pages_bits = &it->committed_pages_bits;
+    GetPagesForMemoryMap(pagemap_fd.get(), *it, committed_pages, pages_bits);
+    SetPagesInfo(page_count_fd, page_flags_fd, committed_pages);
+  }
+  return true;
+}
+
+void KillAll(const std::vector<pid_t>& pids, int signal_number) {
+  for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
+       ++it) {
+    kill(*it, signal_number);
+  }
+}
+
+void ExitWithUsage() {
+  LOG(ERROR) << "Usage: memdump [-a] <PID1>... <PIDN>";
+  exit(EXIT_FAILURE);
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  if (argc == 1)
+    ExitWithUsage();
+  const bool short_output = !strncmp(argv[1], "-a", 2);
+  if (short_output) {
+    if (argc == 2)
+      ExitWithUsage();
+    ++argv;
+  }
+  std::vector<pid_t> pids;
+  for (const char* const* ptr = argv + 1; *ptr; ++ptr) {
+    pid_t pid;
+    if (!base::StringToInt(*ptr, &pid))
+      return EXIT_FAILURE;
+    pids.push_back(pid);
+  }
+
+  std::vector<ProcessMemory> processes_memory(pids.size());
+  {
+    base::ScopedFD page_count_fd(
+        HANDLE_EINTR(open("/proc/kpagecount", O_RDONLY)));
+    if (!page_count_fd.is_valid()) {
+      PLOG(ERROR) << "open /proc/kpagecount";
+      return EXIT_FAILURE;
+    }
+
+    base::ScopedFD page_flags_fd(open("/proc/kpageflags", O_RDONLY));
+    if (!page_flags_fd.is_valid()) {
+      PLOG(ERROR) << "open /proc/kpageflags";
+      return EXIT_FAILURE;
+    }
+
+    base::ScopedClosureRunner auto_resume_processes(
+        base::Bind(&KillAll, pids, SIGCONT));
+    KillAll(pids, SIGSTOP);
+    for (std::vector<pid_t>::const_iterator it = pids.begin(); it != pids.end();
+         ++it) {
+      ProcessMemory* const process_memory =
+          &processes_memory[it - pids.begin()];
+      process_memory->pid = *it;
+      if (!CollectProcessMemoryInformation(
+              page_count_fd.get(), page_flags_fd.get(), process_memory)) {
+        return EXIT_FAILURE;
+      }
+    }
+  }
+
+  ClassifyPages(&processes_memory);
+  if (short_output)
+    DumpProcessesMemoryMapsInShortFormat(processes_memory);
+  else
+    DumpProcessesMemoryMapsInExtendedFormat(processes_memory);
+  return EXIT_SUCCESS;
+}
diff --git a/tools/android/memdump/memdump.gyp b/tools/android/memdump/memdump.gyp
new file mode 100644
index 0000000..f47cedf
--- /dev/null
+++ b/tools/android/memdump/memdump.gyp
@@ -0,0 +1,39 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'memdump-unstripped',
+      'type': 'executable',
+      'dependencies': [
+        '../../../base/base.gyp:base',
+      ],
+      'sources': [
+        'memdump.cc',
+      ],
+    },
+    {
+      'target_name': 'memdump',
+      'type': 'none',
+      'dependencies': [
+        'memdump-unstripped',
+      ],
+      'actions': [
+        {
+          'action_name': 'strip_memdump',
+          'inputs': ['<(PRODUCT_DIR)/memdump-unstripped'],
+          'outputs': ['<(PRODUCT_DIR)/memdump'],
+          'action': [
+            '<(android_strip)',
+            '--strip-unneeded',
+            '<@(_inputs)',
+            '-o',
+            '<@(_outputs)',
+          ],
+        },
+      ],
+    },
+  ],
+}
diff --git a/tools/android/memdump/memsymbols.py b/tools/android/memdump/memsymbols.py
new file mode 100755
index 0000000..3721963
--- /dev/null
+++ b/tools/android/memdump/memsymbols.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import base64
+import os
+import sys
+import re
+
+from optparse import OptionParser
+
+"""Extracts the list of resident symbols of a library loaded in a process.
+
+This scripts combines the extended output of memdump for a given process
+(obtained through memdump -x PID) and the symbol table of a .so loaded in that
+process (obtained through nm -C lib-with-symbols.so), filtering out only those
+symbols that, at the time of the snapshot, were resident in memory (that are,
+the symbols which start address belongs to a mapped page of the .so which was
+resident at the time of the snapshot).
+The aim is to perform a "code coverage"-like profiling of a binary, intersecting
+run-time information (list of resident pages) and debug symbols.
+"""
+
+_PAGE_SIZE = 4096
+
+
+def _TestBit(word, bit):
+  assert(bit >= 0 and bit < 8)
+  return not not ((word >> bit) & 1)
+
+
+def _HexAddr(addr):
+  return hex(addr)[2:].zfill(8)
+
+
+def _GetResidentPagesSet(memdump_contents, lib_name, verbose):
+  """Parses the memdump output and extracts the resident page set for lib_name.
+  Args:
+    memdump_contents: Array of strings (lines) of a memdump output.
+    lib_name: A string containing the name of the library.so to be matched.
+    verbose: Print a verbose header for each mapping matched.
+
+  Returns:
+    A set of resident pages (the key is the page index) for all the
+    mappings matching .*lib_name.
+  """
+  resident_pages = set()
+  MAP_RX = re.compile(
+      r'^([0-9a-f]+)-([0-9a-f]+) ([\w-]+) ([0-9a-f]+) .* "(.*)" \[(.*)\]$')
+  for line in memdump_contents:
+    line = line.rstrip('\r\n')
+    if line.startswith('[ PID'):
+      continue
+
+    r = MAP_RX.match(line)
+    if not r:
+      sys.stderr.write('Skipping %s from %s\n' % (line, memdump_file))
+      continue
+
+    map_start = int(r.group(1), 16)
+    map_end = int(r.group(2), 16)
+    prot = r.group(3)
+    offset = int(r.group(4), 16)
+    assert(offset % _PAGE_SIZE == 0)
+    lib = r.group(5)
+    enc_bitmap = r.group(6)
+
+    if not lib.endswith(lib_name):
+      continue
+
+    bitmap = base64.b64decode(enc_bitmap)
+    map_pages_count = (map_end - map_start + 1) / _PAGE_SIZE
+    bitmap_pages_count = len(bitmap) * 8
+
+    if verbose:
+      print 'Found %s: mapped %d pages in mode %s @ offset %s.' % (
+            lib, map_pages_count, prot, _HexAddr(offset))
+      print ' Map range in the process VA: [%s - %s]. Len: %s' % (
+          _HexAddr(map_start),
+          _HexAddr(map_end),
+          _HexAddr(map_pages_count * _PAGE_SIZE))
+      print ' Corresponding addresses in the binary: [%s - %s]. Len: %s' % (
+          _HexAddr(offset),
+          _HexAddr(offset + map_end - map_start),
+          _HexAddr(map_pages_count * _PAGE_SIZE))
+      print ' Bitmap: %d pages' % bitmap_pages_count
+      print ''
+
+    assert(bitmap_pages_count >= map_pages_count)
+    for i in xrange(map_pages_count):
+      bitmap_idx = i / 8
+      bitmap_off = i % 8
+      if (bitmap_idx < len(bitmap) and
+          _TestBit(ord(bitmap[bitmap_idx]), bitmap_off)):
+        resident_pages.add(offset / _PAGE_SIZE + i)
+  return resident_pages
+
+
+def main(argv):
+  NM_RX = re.compile(r'^([0-9a-f]+)\s+.*$')
+
+  parser = OptionParser()
+  parser.add_option("-r", "--reverse",
+                    action="store_true", dest="reverse", default=False,
+                    help="Print out non present symbols")
+  parser.add_option("-v", "--verbose",
+                    action="store_true", dest="verbose", default=False,
+                    help="Print out verbose debug information.")
+
+  (options, args) = parser.parse_args()
+
+  if len(args) != 3:
+    print 'Usage: %s [-v] memdump.file nm.file library.so' % (
+        os.path.basename(argv[0]))
+    return 1
+
+  memdump_file = args[0]
+  nm_file = args[1]
+  lib_name = args[2]
+
+  if memdump_file == '-':
+    memdump_contents = sys.stdin.readlines()
+  else:
+    memdump_contents = open(memdump_file, 'r').readlines()
+  resident_pages = _GetResidentPagesSet(memdump_contents,
+                                        lib_name,
+                                        options.verbose)
+
+  # Process the nm symbol table, filtering out the resident symbols.
+  nm_fh = open(nm_file, 'r')
+  for line in nm_fh:
+    line = line.rstrip('\r\n')
+    # Skip undefined symbols (lines with no address).
+    if line.startswith(' '):
+      continue
+
+    r = NM_RX.match(line)
+    if not r:
+      sys.stderr.write('Skipping %s from %s\n' % (line, nm_file))
+      continue
+
+    sym_addr = int(r.group(1), 16)
+    sym_page = sym_addr / _PAGE_SIZE
+    last_sym_matched = (sym_page in resident_pages)
+    if (sym_page in resident_pages) != options.reverse:
+      print line
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/tools/android/mempressure.py b/tools/android/mempressure.py
new file mode 100755
index 0000000..fa3daba
--- /dev/null
+++ b/tools/android/mempressure.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import logging
+import optparse
+import os
+import sys
+
+BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__),
+                                 os.pardir,
+                                 os.pardir,
+                                 'build',
+                                 'android')
+sys.path.append(BUILD_ANDROID_DIR)
+from pylib import android_commands
+from pylib import constants
+from pylib import flag_changer
+from pylib.device import device_errors
+from pylib.device import device_utils
+
+# Browser Constants
+DEFAULT_BROWSER = 'chrome'
+
+# Action Constants
+ACTION_PACKAGE = 'org.chromium.base'
+ACTION_TRIM = {
+    'moderate' : ACTION_PACKAGE + '.ACTION_TRIM_MEMORY_MODERATE',
+    'critical' : ACTION_PACKAGE + '.ACTION_TRIM_MEMORY_RUNNING_CRITICAL',
+    'complete' : ACTION_PACKAGE + '.ACTION_TRIM_MEMORY'
+}
+ACTION_LOW = ACTION_PACKAGE + '.ACTION_LOW_MEMORY'
+
+# Command Line Constants
+ENABLE_TEST_INTENTS_FLAG = '--enable-test-intents'
+
+def main(argv):
+  option_parser = optparse.OptionParser()
+  option_parser.add_option('-l',
+                           '--low',
+                           help='Simulate Activity#onLowMemory()',
+                           action='store_true')
+  option_parser.add_option('-t',
+                           '--trim',
+                           help=('Simulate Activity#onTrimMemory(...) with ' +
+                                 ', '.join(ACTION_TRIM.keys())),
+                           type='string')
+  option_parser.add_option('-b',
+                           '--browser',
+                           default=DEFAULT_BROWSER,
+                           help=('Which browser to use. One of ' +
+                                 ', '.join(constants.PACKAGE_INFO.keys()) +
+                                 ' [default: %default]'),
+                           type='string')
+
+  (options, args) = option_parser.parse_args(argv)
+
+  if len(args) > 1:
+    print 'Unknown argument: ', args[1:]
+    option_parser.print_help()
+    sys.exit(1)
+
+  if options.low and options.trim:
+    option_parser.error('options --low and --trim are mutually exclusive')
+
+  if not options.low and not options.trim:
+    option_parser.print_help()
+    sys.exit(1)
+
+  action = None
+  if options.low:
+    action = ACTION_LOW
+  elif options.trim in ACTION_TRIM.keys():
+    action = ACTION_TRIM[options.trim]
+
+  if action is None:
+    option_parser.print_help()
+    sys.exit(1)
+
+  if not options.browser in constants.PACKAGE_INFO.keys():
+    option_parser.error('Unknown browser option ' + options.browser)
+
+  package_info = constants.PACKAGE_INFO[options.browser]
+
+  package = package_info.package
+  activity = package_info.activity
+
+  devices = android_commands.GetAttachedDevices()
+  if not devices:
+    raise device_errors.NoDevicesError()
+  elif len(devices) > 1:
+    logging.warning('Multiple devices attached. Using %s.' % devices[0])
+  device = device_utils.DeviceUtils(devices[0])
+
+  try:
+    device.EnableRoot()
+  except device_errors.CommandFailedError as e:
+    # Try to change the flags and start the activity anyway.
+    # TODO(jbudorick) Handle this exception appropriately after interface
+    #                 conversions are finished.
+    logging.error(str(e))
+  flags = flag_changer.FlagChanger(device, package_info.cmdline_file)
+  if ENABLE_TEST_INTENTS_FLAG not in flags.Get():
+    flags.AddFlags([ENABLE_TEST_INTENTS_FLAG])
+
+  device.StartActivity(intent.Intent(package=package, activity=activity,
+                                     action=action))
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/tools/android/ps_ext/ps_ext.c b/tools/android/ps_ext/ps_ext.c
new file mode 100644
index 0000000..06cf7bc
--- /dev/null
+++ b/tools/android/ps_ext/ps_ext.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2014 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+/*
+ * This tool is essentially an extended version of ps with JSON output.
+ * Its output is meant consumed by scripts / tools for gathering OS/ps stats.
+ * Output units:
+ *   All times are expressed in ticks.
+ *   All memory counters are expressed in Kb.
+ */
+
+static void dump_time(void) {
+  float uptime_secs = 0.0F;
+  const long rate = sysconf(_SC_CLK_TCK);
+  FILE *f = fopen("/proc/uptime", "r");
+  if (!f)
+    return;
+  fscanf(f, "%f", &uptime_secs);
+  fclose(f);
+  const long ticks = (long) (rate * uptime_secs);
+  printf("  \"time\": { \"ticks\": %ld, \"rate\": %ld}", ticks, rate);
+}
+
+static void dump_cpu_stats(void) {
+  FILE *f = fopen("/proc/stat", "r");
+  if (!f)
+    return;
+  printf("  \"cpu\":\n  [\n");
+
+  bool terminate_prev_line = false;
+  while (!feof(f)) {
+    char line[256];
+    char cpu[8];
+    long unsigned t_usr = 0;
+    long unsigned t_nice = 0;
+    long unsigned t_sys = 0;
+    long unsigned t_idle = 0;
+    fgets(line, sizeof(line), f);
+
+    /* Skip the total 'cpu ' line and the other irrelevant ones. */
+    if (strncmp(line, "cpu", 3) != 0 || line[3] == ' ')
+      continue;
+    if (sscanf(line, "%s %lu %lu %lu %lu",
+               cpu, &t_usr, &t_nice, &t_sys, &t_idle) != 5) {
+      continue;
+    }
+
+    if (terminate_prev_line)
+      printf(",\n");
+    terminate_prev_line = true;
+    printf("    {\"usr\": %lu, \"sys\": %lu, \"idle\": %lu}",
+           t_usr + t_nice, t_sys, t_idle);
+  }
+  fclose(f);
+  printf("\n  ]");
+}
+
+static void dump_mem_stats(void) {
+  FILE *f = fopen("/proc/meminfo", "r");
+  if (!f)
+    return;
+  printf("  \"mem\":\n  {\n");
+
+  bool terminate_prev_line = false;
+  while (!feof(f)) {
+    char line[256];
+    char key[32];
+    long value = 0;
+
+    fgets(line, sizeof(line), f);
+    if (sscanf(line, "%s %lu %*s", key, &value) < 2)
+      continue;
+
+    if (terminate_prev_line)
+      printf(",\n");
+    terminate_prev_line = true;
+    printf("    \"%s\": %lu", key, value);
+  }
+  fclose(f);
+  printf("\n  }");
+}
+
+static void dump_proc_stats(void) {
+  struct dirent *de;
+  DIR *d = opendir("/proc");
+  if (!d)
+    return;
+
+  const long kb_per_page = sysconf(_SC_PAGESIZE) / 1024;
+  bool terminate_prev_line = false;
+  printf("  \"processes\":\n  {\n");
+  while ((de = readdir(d))) {
+    if (!isdigit(de->d_name[0]))
+      continue;
+    const int pid = atoi(de->d_name);
+
+    /* Don't print out ourselves (how civilized). */
+    if (pid == getpid())
+      continue;
+
+    char cmdline[64];
+    char fpath[32];
+    FILE *f;
+
+    /* Read full process path / package from cmdline. */
+    sprintf(fpath, "/proc/%d/cmdline", pid);
+    f = fopen(fpath, "r");
+    if (!f)
+      continue;
+    cmdline[0] = '\0';
+    fgets(cmdline, sizeof(cmdline), f);
+    fclose(f);
+
+    /* Read cpu/io/mem stats. */
+    char proc_name[256];
+    long num_threads = 0;
+    long unsigned min_faults = 0;
+    long unsigned maj_faults = 0;
+    long unsigned utime = 0;
+    long unsigned ktime = 0;
+    long unsigned vm_rss = 0;
+    long long unsigned start_time = 0;
+
+    sprintf(fpath, "/proc/%d/stat", pid);
+    f = fopen(fpath, "r");
+    if (!f)
+      continue;
+    fscanf(f, "%*d %s %*c %*d %*d %*d %*d %*d %*u %lu %*u %lu %*u %lu %lu "
+           "%*d %*d %*d %*d %ld %*d %llu %*u %ld", proc_name, &min_faults,
+           &maj_faults, &utime, &ktime, &num_threads, &start_time, &vm_rss);
+    fclose(f);
+
+    /* Prefer the cmdline when available, since it contains the package name. */
+    char const * const cmd = (strlen(cmdline) > 0) ? cmdline : proc_name;
+
+    if (terminate_prev_line)
+      printf(",\n");
+    terminate_prev_line = true;
+    printf("   \"%d\": {"
+           "\"name\": \"%s\", "
+           "\"n_threads\": %ld, "
+           "\"start_time\": %llu, "
+           "\"user_time\": %lu, "
+           "\"sys_time\": %lu, "
+           "\"min_faults\": %lu, "
+           "\"maj_faults\": %lu, "
+           "\"vm_rss\": %lu"
+           "}",
+           pid,
+           cmd,
+           num_threads,
+           start_time,
+           utime,
+           ktime,
+           min_faults,
+           maj_faults,
+           vm_rss * kb_per_page);
+  }
+  closedir(d);
+  printf("\n  }");
+}
+
+int main()
+{
+  printf("{\n");
+
+  dump_time();
+  printf(",\n");
+
+  dump_mem_stats();
+  printf(",\n");
+
+  dump_cpu_stats();
+  printf(",\n");
+
+  dump_proc_stats();
+  printf("\n}\n");
+
+  return 0;
+}
diff --git a/tools/android/ps_ext/ps_ext.gyp b/tools/android/ps_ext/ps_ext.gyp
new file mode 100644
index 0000000..f467d93
--- /dev/null
+++ b/tools/android/ps_ext/ps_ext.gyp
@@ -0,0 +1,36 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'ps_ext-unstripped',
+      'type': 'executable',
+      'sources': [
+        'ps_ext.c',
+      ],
+    },
+    {
+      'target_name': 'ps_ext',
+      'type': 'none',
+      'dependencies': [
+        'ps_ext-unstripped',
+      ],
+      'actions': [
+        {
+          'action_name': 'strip_ps_ext',
+          'inputs': ['<(PRODUCT_DIR)/ps_ext-unstripped'],
+          'outputs': ['<(PRODUCT_DIR)/ps_ext'],
+          'action': [
+            '<(android_strip)',
+            '--strip-unneeded',
+            '<@(_inputs)',
+            '-o',
+            '<@(_outputs)',
+          ],
+        },
+      ],
+    },
+  ],
+}
diff --git a/tools/android/purge_ashmem/purge_ashmem.c b/tools/android/purge_ashmem/purge_ashmem.c
new file mode 100644
index 0000000..0de582d
--- /dev/null
+++ b/tools/android/purge_ashmem/purge_ashmem.c
@@ -0,0 +1,20 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "third_party/ashmem/ashmem.h"
+
+int main(void) {
+  const int pages_purged = ashmem_purge_all();
+  if (pages_purged < 0) {
+    perror("ashmem_purge_all");
+    return EXIT_FAILURE;
+  }
+  printf("Purged %d pages (%d KBytes)\n",
+         pages_purged, pages_purged * getpagesize() / 1024);
+  return EXIT_SUCCESS;
+}
diff --git a/tools/android/purge_ashmem/purge_ashmem.gyp b/tools/android/purge_ashmem/purge_ashmem.gyp
new file mode 100644
index 0000000..d563b1f
--- /dev/null
+++ b/tools/android/purge_ashmem/purge_ashmem.gyp
@@ -0,0 +1,21 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'purge_ashmem',
+      'type': 'executable',
+      'dependencies': [
+        '../../../third_party/ashmem/ashmem.gyp:ashmem',
+      ],
+      'include_dirs': [
+        '../../../',
+      ],
+      'sources': [
+        'purge_ashmem.c',
+      ],
+    },
+  ],
+}
diff --git a/tools/android/remove_strings.py b/tools/android/remove_strings.py
new file mode 100755
index 0000000..b8c4807
--- /dev/null
+++ b/tools/android/remove_strings.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Remove strings by name from a GRD file."""
+
+import optparse
+import re
+import sys
+
+
+def RemoveStrings(grd_path, string_names):
+  """Removes strings with the given names from a GRD file. Overwrites the file.
+
+  Args:
+    grd_path: path to the GRD file.
+    string_names: a list of string names to be removed.
+  """
+  with open(grd_path, 'r') as f:
+    grd = f.read()
+  names_pattern = '|'.join(map(re.escape, string_names))
+  pattern = r'<message [^>]*name="(%s)".*?</message>\s*' % names_pattern
+  grd = re.sub(pattern, '', grd, flags=re.DOTALL)
+  with open(grd_path, 'w') as f:
+    f.write(grd)
+
+
+def ParseArgs(args):
+  usage = 'usage: %prog GRD_PATH...'
+  parser = optparse.OptionParser(
+      usage=usage, description='Remove strings from GRD files. Reads string '
+      'names from stdin, and removes strings with those names from the listed '
+      'GRD files.')
+  options, args = parser.parse_args(args=args)
+  if not args:
+    parser.error('must provide GRD_PATH argument(s)')
+  return args
+
+
+def main(args=None):
+  grd_paths = ParseArgs(args)
+  strings_to_remove = filter(None, map(str.strip, sys.stdin.readlines()))
+  for grd_path in grd_paths:
+    RemoveStrings(grd_path, strings_to_remove)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/android/run_pie/run_pie.c b/tools/android/run_pie/run_pie.c
new file mode 100644
index 0000000..ee1a622
--- /dev/null
+++ b/tools/android/run_pie/run_pie.c
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+// This is a wrapper to run position independent executables on Android ICS,
+// where the linker doesn't support PIE. This requires the PIE binaries to be
+// built with CFLAGS +=-fvisibility=default -fPIE, and LDFLAGS += -rdynamic -pie
+// such that the main() symbol remains exported and can be dlsym-ed.
+
+#define ERR_PREFIX "[PIE Loader] "
+
+typedef int (*main_t)(int, char**);
+
+
+int main(int argc, char** argv) {
+  if (argc < 2) {
+    printf("Usage: %s path_to_pie_executable [args]\n", argv[0]);
+    return -1;
+  }
+
+  // Shift left the argv[]. argv is what /proc/PID/cmdline prints out. In turn
+  // cmdline is what Android "ps" prints out. In turn "ps" is what many scripts
+  // look for to decide which processes to kill / killall.
+  int i;
+  char* next_argv_start = argv[0];
+  for (i = 1; i < argc; ++i) {
+    const size_t argv_len = strlen(argv[i]) + 1;
+    memcpy(argv[i - 1], argv[i], argv_len);
+    next_argv_start += argv_len;
+    argv[i] = next_argv_start;
+  }
+  argv[argc - 1] = NULL;  // The last argv must be a NULL ptr.
+
+  // Set also the proc name accordingly (/proc/PID/comm).
+  prctl(PR_SET_NAME, (long) argv[0]);
+
+  // dlopen should not fail, unless:
+  // - The target binary does not exists:
+  // - The dependent .so libs cannot be loaded.
+  // In both cases, just bail out with an explicit error message.
+  void* handle = dlopen(argv[0], RTLD_NOW);
+  if (handle == NULL) {
+    printf(ERR_PREFIX "dlopen() failed: %s.\n", dlerror());
+    return -1;
+  }
+
+  main_t pie_main = (main_t) dlsym(handle, "main");
+  if (pie_main) {
+    return pie_main(argc - 1, argv);
+  }
+
+  // If we reached this point dlsym failed, very likely because the target
+  // binary has not been compiled with the proper CFLAGS / LDFLAGS.
+  // At this point the most sensible thing to do is running that normally
+  // via exec and hope that the target binary wasn't a PIE.
+  execv(argv[0], argv);
+
+  // exevc is supposed to never return, unless it fails.
+  printf(ERR_PREFIX "Both dlsym() and the execv() fallback failed.\n");
+  perror("execv");
+  return -1;
+}
diff --git a/tools/android/run_pie/run_pie.gyp b/tools/android/run_pie/run_pie.gyp
new file mode 100644
index 0000000..b713dc4
--- /dev/null
+++ b/tools/android/run_pie/run_pie.gyp
@@ -0,0 +1,49 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'run_pie-unstripped',
+      'type': 'executable',
+      'sources': [
+        'run_pie.c',
+      ],
+      # See crbug.com/373219. This is the only Android executable which must be
+      # non PIE.
+      'cflags!': [
+        '-fPIE',
+      ],
+      'ldflags!': [
+        '-pie',
+      ],
+      # Don't inherit unneeded dependencies on stlport.so, so the binary remains
+      # self-contained also in component=shared_library builds.
+      'libraries!': [
+        '-l<(android_stlport_library)',
+      ],
+    },
+    {
+      'target_name': 'run_pie',
+      'type': 'none',
+      'dependencies': [
+        'run_pie-unstripped',
+      ],
+      'actions': [
+        {
+          'action_name': 'strip_run_pie',
+          'inputs': ['<(PRODUCT_DIR)/run_pie-unstripped'],
+          'outputs': ['<(PRODUCT_DIR)/run_pie'],
+          'action': [
+            '<(android_strip)',
+            '--strip-unneeded',
+            '<@(_inputs)',
+            '-o',
+            '<@(_outputs)',
+          ],
+        },
+      ],
+    },
+  ],
+}