'.
+ RET=$(echo "$LASTLINE" | \
+ awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,RSTART+2); } }')
+ # Remove the status code from the last line. Note that this may result
+ # in an empty line.
+ LASTLINE=$(echo "$LASTLINE" | \
+ awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,1,RSTART-1); } }')
+ # The output itself: all lines except the status code.
+ sed -e '$d' $TMPOUT && printf "%s" "$LASTLINE"
+ # Remove temp file.
+ rm -f $TMPOUT
+ # Exit with the appropriate status.
+ return $RET
+}
+
+# Find the target architecture from a local shared library.
+# This returns an NDK-compatible architecture name.
+# out: NDK Architecture name, or empty string.
+get_gyp_target_arch () {
+ local RANDOM_LIB=$(ls "$SYMBOL_DIR"/lib*.so | head -n1)
+ local SO_DESC=$(file $RANDOM_LIB)
+ case $ARCH in
+ *32-bit*ARM,*) echo "arm";;
+ *64-bit*ARM,*) echo "arm64";;
+ *32-bit*Intel,*) echo "x86";;
+ *x86-64,*) echo "x86_64";;
+ *32-bit*MIPS,*) echo "mips";;
+ *) echo "";
+ esac
+}
+
+if [ -z "$TARGET_ARCH" ]; then
+ TARGET_ARCH=$(get_gyp_target_arch)
+ if [ -z "$TARGET_ARCH" ]; then
+ TARGET_ARCH=arm
+ fi
+else
+ # Nit: accept Chromium's 'ia32' as a valid target architecture. This
+ # script prefers the NDK 'x86' name instead because it uses it to find
+ # NDK-specific files (host gdb) with it.
+ if [ "$TARGET_ARCH" = "ia32" ]; then
+ TARGET_ARCH=x86
+ log "Auto-config: --arch=$TARGET_ARCH (equivalent to ia32)"
+ fi
+fi
+
+# Detect the NDK system name, i.e. the name used to identify the host.
+# out: NDK system name (e.g. 'linux' or 'darwin')
+get_ndk_host_system () {
+ local HOST_OS
+ if [ -z "$NDK_HOST_SYSTEM" ]; then
+ HOST_OS=$(uname -s)
+ case $HOST_OS in
+ Linux) NDK_HOST_SYSTEM=linux;;
+ Darwin) NDK_HOST_SYSTEM=darwin;;
+ *) panic "You can't run this script on this system: $HOST_OS";;
+ esac
+ fi
+ echo "$NDK_HOST_SYSTEM"
+}
+
+# Detect the NDK host architecture name.
+# out: NDK arch name (e.g. 'x86' or 'x86_64')
+get_ndk_host_arch () {
+ local HOST_ARCH HOST_OS
+ if [ -z "$NDK_HOST_ARCH" ]; then
+ HOST_OS=$(get_ndk_host_system)
+ HOST_ARCH=$(uname -p)
+ case $HOST_ARCH in
+ i?86) NDK_HOST_ARCH=x86;;
+ x86_64|amd64) NDK_HOST_ARCH=x86_64;;
+ *) panic "You can't run this script on this host architecture: $HOST_ARCH";;
+ esac
+ # Darwin trick: "uname -p" always returns i386 on 64-bit installations.
+ if [ "$HOST_OS" = darwin -a "$NDK_HOST_ARCH" = "x86" ]; then
+ # Use '/usr/bin/file', not just 'file' to avoid buggy MacPorts
+ # implementations of the tool. See http://b.android.com/53769
+ HOST_64BITS=$(/usr/bin/file -L "$SHELL" | grep -e "x86[_-]64")
+ if [ "$HOST_64BITS" ]; then
+ NDK_HOST_ARCH=x86_64
+ fi
+ fi
+ fi
+ echo "$NDK_HOST_ARCH"
+}
+
+# Convert an NDK architecture name into a GNU configure triplet.
+# $1: NDK architecture name (e.g. 'arm')
+# Out: Android GNU configure triplet (e.g. 'arm-linux-androideabi')
+get_arch_gnu_config () {
+ case $1 in
+ arm)
+ echo "arm-linux-androideabi"
+ ;;
+ arm64)
+ echo "aarch64-linux-android"
+ ;;
+ x86)
+ echo "i686-linux-android"
+ ;;
+ x86_64)
+ echo "x86_64-linux-android"
+ ;;
+ mips)
+ echo "mipsel-linux-android"
+ ;;
+ *)
+ echo "$ARCH-linux-android"
+ ;;
+ esac
+}
+
+# Convert an NDK architecture name into a toolchain name prefix
+# $1: NDK architecture name (e.g. 'arm')
+# Out: NDK toolchain name prefix (e.g. 'arm-linux-androideabi')
+get_arch_toolchain_prefix () {
+ # Return the configure triplet, except for x86!
+ if [ "$1" = "x86" ]; then
+ echo "$1"
+ else
+ get_arch_gnu_config $1
+ fi
+}
+
+# Find a NDK toolchain prebuilt file or sub-directory.
+# This will probe the various arch-specific toolchain directories
+# in the NDK for the needed file.
+# $1: NDK install path
+# $2: NDK architecture name
+# $3: prebuilt sub-path to look for.
+# Out: file path, or empty if none is found.
+get_ndk_toolchain_prebuilt () {
+ local NDK_DIR="${1%/}"
+ local ARCH="$2"
+ local SUBPATH="$3"
+ local NAME="$(get_arch_toolchain_prefix $ARCH)"
+ local FILE TARGET
+ FILE=$NDK_DIR/toolchains/$NAME-4.9/prebuilt/$SUBPATH
+ if [ ! -f "$FILE" ]; then
+ FILE=$NDK_DIR/toolchains/$NAME-4.8/prebuilt/$SUBPATH
+ if [ ! -f "$FILE" ]; then
+ FILE=
+ fi
+ fi
+ echo "$FILE"
+}
+
+# Find the path to an NDK's toolchain full prefix for a given architecture
+# $1: NDK install path
+# $2: NDK target architecture name
+# Out: install path + binary prefix (e.g.
+# ".../path/to/bin/arm-linux-androideabi-")
+get_ndk_toolchain_fullprefix () {
+ local NDK_DIR="$1"
+ local ARCH="$2"
+ local TARGET NAME HOST_OS HOST_ARCH GCC CONFIG
+
+ # NOTE: This will need to be updated if the NDK changes the names or moves
+ # the location of its prebuilt toolchains.
+ #
+ GCC=
+ HOST_OS=$(get_ndk_host_system)
+ HOST_ARCH=$(get_ndk_host_arch)
+ CONFIG=$(get_arch_gnu_config $ARCH)
+ GCC=$(get_ndk_toolchain_prebuilt \
+ "$NDK_DIR" "$ARCH" "$HOST_OS-$HOST_ARCH/bin/$CONFIG-gcc")
+ if [ -z "$GCC" -a "$HOST_ARCH" = "x86_64" ]; then
+ GCC=$(get_ndk_toolchain_prebuilt \
+ "$NDK_DIR" "$ARCH" "$HOST_OS-x86/bin/$CONFIG-gcc")
+ fi
+ if [ ! -f "$GCC" -a "$ARCH" = "x86" ]; then
+ # Special case, the x86 toolchain used to be incorrectly
+ # named i686-android-linux-gcc!
+ GCC=$(get_ndk_toolchain_prebuilt \
+ "$NDK_DIR" "$ARCH" "$HOST_OS-x86/bin/i686-android-linux-gcc")
+ fi
+ if [ -z "$GCC" ]; then
+ panic "Cannot find Android NDK toolchain for '$ARCH' architecture. \
+Please verify your NDK installation!"
+ fi
+ echo "${GCC%%gcc}"
+}
+
+# $1: NDK install path
+# $2: target architecture.
+get_ndk_gdbserver () {
+ local NDK_DIR="$1"
+ local ARCH=$2
+ local BINARY
+
+ # The location has moved after NDK r8
+ BINARY=$NDK_DIR/prebuilt/android-$ARCH/gdbserver/gdbserver
+ if [ ! -f "$BINARY" ]; then
+ BINARY=$(get_ndk_toolchain_prebuilt "$NDK_DIR" "$ARCH" gdbserver)
+ fi
+ echo "$BINARY"
+}
+
+# Check/probe the path to the Android toolchain installation. Always
+# use the NDK versions of gdb and gdbserver. They must match to avoid
+# issues when both binaries do not speak the same wire protocol.
+#
+if [ -z "$TOOLCHAIN" ]; then
+ ANDROID_TOOLCHAIN=$(get_ndk_toolchain_fullprefix \
+ "$ANDROID_NDK_ROOT" "$TARGET_ARCH")
+ ANDROID_TOOLCHAIN=$(dirname "$ANDROID_TOOLCHAIN")
+ log "Auto-config: --toolchain=$ANDROID_TOOLCHAIN"
+else
+ # Be flexible, allow one to specify either the install path or the bin
+ # sub-directory in --toolchain:
+ #
+ if [ -d "$TOOLCHAIN/bin" ]; then
+ TOOLCHAIN=$TOOLCHAIN/bin
+ fi
+ ANDROID_TOOLCHAIN=$TOOLCHAIN
+fi
+
+# Cosmetic: Remove trailing directory separator.
+ANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN%/}
+
+# Find host GDB client binary
+if [ -z "$GDB" ]; then
+ GDB=$(which $ANDROID_TOOLCHAIN/*-$GDBEXEPOSTFIX 2>/dev/null | head -1)
+ if [ -z "$GDB" ]; then
+ panic "Can't find Android gdb client in your path, check your \
+--toolchain or --gdb path."
+ fi
+ log "Host gdb client: $GDB"
+fi
+
+# Find gdbserver binary, we will later push it to /data/local/tmp
+# This ensures that both gdbserver and $GDB talk the same binary protocol,
+# otherwise weird problems will appear.
+#
+if [ -z "$GDBSERVER" ]; then
+ GDBSERVER=$(get_ndk_gdbserver "$ANDROID_NDK_ROOT" "$TARGET_ARCH")
+ if [ -z "$GDBSERVER" ]; then
+ panic "Can't find NDK gdbserver binary. use --gdbserver to specify \
+valid one!"
+ fi
+ log "Auto-config: --gdbserver=$GDBSERVER"
+fi
+
+# A unique ID for this script's session. This needs to be the same in all
+# sub-shell commands we're going to launch, so take the PID of the launcher
+# process.
+TMP_ID=$$
+
+# Temporary directory, will get cleaned up on exit.
+TMPDIR=/tmp/$USER-adb-gdb-tmp-$TMP_ID
+mkdir -p "$TMPDIR" && rm -rf "$TMPDIR"/*
+
+GDBSERVER_PIDFILE="$TMPDIR"/gdbserver-$TMP_ID.pid
+
+# If --force is specified, try to kill any gdbserver process started by the
+# same user on the device. Normally, these are killed automatically by the
+# script on exit, but there are a few corner cases where this would still
+# be needed.
+if [ "$FORCE" ]; then
+ GDBSERVER_PIDS=$(adb_shell ps | awk '$9 ~ /gdbserver/ { print $2; }')
+ for GDB_PID in $GDBSERVER_PIDS; do
+ log "Killing previous gdbserver (PID=$GDB_PID)"
+ adb_shell kill -9 $GDB_PID
+ done
+fi
+
+if [ "$START" ]; then
+ log "Starting $PROGRAM_NAME on device."
+ adb_shell am start -n $PACKAGE_NAME/$ACTIVITY ${START_URL:+-d "$START_URL"}
+ adb_shell ps | grep -q $PACKAGE_NAME
+ fail_panic "Could not start $PROGRAM_NAME on device. Are you sure the \
+package is installed?"
+fi
+
+# Return the timestamp of a given file, as number of seconds since epoch.
+# $1: file path
+# Out: file timestamp
+get_file_timestamp () {
+ stat -c %Y "$1" 2>/dev/null
+}
+
+# Allow several concurrent debugging sessions
+TARGET_GDBSERVER=/data/data/$PACKAGE_NAME/gdbserver-adb-gdb-$TMP_ID
+TMP_TARGET_GDBSERVER=/data/local/tmp/gdbserver-adb-gdb-$TMP_ID
+
+# Return the build fingerprint contained in a build.prop file.
+# $1: path to build.prop file
+get_build_fingerprint_from () {
+ cat "$1" | grep -e '^ro.build.fingerprint=' | cut -d= -f2
+}
+
+
+ORG_PULL_LIBS_DIR=$PULL_LIBS_DIR
+PULL_LIBS_DIR=${PULL_LIBS_DIR:-$DEFAULT_PULL_LIBS_DIR}
+
+HOST_FINGERPRINT=
+DEVICE_FINGERPRINT=$(adb_shell getprop ro.build.fingerprint)
+[[ "$DEVICE_FINGERPRINT" ]] || panic "Failed to get the device fingerprint"
+log "Device build fingerprint: $DEVICE_FINGERPRINT"
+
+# If --pull-libs-dir is not specified, and this is a platform build, look
+# if we can use the symbolic libraries under $ANDROID_PRODUCT_OUT/symbols/
+# directly, if the build fingerprint matches the device.
+if [ -z "$ORG_PULL_LIBS_DIR" -a \
+ "$ANDROID_PRODUCT_OUT" -a \
+ -f "$ANDROID_PRODUCT_OUT/system/build.prop" ]; then
+ ANDROID_FINGERPRINT=$(get_build_fingerprint_from \
+ "$ANDROID_PRODUCT_OUT"/system/build.prop)
+ log "Android build fingerprint: $ANDROID_FINGERPRINT"
+ if [ "$ANDROID_FINGERPRINT" = "$DEVICE_FINGERPRINT" ]; then
+ log "Perfect match!"
+ PULL_LIBS_DIR=$ANDROID_PRODUCT_OUT/symbols
+ HOST_FINGERPRINT=$ANDROID_FINGERPRINT
+ if [ "$PULL_LIBS" ]; then
+ log "Ignoring --pull-libs since the device and platform build \
+fingerprints match."
+ NO_PULL_LIBS=true
+ fi
+ fi
+fi
+
+# If neither --pull-libs an --no-pull-libs were specified, check the build
+# fingerprints of the device, and the cached system libraries on the host.
+#
+if [ -z "$NO_PULL_LIBS" -a -z "$PULL_LIBS" ]; then
+ if [ ! -f "$PULL_LIBS_DIR/build.fingerprint" ]; then
+ log "Auto-config: --pull-libs (no cached libraries)"
+ PULL_LIBS=true
+ else
+ HOST_FINGERPRINT=$(< "$PULL_LIBS_DIR/build.fingerprint")
+ log "Host build fingerprint: $HOST_FINGERPRINT"
+ if [ "$HOST_FINGERPRINT" == "$DEVICE_FINGERPRINT" ]; then
+ log "Auto-config: --no-pull-libs (fingerprint match)"
+ NO_PULL_LIBS=true
+ else
+ log "Auto-config: --pull-libs (fingerprint mismatch)"
+ PULL_LIBS=true
+ fi
+ fi
+fi
+
+# Extract the system libraries from the device if necessary.
+if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then
+ echo "Extracting system libraries into: $PULL_LIBS_DIR"
+fi
+
+mkdir -p "$PULL_LIBS_DIR"
+fail_panic "Can't create --libs-dir directory: $PULL_LIBS_DIR"
+
+# If requested, work for M-x gdb. The gdb indirections make it
+# difficult to pass --annotate=3 to the gdb binary itself.
+GDB_ARGS=
+if [ "$ANNOTATE" ]; then
+ GDB_ARGS=$GDB_ARGS" --annotate=$ANNOTATE"
+fi
+
+# Get the PID from the first argument or else find the PID of the
+# browser process.
+if [ -z "$PID" ]; then
+ PROCESSNAME=$PACKAGE_NAME
+ if [ "$SANDBOXED_INDEX" ]; then
+ PROCESSNAME=$PROCESSNAME:sandboxed_process$SANDBOXED_INDEX
+ elif [ "$SANDBOXED" ]; then
+ PROCESSNAME=$PROCESSNAME:sandboxed_process
+ PID=$(adb_shell ps | \
+ awk '$9 ~ /^'$PROCESSNAME'/ { print $2; }' | head -1)
+ elif [ "$PRIVILEGED_INDEX" ]; then
+ PROCESSNAME=$PROCESSNAME:privileged_process$PRIVILEGED_INDEX
+ elif [ "$PRIVILEGED" ]; then
+ PROCESSNAME=$PROCESSNAME:privileged_process
+ PID=$(adb_shell ps | \
+ awk '$9 ~ /^'$PROCESSNAME'/ { print $2; }' | head -1)
+ fi
+ if [ -z "$PID" ]; then
+ PID=$(adb_shell ps | \
+ awk '$9 == "'$PROCESSNAME'" { print $2; }' | head -1)
+ fi
+ if [ -z "$PID" ]; then
+ if [ "$START" ]; then
+ panic "Can't find application process PID, did it crash?"
+ else
+ panic "Can't find application process PID, are you sure it is \
+running? Try using --start."
+ fi
+ fi
+ log "Found process PID: $PID"
+elif [ "$SANDBOXED" ]; then
+ echo "WARNING: --sandboxed option ignored due to use of --pid."
+elif [ "$PRIVILEGED" ]; then
+ echo "WARNING: --privileged option ignored due to use of --pid."
+fi
+
+# Determine if 'adb shell' runs as root or not.
+# If so, we can launch gdbserver directly, otherwise, we have to
+# use run-as $PACKAGE_NAME ..., which requires the package to be debuggable.
+#
+if [ "$SU_PREFIX" ]; then
+ # Need to check that this works properly.
+ SU_PREFIX_TEST_LOG=$TMPDIR/su-prefix.log
+ adb_shell $SU_PREFIX \"echo "foo"\" > $SU_PREFIX_TEST_LOG 2>&1
+ if [ $? != 0 -o "$(cat $SU_PREFIX_TEST_LOG)" != "foo" ]; then
+ echo "ERROR: Cannot use '$SU_PREFIX' as a valid su prefix:"
+ echo "$ adb shell $SU_PREFIX \"echo foo\""
+ cat $SU_PREFIX_TEST_LOG
+ exit 1
+ fi
+ COMMAND_PREFIX="$SU_PREFIX \""
+ COMMAND_SUFFIX="\""
+else
+ SHELL_UID=$(adb shell cat /proc/self/status | \
+ awk '$1 == "Uid:" { print $2; }')
+ log "Shell UID: $SHELL_UID"
+ if [ "$SHELL_UID" != 0 -o -n "$NO_ROOT" ]; then
+ COMMAND_PREFIX="run-as $PACKAGE_NAME"
+ COMMAND_SUFFIX=
+ else
+ COMMAND_PREFIX=
+ COMMAND_SUFFIX=
+ fi
+fi
+log "Command prefix: '$COMMAND_PREFIX'"
+log "Command suffix: '$COMMAND_SUFFIX'"
+
+# Pull device's system libraries that are mapped by our process.
+# Pulling all system libraries is too long, so determine which ones
+# we need by looking at /proc/$PID/maps instead
+if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then
+ echo "Extracting system libraries into: $PULL_LIBS_DIR"
+ MAPPINGS=$(adb_shell $COMMAND_PREFIX cat /proc/$PID/maps $COMMAND_SUFFIX)
+ if [ $? != 0 ]; then
+ echo "ERROR: Could not list process's memory mappings."
+ if [ "$SU_PREFIX" ]; then
+ panic "Are you sure your --su-prefix is correct?"
+ else
+ panic "Use --su-prefix if the application is not debuggable."
+ fi
+ fi
+ # Remove the fingerprint file in case pulling one of the libs fails.
+ rm -f "$PULL_LIBS_DIR/build.fingerprint"
+ SYSTEM_LIBS=$(echo "$MAPPINGS" | \
+ awk '$6 ~ /\/system\/.*\.so$/ { print $6; }' | sort -u)
+ for SYSLIB in /system/bin/linker $SYSTEM_LIBS; do
+ echo "Pulling from device: $SYSLIB"
+ DST_FILE=$PULL_LIBS_DIR$SYSLIB
+ DST_DIR=$(dirname "$DST_FILE")
+ mkdir -p "$DST_DIR" && adb pull $SYSLIB "$DST_FILE" 2>/dev/null
+ fail_panic "Could not pull $SYSLIB from device !?"
+ done
+ echo "Writing the device fingerprint"
+ echo "$DEVICE_FINGERPRINT" > "$PULL_LIBS_DIR/build.fingerprint"
+fi
+
+# Find all the sub-directories of $PULL_LIBS_DIR, up to depth 4
+# so we can add them to solib-search-path later.
+SOLIB_DIRS=$(find $PULL_LIBS_DIR -mindepth 1 -maxdepth 4 -type d | \
+ grep -v "^$" | tr '\n' ':')
+
+# This is a re-implementation of gdbclient, where we use compatible
+# versions of gdbserver and $GDBNAME to ensure that everything works
+# properly.
+#
+
+# Push gdbserver to the device
+log "Pushing gdbserver $GDBSERVER to $TARGET_GDBSERVER"
+adb push $GDBSERVER $TMP_TARGET_GDBSERVER &>/dev/null
+adb shell $COMMAND_PREFIX cp $TMP_TARGET_GDBSERVER $TARGET_GDBSERVER
+adb shell rm $TMP_TARGET_GDBSERVER
+fail_panic "Could not copy gdbserver to the device!"
+
+if [ -z "$PORT" ]; then
+ PORT=5039
+fi
+HOST_PORT=$PORT
+TARGET_PORT=$PORT
+
+# Select correct app_process for architecture.
+case $TARGET_ARCH in
+ arm|x86|mips) GDBEXEC=app_process32;;
+ arm64|x86_64) GDBEXEC=app_process64;;
+ *) fail_panic "Unknown app_process for architecture!";;
+esac
+
+# Default to app_process if bit-width specific process isn't found.
+adb_shell ls /system/bin/$GDBEXEC
+if [ $? != 0 ]; then
+ GDBEXEC=app_process
+fi
+
+# Detect AddressSanitizer setup on the device. In that case app_process is a
+# script, and the real executable is app_process.real.
+GDBEXEC_ASAN=app_process.real
+adb_shell ls /system/bin/$GDBEXEC_ASAN
+if [ $? == 0 ]; then
+ GDBEXEC=$GDBEXEC_ASAN
+fi
+
+# Pull the app_process binary from the device.
+log "Pulling $GDBEXEC from device"
+adb pull /system/bin/$GDBEXEC "$TMPDIR"/$GDBEXEC &>/dev/null
+fail_panic "Could not retrieve $GDBEXEC from the device!"
+
+# Setup network redirection
+log "Setting network redirection (host:$HOST_PORT -> device:$TARGET_PORT)"
+adb forward tcp:$HOST_PORT tcp:$TARGET_PORT
+fail_panic "Could not setup network redirection from \
+host:localhost:$HOST_PORT to device:localhost:$TARGET_PORT!"
+
+# Start gdbserver in the background
+# Note that using run-as requires the package to be debuggable.
+#
+# If not, this will fail horribly. The alternative is to run the
+# program as root, which requires of course root privileges.
+# Maybe we should add a --root option to enable this?
+#
+log "Starting gdbserver in the background:"
+GDBSERVER_LOG=$TMPDIR/gdbserver-$TMP_ID.log
+log "adb shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \
+ --attach $PID $COMMAND_SUFFIX"
+"$ADB" shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \
+ --attach $PID $COMMAND_SUFFIX > $GDBSERVER_LOG 2>&1 &
+GDBSERVER_PID=$!
+echo "$GDBSERVER_PID" > $GDBSERVER_PIDFILE
+log "background job pid: $GDBSERVER_PID"
+
+# Sleep to allow gdbserver to attach to the remote process and be
+# ready to connect to.
+log "Sleeping ${ATTACH_DELAY}s to allow gdbserver to attach."
+sleep "$ATTACH_DELAY"
+log "Job control: $(jobs -l)"
+STATE=$(jobs -l | awk '$2 == "'$GDBSERVER_PID'" { print $3; }')
+if [ "$STATE" != "Running" ]; then
+ echo "ERROR: GDBServer either failed to run or attach to PID $PID!"
+ if [ $(adb_shell su -c getenforce) != "Permissive" ]; then
+ echo "Device mode is Enforcing. Changing Device mode to Permissive "
+ $(adb_shell su -c setenforce 0)
+ if [ $(adb_shell su -c getenforce) != "Permissive" ]; then
+ echo "ERROR: Failed to Change Device mode to Permissive"
+ echo "Failure log (use --verbose for more information):"
+ cat $GDBSERVER_LOG
+ exit 1
+ fi
+ else
+ echo "Failure log (use --verbose for more information):"
+ cat $GDBSERVER_LOG
+ exit 1
+ fi
+fi
+
+# Generate a file containing useful GDB initialization commands
+readonly COMMANDS=$TMPDIR/gdb.init
+log "Generating GDB initialization commands file: $COMMANDS"
+echo -n "" > $COMMANDS
+echo "set print pretty 1" >> $COMMANDS
+echo "python" >> $COMMANDS
+echo "import sys" >> $COMMANDS
+echo "sys.path.insert(0, '$CHROMIUM_SRC/tools/gdb/')" >> $COMMANDS
+echo "try:" >> $COMMANDS
+echo " import gdb_chrome" >> $COMMANDS
+echo "finally:" >> $COMMANDS
+echo " sys.path.pop(0)" >> $COMMANDS
+echo "end" >> $COMMANDS
+echo "file $TMPDIR/$GDBEXEC" >> $COMMANDS
+echo "directory $CHROMIUM_SRC" >> $COMMANDS
+echo "set solib-absolute-prefix $PULL_LIBS_DIR" >> $COMMANDS
+echo "set solib-search-path $SOLIB_DIRS:$PULL_LIBS_DIR:$SYMBOL_DIR" \
+ >> $COMMANDS
+echo "echo Attaching and reading symbols, this may take a while.." \
+ >> $COMMANDS
+echo "target remote :$HOST_PORT" >> $COMMANDS
+
+if [ "$GDBINIT" ]; then
+ cat "$GDBINIT" >> $COMMANDS
+fi
+
+if [ "$VERBOSE" -gt 0 ]; then
+ echo "### START $COMMANDS"
+ cat $COMMANDS
+ echo "### END $COMMANDS"
+fi
+
+log "Launching gdb client: $GDB $GDB_ARGS -x $COMMANDS"
+$GDB $GDB_ARGS -x $COMMANDS &&
+rm -f "$GDBSERVER_PIDFILE"
diff --git a/build/android/adb_gdb_android_webview_shell b/build/android/adb_gdb_android_webview_shell
new file mode 100644
index 00000000000..f685fda77c5
--- /dev/null
+++ b/build/android/adb_gdb_android_webview_shell
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# 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.
+#
+# Attach to or start a ContentShell process and debug it.
+# See --help for details.
+#
+PROGDIR=$(dirname "$0")
+export ADB_GDB_PROGNAME=$(basename "$0")
+export ADB_GDB_ACTIVITY=.AwShellActivity
+"$PROGDIR"/adb_gdb \
+ --program-name=AwShellApplication \
+ --package-name=org.chromium.android_webview.shell \
+ "$@"
diff --git a/build/android/adb_gdb_blimp_client b/build/android/adb_gdb_blimp_client
new file mode 100644
index 00000000000..3c2e21d6b90
--- /dev/null
+++ b/build/android/adb_gdb_blimp_client
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+#
+# Attach to or start a Blimp process and debug it.
+# See --help for details.
+#
+PROGDIR=$(dirname "$0")
+export ADB_GDB_PROGNAME=$(basename "$0")
+export ADB_GDB_ACTIVITY=org.chromium.blimp.BlimpRendererActivity
+"$PROGDIR"/adb_gdb \
+ --program-name=Blimp \
+ --package-name=org.chromium.blimp \
+ "$@"
diff --git a/build/android/adb_gdb_chrome_public b/build/android/adb_gdb_chrome_public
new file mode 100644
index 00000000000..4366c838e78
--- /dev/null
+++ b/build/android/adb_gdb_chrome_public
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+#
+# Attach to or start a ChromePublic process and debug it.
+# See --help for details.
+#
+PROGDIR=$(dirname "$0")
+export ADB_GDB_PROGNAME=$(basename "$0")
+export ADB_GDB_ACTIVITY=com.google.android.apps.chrome.Main
+"$PROGDIR"/adb_gdb \
+ --program-name=ChromePublic \
+ --package-name=org.chromium.chrome \
+ "$@"
diff --git a/build/android/adb_gdb_content_shell b/build/android/adb_gdb_content_shell
new file mode 100644
index 00000000000..18e1a61d893
--- /dev/null
+++ b/build/android/adb_gdb_content_shell
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# 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.
+#
+# Attach to or start a ContentShell process and debug it.
+# See --help for details.
+#
+PROGDIR=$(dirname "$0")
+export ADB_GDB_PROGNAME=$(basename "$0")
+export ADB_GDB_ACTIVITY=.ContentShellActivity
+"$PROGDIR"/adb_gdb \
+ --program-name=ContentShell \
+ --package-name=org.chromium.content_shell_apk \
+ "$@"
diff --git a/build/android/adb_gdb_cronet_sample b/build/android/adb_gdb_cronet_sample
new file mode 100644
index 00000000000..8d0c864d133
--- /dev/null
+++ b/build/android/adb_gdb_cronet_sample
@@ -0,0 +1,16 @@
+#!/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.
+#
+# Attach to or start a ContentShell process and debug it.
+# See --help for details.
+#
+PROGDIR=$(dirname "$0")
+export ADB_GDB_PROGNAME=$(basename "$0")
+export ADB_GDB_ACTIVITY=.CronetSampleActivity
+"$PROGDIR"/adb_gdb \
+ --program-name=CronetSample \
+ --package-name=org.chromium.cronet_sample_apk \
+ "$@"
diff --git a/build/android/adb_gdb_mojo_shell b/build/android/adb_gdb_mojo_shell
new file mode 100644
index 00000000000..ba91149cce9
--- /dev/null
+++ b/build/android/adb_gdb_mojo_shell
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# 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.
+#
+# Attach to or start a ContentShell process and debug it.
+# See --help for details.
+#
+PROGDIR=$(dirname "$0")
+export ADB_GDB_PROGNAME=$(basename "$0")
+export ADB_GDB_ACTIVITY=.MojoShellActivity
+"$PROGDIR"/adb_gdb \
+ --program-name=MojoShell \
+ --package-name=org.chromium.mojo_shell_apk \
+ "$@"
diff --git a/build/android/adb_install_apk.py b/build/android/adb_install_apk.py
new file mode 100644
index 00000000000..7904b41a531
--- /dev/null
+++ b/build/android/adb_install_apk.py
@@ -0,0 +1,132 @@
+#!/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.
+
+"""Utility script to install APKs from the command line quickly."""
+
+import argparse
+import glob
+import logging
+import os
+import sys
+
+import devil_chromium
+from devil.android import apk_helper
+from devil.android import device_blacklist
+from devil.android import device_errors
+from devil.android import device_utils
+from devil.utils import run_tests_helper
+from pylib import constants
+
+
+def main():
+ parser = argparse.ArgumentParser()
+
+ apk_group = parser.add_mutually_exclusive_group(required=True)
+ apk_group.add_argument('--apk', dest='apk_name',
+ help='DEPRECATED The name of the apk containing the'
+ ' application (with the .apk extension).')
+ apk_group.add_argument('apk_path', nargs='?',
+ help='The path to the APK to install.')
+
+ # TODO(jbudorick): Remove once no clients pass --apk_package
+ parser.add_argument('--apk_package', help='DEPRECATED unused')
+ parser.add_argument('--split',
+ action='append',
+ dest='splits',
+ help='A glob matching the apk splits. '
+ 'Can be specified multiple times.')
+ parser.add_argument('--keep_data',
+ action='store_true',
+ default=False,
+ help='Keep the package data when installing '
+ 'the application.')
+ parser.add_argument('--debug', action='store_const', const='Debug',
+ dest='build_type',
+ default=os.environ.get('BUILDTYPE', 'Debug'),
+ help='If set, run test suites under out/Debug. '
+ 'Default is env var BUILDTYPE or Debug')
+ parser.add_argument('--release', action='store_const', const='Release',
+ dest='build_type',
+ help='If set, run test suites under out/Release. '
+ 'Default is env var BUILDTYPE or Debug.')
+ parser.add_argument('-d', '--device', dest='devices', action='append',
+ default=[],
+ help='Target device for apk to install on. Enter multiple'
+ ' times for multiple devices.')
+ parser.add_argument('--adb-path', type=os.path.abspath,
+ help='Absolute path to the adb binary to use.')
+ parser.add_argument('--blacklist-file', help='Device blacklist JSON file.')
+ parser.add_argument('-v', '--verbose', action='count',
+ help='Enable verbose logging.')
+ parser.add_argument('--downgrade', action='store_true',
+ help='If set, allows downgrading of apk.')
+ parser.add_argument('--timeout', type=int,
+ default=device_utils.DeviceUtils.INSTALL_DEFAULT_TIMEOUT,
+ help='Seconds to wait for APK installation. '
+ '(default: %(default)s)')
+
+ args = parser.parse_args()
+
+ run_tests_helper.SetLogLevel(args.verbose)
+ constants.SetBuildType(args.build_type)
+
+ devil_chromium.Initialize(
+ output_directory=constants.GetOutDirectory(),
+ adb_path=args.adb_path)
+
+ apk = args.apk_path or args.apk_name
+ if not apk.endswith('.apk'):
+ apk += '.apk'
+ if not os.path.exists(apk):
+ apk = os.path.join(constants.GetOutDirectory(), 'apks', apk)
+ if not os.path.exists(apk):
+ parser.error('%s not found.' % apk)
+
+ if args.splits:
+ splits = []
+ base_apk_package = apk_helper.ApkHelper(apk).GetPackageName()
+ for split_glob in args.splits:
+ apks = [f for f in glob.glob(split_glob) if f.endswith('.apk')]
+ if not apks:
+ logging.warning('No apks matched for %s.', split_glob)
+ for f in apks:
+ helper = apk_helper.ApkHelper(f)
+ if (helper.GetPackageName() == base_apk_package
+ and helper.GetSplitName()):
+ splits.append(f)
+
+ blacklist = (device_blacklist.Blacklist(args.blacklist_file)
+ if args.blacklist_file
+ else None)
+ devices = device_utils.DeviceUtils.HealthyDevices(blacklist=blacklist,
+ device_arg=args.devices)
+
+ def blacklisting_install(device):
+ try:
+ if args.splits:
+ device.InstallSplitApk(apk, splits, reinstall=args.keep_data,
+ allow_downgrade=args.downgrade)
+ else:
+ device.Install(apk, reinstall=args.keep_data,
+ allow_downgrade=args.downgrade,
+ timeout=args.timeout)
+ except device_errors.CommandFailedError:
+ logging.exception('Failed to install %s', args.apk_name)
+ if blacklist:
+ blacklist.Extend([str(device)], reason='install_failure')
+ logging.warning('Blacklisting %s', str(device))
+ except device_errors.CommandTimeoutError:
+ logging.exception('Timed out while installing %s', args.apk_name)
+ if blacklist:
+ blacklist.Extend([str(device)], reason='install_timeout')
+ logging.warning('Blacklisting %s', str(device))
+
+ device_utils.DeviceUtils.parallel(devices).pMap(blacklisting_install)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
+
diff --git a/build/android/adb_kill_android_webview_shell b/build/android/adb_kill_android_webview_shell
new file mode 100644
index 00000000000..5f287f08266
--- /dev/null
+++ b/build/android/adb_kill_android_webview_shell
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+#
+# Kill a running android webview shell.
+#
+# Assumes you have sourced the build/android/envsetup.sh script.
+
+SHELL_PID_LINES=$(adb shell ps | grep ' org.chromium.android_webview.shell')
+VAL=$(echo "$SHELL_PID_LINES" | wc -l)
+if [ $VAL -lt 1 ] ; then
+ echo "Not running android webview shell."
+else
+ SHELL_PID=$(echo $SHELL_PID_LINES | awk '{print $2}')
+ if [ "$SHELL_PID" != "" ] ; then
+ set -x
+ adb shell kill $SHELL_PID
+ set -
+ else
+ echo "Android webview shell does not appear to be running."
+ fi
+fi
diff --git a/build/android/adb_kill_blimp_client b/build/android/adb_kill_blimp_client
new file mode 100644
index 00000000000..6221e45234a
--- /dev/null
+++ b/build/android/adb_kill_blimp_client
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+#
+# Kill a running instance of Blimp.
+#
+# Assumes you have sourced the build/android/envsetup.sh script.
+
+SHELL_PID_LINES=$(adb shell ps | grep -w 'org.chromium.blimp')
+VAL=$(echo "$SHELL_PID_LINES" | wc -l)
+if [ $VAL -lt 1 ] ; then
+ echo "Not running Blimp."
+else
+ SHELL_PID=$(echo $SHELL_PID_LINES | awk '{print $2}')
+ if [ "$SHELL_PID" != "" ] ; then
+ set -x
+ adb shell kill $SHELL_PID
+ set -
+ else
+ echo "Blimp does not appear to be running."
+ fi
+fi
diff --git a/build/android/adb_kill_chrome_public b/build/android/adb_kill_chrome_public
new file mode 100644
index 00000000000..5b539a043d4
--- /dev/null
+++ b/build/android/adb_kill_chrome_public
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+#
+# Kill a running instance of ChromePublic.
+#
+# Assumes you have sourced the build/android/envsetup.sh script.
+
+SHELL_PID_LINES=$(adb shell ps | grep -w 'org.chromium.chrome')
+VAL=$(echo "$SHELL_PID_LINES" | wc -l)
+if [ $VAL -lt 1 ] ; then
+ echo "Not running ChromePublic."
+else
+ SHELL_PID=$(echo $SHELL_PID_LINES | awk '{print $2}')
+ if [ "$SHELL_PID" != "" ] ; then
+ set -x
+ adb shell kill $SHELL_PID
+ set -
+ else
+ echo "ChromePublic does not appear to be running."
+ fi
+fi
diff --git a/build/android/adb_kill_content_shell b/build/android/adb_kill_content_shell
new file mode 100644
index 00000000000..e379dd47149
--- /dev/null
+++ b/build/android/adb_kill_content_shell
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# 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.
+#
+# Kill a running content shell.
+#
+# Assumes you have sourced the build/android/envsetup.sh script.
+
+SHELL_PID_LINES=$(adb shell ps | grep ' org.chromium.content_shell_apk')
+VAL=$(echo "$SHELL_PID_LINES" | wc -l)
+if [ $VAL -lt 1 ] ; then
+ echo "Not running Content shell."
+else
+ SHELL_PID=$(echo $SHELL_PID_LINES | awk '{print $2}')
+ if [ "$SHELL_PID" != "" ] ; then
+ set -x
+ adb shell kill $SHELL_PID
+ set -
+ else
+ echo "Content shell does not appear to be running."
+ fi
+fi
diff --git a/build/android/adb_logcat_monitor.py b/build/android/adb_logcat_monitor.py
new file mode 100644
index 00000000000..d3cc67dbcc8
--- /dev/null
+++ b/build/android/adb_logcat_monitor.py
@@ -0,0 +1,156 @@
+#!/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.
+
+"""Saves logcats from all connected devices.
+
+Usage: adb_logcat_monitor.py []
+
+This script will repeatedly poll adb for new devices and save logcats
+inside the directory, which it attempts to create. The
+script will run until killed by an external signal. To test, run the
+script in a shell and -C it after a while. It should be
+resilient across phone disconnects and reconnects and start the logcat
+early enough to not miss anything.
+"""
+
+import logging
+import os
+import re
+import shutil
+import signal
+import subprocess
+import sys
+import time
+
+# Map from device_id -> (process, logcat_num)
+devices = {}
+
+
+class TimeoutException(Exception):
+ """Exception used to signal a timeout."""
+ pass
+
+
+class SigtermError(Exception):
+ """Exception used to catch a sigterm."""
+ pass
+
+
+def StartLogcatIfNecessary(device_id, adb_cmd, base_dir):
+ """Spawns a adb logcat process if one is not currently running."""
+ process, logcat_num = devices[device_id]
+ if process:
+ if process.poll() is None:
+ # Logcat process is still happily running
+ return
+ else:
+ logging.info('Logcat for device %s has died', device_id)
+ error_filter = re.compile('- waiting for device -')
+ for line in process.stderr:
+ if not error_filter.match(line):
+ logging.error(device_id + ': ' + line)
+
+ logging.info('Starting logcat %d for device %s', logcat_num,
+ device_id)
+ logcat_filename = 'logcat_%s_%03d' % (device_id, logcat_num)
+ logcat_file = open(os.path.join(base_dir, logcat_filename), 'w')
+ process = subprocess.Popen([adb_cmd, '-s', device_id,
+ 'logcat', '-v', 'threadtime'],
+ stdout=logcat_file,
+ stderr=subprocess.PIPE)
+ devices[device_id] = (process, logcat_num + 1)
+
+
+def GetAttachedDevices(adb_cmd):
+ """Gets the device list from adb.
+
+ We use an alarm in this function to avoid deadlocking from an external
+ dependency.
+
+ Args:
+ adb_cmd: binary to run adb
+
+ Returns:
+ list of devices or an empty list on timeout
+ """
+ signal.alarm(2)
+ try:
+ out, err = subprocess.Popen([adb_cmd, 'devices'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()
+ if err:
+ logging.warning('adb device error %s', err.strip())
+ return re.findall('^(\\S+)\tdevice$', out, re.MULTILINE)
+ except TimeoutException:
+ logging.warning('"adb devices" command timed out')
+ return []
+ except (IOError, OSError):
+ logging.exception('Exception from "adb devices"')
+ return []
+ finally:
+ signal.alarm(0)
+
+
+def main(base_dir, adb_cmd='adb'):
+ """Monitor adb forever. Expects a SIGINT (Ctrl-C) to kill."""
+ # We create the directory to ensure 'run once' semantics
+ if os.path.exists(base_dir):
+ print 'adb_logcat_monitor: %s already exists? Cleaning' % base_dir
+ shutil.rmtree(base_dir, ignore_errors=True)
+
+ os.makedirs(base_dir)
+ logging.basicConfig(filename=os.path.join(base_dir, 'eventlog'),
+ level=logging.INFO,
+ format='%(asctime)-2s %(levelname)-8s %(message)s')
+
+ # Set up the alarm for calling 'adb devices'. This is to ensure
+ # our script doesn't get stuck waiting for a process response
+ def TimeoutHandler(_signum, _unused_frame):
+ raise TimeoutException()
+ signal.signal(signal.SIGALRM, TimeoutHandler)
+
+ # Handle SIGTERMs to ensure clean shutdown
+ def SigtermHandler(_signum, _unused_frame):
+ raise SigtermError()
+ signal.signal(signal.SIGTERM, SigtermHandler)
+
+ logging.info('Started with pid %d', os.getpid())
+ pid_file_path = os.path.join(base_dir, 'LOGCAT_MONITOR_PID')
+
+ try:
+ with open(pid_file_path, 'w') as f:
+ f.write(str(os.getpid()))
+ while True:
+ for device_id in GetAttachedDevices(adb_cmd):
+ if not device_id in devices:
+ subprocess.call([adb_cmd, '-s', device_id, 'logcat', '-c'])
+ devices[device_id] = (None, 0)
+
+ for device in devices:
+ # This will spawn logcat watchers for any device ever detected
+ StartLogcatIfNecessary(device, adb_cmd, base_dir)
+
+ time.sleep(5)
+ except SigtermError:
+ logging.info('Received SIGTERM, shutting down')
+ except: # pylint: disable=bare-except
+ logging.exception('Unexpected exception in main.')
+ finally:
+ for process, _ in devices.itervalues():
+ if process:
+ try:
+ process.terminate()
+ except OSError:
+ pass
+ os.remove(pid_file_path)
+
+
+if __name__ == '__main__':
+ if 2 <= len(sys.argv) <= 3:
+ print 'adb_logcat_monitor: Initializing'
+ sys.exit(main(*sys.argv[1:3]))
+
+ print 'Usage: %s []' % sys.argv[0]
diff --git a/build/android/adb_logcat_printer.py b/build/android/adb_logcat_printer.py
new file mode 100644
index 00000000000..a715170759d
--- /dev/null
+++ b/build/android/adb_logcat_printer.py
@@ -0,0 +1,222 @@
+#!/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.
+
+"""Shutdown adb_logcat_monitor and print accumulated logs.
+
+To test, call './adb_logcat_printer.py ' where
+ contains 'adb logcat -v threadtime' files named as
+logcat__
+
+The script will print the files to out, and will combine multiple
+logcats from a single device if there is overlap.
+
+Additionally, if a /LOGCAT_MONITOR_PID exists, the script
+will attempt to terminate the contained PID by sending a SIGINT and
+monitoring for the deletion of the aforementioned file.
+"""
+# pylint: disable=W0702
+
+import cStringIO
+import logging
+import optparse
+import os
+import re
+import signal
+import sys
+import time
+
+
+# Set this to debug for more verbose output
+LOG_LEVEL = logging.INFO
+
+
+def CombineLogFiles(list_of_lists, logger):
+ """Splices together multiple logcats from the same device.
+
+ Args:
+ list_of_lists: list of pairs (filename, list of timestamped lines)
+ logger: handler to log events
+
+ Returns:
+ list of lines with duplicates removed
+ """
+ cur_device_log = ['']
+ for cur_file, cur_file_lines in list_of_lists:
+ # Ignore files with just the logcat header
+ if len(cur_file_lines) < 2:
+ continue
+ common_index = 0
+ # Skip this step if list just has empty string
+ if len(cur_device_log) > 1:
+ try:
+ line = cur_device_log[-1]
+ # Used to make sure we only splice on a timestamped line
+ if re.match(r'^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3} ', line):
+ common_index = cur_file_lines.index(line)
+ else:
+ logger.warning('splice error - no timestamp in "%s"?', line.strip())
+ except ValueError:
+ # The last line was valid but wasn't found in the next file
+ cur_device_log += ['***** POSSIBLE INCOMPLETE LOGCAT *****']
+ logger.info('Unable to splice %s. Incomplete logcat?', cur_file)
+
+ cur_device_log += ['*'*30 + ' %s' % cur_file]
+ cur_device_log.extend(cur_file_lines[common_index:])
+
+ return cur_device_log
+
+
+def FindLogFiles(base_dir):
+ """Search a directory for logcat files.
+
+ Args:
+ base_dir: directory to search
+
+ Returns:
+ Mapping of device_id to a sorted list of file paths for a given device
+ """
+ logcat_filter = re.compile(r'^logcat_(\S+)_(\d+)$')
+ # list of tuples (, , )
+ filtered_list = []
+ for cur_file in os.listdir(base_dir):
+ matcher = logcat_filter.match(cur_file)
+ if matcher:
+ filtered_list += [(matcher.group(1), int(matcher.group(2)),
+ os.path.join(base_dir, cur_file))]
+ filtered_list.sort()
+ file_map = {}
+ for device_id, _, cur_file in filtered_list:
+ if device_id not in file_map:
+ file_map[device_id] = []
+
+ file_map[device_id] += [cur_file]
+ return file_map
+
+
+def GetDeviceLogs(log_filenames, logger):
+ """Read log files, combine and format.
+
+ Args:
+ log_filenames: mapping of device_id to sorted list of file paths
+ logger: logger handle for logging events
+
+ Returns:
+ list of formatted device logs, one for each device.
+ """
+ device_logs = []
+
+ for device, device_files in log_filenames.iteritems():
+ logger.debug('%s: %s', device, str(device_files))
+ device_file_lines = []
+ for cur_file in device_files:
+ with open(cur_file) as f:
+ device_file_lines += [(cur_file, f.read().splitlines())]
+ combined_lines = CombineLogFiles(device_file_lines, logger)
+ # Prepend each line with a short unique ID so it's easy to see
+ # when the device changes. We don't use the start of the device
+ # ID because it can be the same among devices. Example lines:
+ # AB324: foo
+ # AB324: blah
+ device_logs += [('\n' + device[-5:] + ': ').join(combined_lines)]
+ return device_logs
+
+
+def ShutdownLogcatMonitor(base_dir, logger):
+ """Attempts to shutdown adb_logcat_monitor and blocks while waiting."""
+ try:
+ monitor_pid_path = os.path.join(base_dir, 'LOGCAT_MONITOR_PID')
+ with open(monitor_pid_path) as f:
+ monitor_pid = int(f.readline())
+
+ logger.info('Sending SIGTERM to %d', monitor_pid)
+ os.kill(monitor_pid, signal.SIGTERM)
+ i = 0
+ while True:
+ time.sleep(.2)
+ if not os.path.exists(monitor_pid_path):
+ return
+ if not os.path.exists('/proc/%d' % monitor_pid):
+ logger.warning('Monitor (pid %d) terminated uncleanly?', monitor_pid)
+ return
+ logger.info('Waiting for logcat process to terminate.')
+ i += 1
+ if i >= 10:
+ logger.warning('Monitor pid did not terminate. Continuing anyway.')
+ return
+
+ except (ValueError, IOError, OSError):
+ logger.exception('Error signaling logcat monitor - continuing')
+
+
+def main(argv):
+ parser = optparse.OptionParser(usage='Usage: %prog [options] ')
+ parser.add_option('--output-path',
+ help='Output file path (if unspecified, prints to stdout)')
+ options, args = parser.parse_args(argv)
+ if len(args) != 1:
+ parser.error('Wrong number of unparsed args')
+ base_dir = args[0]
+
+ log_stringio = cStringIO.StringIO()
+ logger = logging.getLogger('LogcatPrinter')
+ logger.setLevel(LOG_LEVEL)
+ sh = logging.StreamHandler(log_stringio)
+ sh.setFormatter(logging.Formatter('%(asctime)-2s %(levelname)-8s'
+ ' %(message)s'))
+ logger.addHandler(sh)
+
+ if options.output_path:
+ if not os.path.exists(os.path.dirname(options.output_path)):
+ logger.warning('Output dir %s doesn\'t exist. Creating it.',
+ os.path.dirname(options.output_path))
+ os.makedirs(os.path.dirname(options.output_path))
+ output_file = open(options.output_path, 'w')
+ logger.info('Dumping logcat to local file %s. If running in a build, '
+ 'this file will likely will be uploaded to google storage '
+ 'in a later step. It can be downloaded from there.',
+ options.output_path)
+ else:
+ output_file = sys.stdout
+
+ try:
+ # Wait at least 5 seconds after base_dir is created before printing.
+ #
+ # The idea is that 'adb logcat > file' output consists of 2 phases:
+ # 1 Dump all the saved logs to the file
+ # 2 Stream log messages as they are generated
+ #
+ # We want to give enough time for phase 1 to complete. There's no
+ # good method to tell how long to wait, but it usually only takes a
+ # second. On most bots, this code path won't occur at all, since
+ # adb_logcat_monitor.py command will have spawned more than 5 seconds
+ # prior to called this shell script.
+ try:
+ sleep_time = 5 - (time.time() - os.path.getctime(base_dir))
+ except OSError:
+ sleep_time = 5
+ if sleep_time > 0:
+ logger.warning('Monitor just started? Sleeping %.1fs', sleep_time)
+ time.sleep(sleep_time)
+
+ assert os.path.exists(base_dir), '%s does not exist' % base_dir
+ ShutdownLogcatMonitor(base_dir, logger)
+ separator = '\n' + '*' * 80 + '\n\n'
+ for log in GetDeviceLogs(FindLogFiles(base_dir), logger):
+ output_file.write(log)
+ output_file.write(separator)
+ with open(os.path.join(base_dir, 'eventlog')) as f:
+ output_file.write('\nLogcat Monitor Event Log\n')
+ output_file.write(f.read())
+ except:
+ logger.exception('Unexpected exception')
+
+ logger.info('Done.')
+ sh.flush()
+ output_file.write('\nLogcat Printer Event Log\n')
+ output_file.write(log_stringio.getvalue())
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/build/android/adb_profile_chrome b/build/android/adb_profile_chrome
new file mode 100644
index 00000000000..d3244ffdf60
--- /dev/null
+++ b/build/android/adb_profile_chrome
@@ -0,0 +1,9 @@
+#!/bin/bash
+#
+# 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.
+#
+# Start / stop profiling in chrome.
+CATAPULT_DIR="$(dirname "$0")"/../../third_party/catapult
+exec "${CATAPULT_DIR}"/systrace/bin/adb_profile_chrome "$@"
diff --git a/build/android/adb_profile_chrome_startup b/build/android/adb_profile_chrome_startup
new file mode 100644
index 00000000000..d5836cdf702
--- /dev/null
+++ b/build/android/adb_profile_chrome_startup
@@ -0,0 +1,9 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+#
+# Start / stop profiling for chrome startup.
+CATAPULT_DIR="$(dirname "$0")"/../../third_party/catapult
+exec "${CATAPULT_DIR}"/systrace/bin/adb_profile_chrome_startup "$@"
diff --git a/build/android/adb_reverse_forwarder.py b/build/android/adb_reverse_forwarder.py
new file mode 100644
index 00000000000..b0a8dc357fb
--- /dev/null
+++ b/build/android/adb_reverse_forwarder.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env 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.
+
+"""Command line tool for forwarding ports from a device to the host.
+
+Allows an Android device to connect to services running on the host machine,
+i.e., "adb forward" in reverse. Requires |host_forwarder| and |device_forwarder|
+to be built.
+"""
+
+import optparse
+import sys
+import time
+
+import devil_chromium
+
+from devil.android import device_blacklist
+from devil.android import device_utils
+from devil.android import forwarder
+from devil.utils import run_tests_helper
+
+from pylib import constants
+
+
+def main(argv):
+ parser = optparse.OptionParser(usage='Usage: %prog [options] device_port '
+ 'host_port [device_port_2 host_port_2] ...',
+ description=__doc__)
+ parser.add_option('-v',
+ '--verbose',
+ dest='verbose_count',
+ default=0,
+ action='count',
+ help='Verbose level (multiple times for more)')
+ parser.add_option('--device',
+ help='Serial number of device we should use.')
+ parser.add_option('--blacklist-file', help='Device blacklist JSON file.')
+ parser.add_option('--debug', action='store_const', const='Debug',
+ dest='build_type', default='Release',
+ help='Use Debug build of host tools instead of Release.')
+
+ options, args = parser.parse_args(argv)
+ run_tests_helper.SetLogLevel(options.verbose_count)
+
+ devil_chromium.Initialize()
+
+ if len(args) < 2 or not len(args) % 2:
+ parser.error('Need even number of port pairs')
+ sys.exit(1)
+
+ try:
+ port_pairs = [int(a) for a in args[1:]]
+ port_pairs = zip(port_pairs[::2], port_pairs[1::2])
+ except ValueError:
+ parser.error('Bad port number')
+ sys.exit(1)
+
+ blacklist = (device_blacklist.Blacklist(options.blacklist_file)
+ if options.blacklist_file
+ else None)
+ device = device_utils.DeviceUtils.HealthyDevices(
+ blacklist=blacklist, device_arg=options.device)[0]
+ constants.SetBuildType(options.build_type)
+ try:
+ forwarder.Forwarder.Map(port_pairs, device)
+ while True:
+ time.sleep(60)
+ except KeyboardInterrupt:
+ sys.exit(0)
+ finally:
+ forwarder.Forwarder.UnmapAllDevicePorts(device)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/build/android/adb_run_android_webview_shell b/build/android/adb_run_android_webview_shell
new file mode 100644
index 00000000000..1014a731f47
--- /dev/null
+++ b/build/android/adb_run_android_webview_shell
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# 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.
+
+optional_url=$1
+
+adb shell am start \
+ -a android.intent.action.VIEW \
+ -n org.chromium.android_webview.shell/.AwShellActivity \
+ ${optional_url:+-d "$optional_url"}
diff --git a/build/android/adb_run_blimp_client b/build/android/adb_run_blimp_client
new file mode 100644
index 00000000000..4b3b4a888b0
--- /dev/null
+++ b/build/android/adb_run_blimp_client
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+optional_url=$1
+
+adb shell am start \
+ -a android.intent.action.VIEW \
+ -n org.chromium.blimp/org.chromium.blimp.BlimpRendererActivity \
+ ${optional_url:+-d "$optional_url"}
diff --git a/build/android/adb_run_chrome_public b/build/android/adb_run_chrome_public
new file mode 100644
index 00000000000..bf150711442
--- /dev/null
+++ b/build/android/adb_run_chrome_public
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+optional_url=$1
+
+adb shell am start \
+ -a android.intent.action.VIEW \
+ -n org.chromium.chrome/com.google.android.apps.chrome.Main \
+ ${optional_url:+-d "$optional_url"}
diff --git a/build/android/adb_run_content_shell b/build/android/adb_run_content_shell
new file mode 100644
index 00000000000..3f01f3bf02f
--- /dev/null
+++ b/build/android/adb_run_content_shell
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# 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.
+
+optional_url=$1
+
+adb shell am start \
+ -a android.intent.action.VIEW \
+ -n org.chromium.content_shell_apk/.ContentShellActivity \
+ ${optional_url:+-d "$optional_url"}
diff --git a/build/android/adb_run_mojo_shell b/build/android/adb_run_mojo_shell
new file mode 100644
index 00000000000..b585e4a71f6
--- /dev/null
+++ b/build/android/adb_run_mojo_shell
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# 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.
+
+optional_url=$1
+parameters=$2
+
+adb logcat -c
+adb shell am start -S \
+ -a android.intent.action.VIEW \
+ -n org.chromium.mojo_shell_apk/.MojoShellActivity \
+ ${parameters:+--esa parameters "$parameters"} \
+ ${optional_url:+-d "$optional_url"}
+adb logcat -s MojoShellApplication MojoShellActivity chromium
diff --git a/build/android/adb_run_system_webview_shell b/build/android/adb_run_system_webview_shell
new file mode 100644
index 00000000000..5d0c0e48e8f
--- /dev/null
+++ b/build/android/adb_run_system_webview_shell
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# Runs a 'mini-browser' using System WebView with an optional url as parameter.
+# SystemWebViewShell.apk should be installed for this to work.
+
+optional_url=$1
+
+adb shell am start \
+ -a android.intent.action.VIEW \
+ -n org.chromium.webview_shell/.WebViewBrowserActivity \
+ ${optional_url:+-d "$optional_url"}
diff --git a/build/android/adb_system_webview_command_line b/build/android/adb_system_webview_command_line
new file mode 100644
index 00000000000..376b0b3d233
--- /dev/null
+++ b/build/android/adb_system_webview_command_line
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+# If no flags are given, prints the current content shell flags.
+#
+# Otherwise, the given flags are used to REPLACE (not modify) the content shell
+# flags. For example:
+# adb_system_webview_command_line --enable-webgl
+#
+# To remove all content shell flags, pass an empty string for the flags:
+# adb_android_webview_command_line ""
+
+exec $(dirname $0)/adb_command_line.py --device-path \
+ /data/local/tmp/webview-command-line "$@"
diff --git a/build/android/android.isolate b/build/android/android.isolate
new file mode 100644
index 00000000000..dfedc6f9383
--- /dev/null
+++ b/build/android/android.isolate
@@ -0,0 +1,29 @@
+# Copyright 2015 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.
+{
+ 'variables': {
+ 'files': [
+ '../../build/util/lib/common/',
+ '../../third_party/android_tools/sdk/build-tools/',
+ '../../third_party/android_tools/sdk/platform-tools/',
+ '../../third_party/appurify-python/',
+ '../../third_party/catapult/',
+ '../../third_party/requests/',
+ '../../tools/swarming_client/',
+ '<(PRODUCT_DIR)/icudtl.dat',
+ '<(PRODUCT_DIR)/lib.java/chromium_commands.dex.jar',
+ '<(PRODUCT_DIR)/host_forwarder',
+ '<(PRODUCT_DIR)/forwarder_dist/',
+ '<(PRODUCT_DIR)/md5sum_bin_host',
+ '<(PRODUCT_DIR)/md5sum_dist/',
+ 'devil_chromium.json',
+ 'devil_chromium.py',
+ 'gyp/util/',
+ 'incremental_install/',
+ 'lighttpd_server.py',
+ 'pylib/',
+ 'test_runner.py',
+ ]
+ }
+}
diff --git a/build/android/android_lint_cache.gyp b/build/android/android_lint_cache.gyp
new file mode 100644
index 00000000000..72b9e9e0099
--- /dev/null
+++ b/build/android/android_lint_cache.gyp
@@ -0,0 +1,51 @@
+# Copyright 2016 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': [
+ {
+ # This target runs a functionally empty lint to create or update the
+ # API versions cache if necessary. This prevents racy creation of the
+ # cache while linting java targets in lint_action.gypi.
+ 'target_name': 'android_lint_cache',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'prepare_android_lint_cache',
+ 'message': 'Preparing Android lint cache',
+ 'variables': {
+ 'android_lint_cache_stamp': '<(PRODUCT_DIR)/android_lint_cache/android_lint_cache.stamp',
+ 'android_manifest_path': '<(DEPTH)/build/android/AndroidManifest.xml',
+ 'result_path': '<(PRODUCT_DIR)/android_lint_cache/result.xml',
+ 'platform_xml_path': '<(android_sdk_root)/platform-tools/api/api-versions.xml',
+ },
+ 'inputs': [
+ '<(DEPTH)/build/android/gyp/util/build_utils.py',
+ '<(DEPTH)/build/android/gyp/lint.py',
+ '<(android_manifest_path)',
+ '<(platform_xml_path)',
+ ],
+ 'outputs': [
+ '<(android_lint_cache_stamp)',
+ '<(result_path)',
+ ],
+ 'action': [
+ 'python', '<(DEPTH)/build/android/gyp/lint.py',
+ '--lint-path', '<(android_sdk_root)/tools/lint',
+ '--cache-dir', '<(PRODUCT_DIR)/android_lint_cache',
+ '--android-sdk-version=<(android_sdk_version)',
+ '--platform-xml-path', '<(platform_xml_path)',
+ '--manifest-path', '<(android_manifest_path)',
+ '--product-dir', '<(PRODUCT_DIR)',
+ '--result-path', '<(result_path)',
+ '--stamp', '<(android_lint_cache_stamp)',
+ '--create-cache',
+ '--silent',
+ '--enable'
+ ],
+ },
+ ],
+ },
+ ],
+}
diff --git a/build/android/android_no_jni_exports.lst b/build/android/android_no_jni_exports.lst
new file mode 100644
index 00000000000..ffc6cf7028c
--- /dev/null
+++ b/build/android/android_no_jni_exports.lst
@@ -0,0 +1,17 @@
+# 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 script makes all JNI exported symbols local, to prevent the JVM from
+# being able to find them, enforcing use of manual JNI function registration.
+# This is used for all Android binaries by default, unless they explicitly state
+# that they want JNI exported symbols to remain visible, as we need to ensure
+# the manual registration path is correct to maintain compatibility with the
+# crazy linker.
+# Check ld version script manual:
+# https://sourceware.org/binutils/docs-2.24/ld/VERSION.html#VERSION
+
+{
+ local:
+ Java_*;
+};
diff --git a/build/android/ant/apk-package.xml b/build/android/ant/apk-package.xml
new file mode 100644
index 00000000000..cb795609181
--- /dev/null
+++ b/build/android/ant/apk-package.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/android/ant/chromium-debug.keystore b/build/android/ant/chromium-debug.keystore
new file mode 100644
index 00000000000..67eb0aa34c5
Binary files /dev/null and b/build/android/ant/chromium-debug.keystore differ
diff --git a/build/android/ant/empty/res/.keep b/build/android/ant/empty/res/.keep
new file mode 100644
index 00000000000..1fd038b8cf3
--- /dev/null
+++ b/build/android/ant/empty/res/.keep
@@ -0,0 +1,2 @@
+# This empty res folder can be passed to aapt while building Java libraries or
+# APKs that don't have any resources.
diff --git a/build/android/apkbuilder_action.gypi b/build/android/apkbuilder_action.gypi
new file mode 100644
index 00000000000..e073e9bdbec
--- /dev/null
+++ b/build/android/apkbuilder_action.gypi
@@ -0,0 +1,84 @@
+# Copyright 2015 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 file is a helper to java_apk.gypi. It should be used to create an
+# action that runs ApkBuilder via ANT.
+#
+# Required variables:
+# apk_name - File name (minus path & extension) of the output apk.
+# apk_path - Path to output apk.
+# package_input_paths - Late-evaluated list of resource zips.
+# native_libs_dir - Path to lib/ directory to use. Set to an empty directory
+# if no native libs are needed.
+# Optional variables:
+# has_code - Whether to include classes.dex in the apk.
+# dex_path - Path to classes.dex. Used only when has_code=1.
+# extra_inputs - List of extra action inputs.
+{
+ 'variables': {
+ 'variables': {
+ 'has_code%': 1,
+ },
+ 'conditions': [
+ ['has_code == 0', {
+ 'has_code_str': 'false',
+ }, {
+ 'has_code_str': 'true',
+ }],
+ ],
+ 'has_code%': '<(has_code)',
+ 'extra_inputs%': [],
+ # Write the inputs list to a file, so that its mtime is updated when
+ # the list of inputs changes.
+ 'inputs_list_file': '>|(apk_package.<(_target_name).<(apk_name).gypcmd >@(package_input_paths))',
+ 'resource_packaged_apk_name': '<(apk_name)-resources.ap_',
+ 'resource_packaged_apk_path': '<(intermediate_dir)/<(resource_packaged_apk_name)',
+ },
+ 'action_name': 'apkbuilder_<(apk_name)',
+ 'message': 'Packaging <(apk_name)',
+ 'inputs': [
+ '<(DEPTH)/build/android/ant/apk-package.xml',
+ '<(DEPTH)/build/android/gyp/util/build_utils.py',
+ '<(DEPTH)/build/android/gyp/ant.py',
+ '<(resource_packaged_apk_path)',
+ '<@(extra_inputs)',
+ '>@(package_input_paths)',
+ '>(inputs_list_file)',
+ ],
+ 'outputs': [
+ '<(apk_path)',
+ ],
+ 'conditions': [
+ ['has_code == 1', {
+ 'inputs': ['<(dex_path)'],
+ 'action': [
+ '-DDEX_FILE_PATH=<(dex_path)',
+ ]
+ }],
+ ['enable_multidex == 1', {
+ 'action': [
+ '-DMULTIDEX_ENABLED=1',
+ ]
+ }]
+ ],
+ 'action': [
+ 'python', '<(DEPTH)/build/android/gyp/ant.py',
+ '--',
+ '-quiet',
+ '-DHAS_CODE=<(has_code_str)',
+ '-DANDROID_SDK_ROOT=<(android_sdk_root)',
+ '-DANDROID_SDK_TOOLS=<(android_sdk_tools)',
+ '-DRESOURCE_PACKAGED_APK_NAME=<(resource_packaged_apk_name)',
+ '-DNATIVE_LIBS_DIR=<(native_libs_dir)',
+ '-DAPK_NAME=<(apk_name)',
+ '-DCONFIGURATION_NAME=<(CONFIGURATION_NAME)',
+ '-DOUT_DIR=<(intermediate_dir)',
+ '-DUNSIGNED_APK_PATH=<(apk_path)',
+ '-DEMMA_INSTRUMENT=<(emma_instrument)',
+ '-DEMMA_DEVICE_JAR=<(emma_device_jar)',
+ '-Dbasedir=.',
+ '-buildfile',
+ '<(DEPTH)/build/android/ant/apk-package.xml',
+ ]
+}
diff --git a/build/android/asan_symbolize.py b/build/android/asan_symbolize.py
new file mode 100644
index 00000000000..d709f7e2217
--- /dev/null
+++ b/build/android/asan_symbolize.py
@@ -0,0 +1,114 @@
+#!/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 optparse
+import os
+import re
+import sys
+
+from pylib import constants
+from pylib.constants import host_paths
+
+# Uses symbol.py from third_party/android_platform, not python's.
+with host_paths.SysPath(
+ host_paths.ANDROID_PLATFORM_DEVELOPMENT_SCRIPTS_PATH,
+ position=0):
+ import symbol
+
+
+_RE_ASAN = re.compile(r'(.*?)(#\S*?)\s+(\S*?)\s+\((.*?)\+(.*?)\)')
+
+def _ParseAsanLogLine(line):
+ m = re.match(_RE_ASAN, line)
+ if not m:
+ return None
+ return {
+ 'prefix': m.group(1),
+ 'library': m.group(4),
+ 'pos': m.group(2),
+ 'rel_address': '%08x' % int(m.group(5), 16),
+ }
+
+
+def _FindASanLibraries():
+ asan_lib_dir = os.path.join(host_paths.DIR_SOURCE_ROOT,
+ 'third_party', 'llvm-build',
+ 'Release+Asserts', 'lib')
+ asan_libs = []
+ for src_dir, _, files in os.walk(asan_lib_dir):
+ asan_libs += [os.path.relpath(os.path.join(src_dir, f))
+ for f in files
+ if f.endswith('.so')]
+ return asan_libs
+
+
+def _TranslateLibPath(library, asan_libs):
+ for asan_lib in asan_libs:
+ if os.path.basename(library) == os.path.basename(asan_lib):
+ return '/' + asan_lib
+ # pylint: disable=no-member
+ return symbol.TranslateLibPath(library)
+
+
+def _Symbolize(asan_input):
+ asan_libs = _FindASanLibraries()
+ libraries = collections.defaultdict(list)
+ asan_lines = []
+ for asan_log_line in [a.rstrip() for a in asan_input]:
+ m = _ParseAsanLogLine(asan_log_line)
+ if m:
+ libraries[m['library']].append(m)
+ asan_lines.append({'raw_log': asan_log_line, 'parsed': m})
+
+ all_symbols = collections.defaultdict(dict)
+ for library, items in libraries.iteritems():
+ libname = _TranslateLibPath(library, asan_libs)
+ lib_relative_addrs = set([i['rel_address'] for i in items])
+ # pylint: disable=no-member
+ info_dict = symbol.SymbolInformationForSet(libname,
+ lib_relative_addrs,
+ True)
+ if info_dict:
+ all_symbols[library]['symbols'] = info_dict
+
+ for asan_log_line in asan_lines:
+ m = asan_log_line['parsed']
+ if not m:
+ print asan_log_line['raw_log']
+ continue
+ if (m['library'] in all_symbols and
+ m['rel_address'] in all_symbols[m['library']]['symbols']):
+ s = all_symbols[m['library']]['symbols'][m['rel_address']][0]
+ print '%s%s %s %s' % (m['prefix'], m['pos'], s[0], s[1])
+ else:
+ print asan_log_line['raw_log']
+
+
+def main():
+ parser = optparse.OptionParser()
+ parser.add_option('-l', '--logcat',
+ help='File containing adb logcat output with ASan stacks. '
+ 'Use stdin if not specified.')
+ parser.add_option('--output-directory',
+ help='Path to the root build directory.')
+ options, _ = parser.parse_args()
+
+ if options.output_directory:
+ constants.SetOutputDirectory(options.output_directory)
+ # Do an up-front test that the output directory is known.
+ constants.CheckOutputDirectory()
+
+ if options.logcat:
+ asan_input = file(options.logcat, 'r')
+ else:
+ asan_input = sys.stdin
+ _Symbolize(asan_input.readlines())
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/build/android/avd.py b/build/android/avd.py
new file mode 100644
index 00000000000..788ceaf053a
--- /dev/null
+++ b/build/android/avd.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env 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.
+
+"""Launches Android Virtual Devices with a set configuration for testing Chrome.
+
+The script will launch a specified number of Android Virtual Devices (AVD's).
+"""
+
+import argparse
+import logging
+import os
+import re
+import sys
+
+import devil_chromium
+import install_emulator_deps
+
+from devil.utils import cmd_helper
+from pylib import constants
+from pylib.utils import emulator
+
+def main(argv):
+ # ANDROID_SDK_ROOT needs to be set to the location of the SDK used to launch
+ # the emulator to find the system images upon launch.
+ emulator_sdk = constants.ANDROID_SDK_ROOT
+ os.environ['ANDROID_SDK_ROOT'] = emulator_sdk
+
+ arg_parser = argparse.ArgumentParser(description='AVD script.')
+ sub_parsers = arg_parser.add_subparsers(title='subparser', dest='command')
+ sub_parsers.add_parser(
+ 'kill', help='Shutdown all existing emulators')
+ sub_parsers.add_parser(
+ 'delete', help='Deleting all the avd files')
+ wait_parser = sub_parsers.add_parser(
+ 'wait', help='Wait for emulators to finish booting')
+ wait_parser.add_argument('-n', '--num', dest='wait_num',
+ help='Number of emulators to wait for', type=int,
+ default=1)
+ run_parser = sub_parsers.add_parser('run', help='Run emulators')
+ run_parser.add_argument('--name', help='Optinaly, name of existing AVD to '
+ 'launch. If not specified, AVD\'s will be created')
+ run_parser.add_argument('-n', '--num', dest='emulator_count',
+ help='Number of emulators to launch (default is 1).',
+ type=int, default='1')
+ run_parser.add_argument('--abi', default='x86',
+ help='Platform of emulators to launch (x86 default)')
+ run_parser.add_argument('--api-level', dest='api_level',
+ help='API level for the image',
+ type=int, default=constants.ANDROID_SDK_VERSION)
+ run_parser.add_argument('--sdcard-size', dest='sdcard_size',
+ default=emulator.DEFAULT_SDCARD_SIZE,
+ help='Set sdcard size of the emulators'
+ ' e.g. --sdcard-size=512M')
+ run_parser.add_argument('--partition-size', dest='partition_size',
+ default=emulator.DEFAULT_STORAGE_SIZE,
+ help='Default internal storage size'
+ ' e.g. --partition-size=1024M')
+ run_parser.add_argument('--launch-without-kill', action='store_false',
+ dest='kill_and_launch', default=True,
+ help='Kill all emulators at launch')
+ run_parser.add_argument('--enable-kvm', action='store_true',
+ dest='enable_kvm', default=False,
+ help='Enable kvm for faster x86 emulator run')
+ run_parser.add_argument('--headless', action='store_true',
+ dest='headless', default=False,
+ help='Launch an emulator with no UI.')
+
+ arguments = arg_parser.parse_args(argv[1:])
+
+ logging.root.setLevel(logging.INFO)
+
+ devil_chromium.Initialize()
+
+ if arguments.command == 'kill':
+ logging.info('Killing all existing emulator and existing the program')
+ emulator.KillAllEmulators()
+ elif arguments.command == 'delete':
+ emulator.DeleteAllTempAVDs()
+ elif arguments.command == 'wait':
+ emulator.WaitForEmulatorLaunch(arguments.wait_num)
+ else:
+ # Check if SDK exist in ANDROID_SDK_ROOT
+ if not install_emulator_deps.CheckSDK():
+ raise Exception('Emulator SDK not installed in %s'
+ % constants.ANDROID_SDK_ROOT)
+
+ # Check if KVM is enabled for x86 AVD
+ if arguments.abi == 'x86':
+ if not install_emulator_deps.CheckKVM():
+ logging.warning('KVM is not installed or enabled')
+ arguments.enable_kvm = False
+
+ # Check if targeted system image exist
+ if not install_emulator_deps.CheckSystemImage(arguments.abi,
+ arguments.api_level):
+ logging.critical('ERROR: System image for %s AVD not installed. Run '
+ 'install_emulator_deps.py', arguments.abi)
+ return 1
+
+ # If AVD is specified, check that the SDK has the required target. If not,
+ # check that the SDK has the desired target for the temporary AVD's.
+ api_level = arguments.api_level
+ if arguments.name:
+ android = os.path.join(constants.ANDROID_SDK_ROOT, 'tools',
+ 'android')
+ avds_output = cmd_helper.GetCmdOutput([android, 'list', 'avd'])
+ names = re.findall(r'Name: (\w+)', avds_output)
+ api_levels = re.findall(r'API level (\d+)', avds_output)
+ try:
+ avd_index = names.index(arguments.name)
+ except ValueError:
+ logging.critical('ERROR: Specified AVD %s does not exist.',
+ arguments.name)
+ return 1
+ api_level = int(api_levels[avd_index])
+
+ if not install_emulator_deps.CheckSDKPlatform(api_level):
+ logging.critical('ERROR: Emulator SDK missing required target for API %d.'
+ ' Run install_emulator_deps.py.')
+ return 1
+
+ if arguments.name:
+ emulator.LaunchEmulator(
+ arguments.name,
+ arguments.abi,
+ enable_kvm=arguments.enable_kvm,
+ kill_and_launch=arguments.reset_and_launch,
+ sdcard_size=arguments.sdcard_size,
+ storage_size=arguments.partition_size,
+ headless=arguments.headless
+ )
+ else:
+ emulator.LaunchTempEmulators(
+ arguments.emulator_count,
+ arguments.abi,
+ arguments.api_level,
+ enable_kvm=arguments.enable_kvm,
+ kill_and_launch=arguments.kill_and_launch,
+ sdcard_size=arguments.sdcard_size,
+ storage_size=arguments.partition_size,
+ wait_for_boot=True,
+ headless=arguments.headless
+ )
+ logging.info('Emulator launch completed')
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/build/android/copy_ex.gypi b/build/android/copy_ex.gypi
new file mode 100644
index 00000000000..8c49d247c34
--- /dev/null
+++ b/build/android/copy_ex.gypi
@@ -0,0 +1,79 @@
+# Copyright 2015 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.
+
+# Copy files to a directory with the option to clear directory first.
+#
+# Variables:
+# dest_path - directory to copy files to.
+# src_files - optional, a list of files to copy without changing name.
+# clear - optional, if set, clear directory before copying files.
+# renaming_sources - optional, a list of files to copy and rename.
+# renaming_destinations - optional, a list of new file names corresponding to
+# renaming_sources.
+#
+# Exmaple
+# {
+# 'target_name': 'copy_assets',
+# 'type': 'none',
+# 'variables': {
+# 'dest_path': 'apk/assets/path',
+# 'src_files': ['path1/fr.pak'],
+# 'clear': 1,
+# # path2/old1 and path3/old2 will be copied to apk/assets/path and
+# # renamed to new1, new2 respectly.
+# 'renaming_sources': ['path2/old1', 'path3/old2'],
+# 'renaming_destinations': ['new1', 'new2'],
+# },
+# 'includes': [ '../build/android/copy_ex.gypi' ],
+# },
+#
+{
+ 'variables': {
+ 'clear%': 0,
+ 'src_files%': [],
+ 'renaming_sources%': [],
+ 'renaming_destinations%': [],
+ },
+ 'actions': [{
+ 'action_name': '<(_target_name)_copy_ex',
+ 'variables': {
+ 'additional_args':[],
+ 'local_inputs': [],
+ 'dest_files': [],
+ 'conditions': [
+ ['clear == 1', {
+ 'additional_args': ['--clear'],
+ }],
+ ['src_files != []', {
+ 'additional_args': ['--files', '<(src_files)'],
+ 'local_inputs': ['<@(src_files)'],
+ # src_files will be used to generate destination files path for
+ # outputs.
+ 'dest_files': ['<@(src_files)'],
+ }],
+ ['renaming_sources != []', {
+ 'additional_args': [
+ '--renaming-sources', '<(renaming_sources)',
+ '--renaming-destinations', '<(renaming_destinations)'
+ ],
+ 'local_inputs': ['<@(renaming_sources)'],
+ 'dest_files': ['<@(renaming_destinations)'],
+ }],
+ ],
+ },
+ 'inputs': [
+ '<(DEPTH)/build/android/gyp/copy_ex.py',
+ '<(DEPTH)/build/android/gyp/generate_copy_ex_outputs.py',
+ '<@(local_inputs)',
+ ],
+ 'outputs': [
+ '@(inputs)',
+ ],
+ 'outputs': [
+ '<(output_apk_path)',
+ ],
+ 'action': [
+ 'python', '<(DEPTH)/build/android/gyp/create_standalone_apk.py',
+ '--libraries-top-dir=<(libraries_top_dir)',
+ '--input-apk-path=<(input_apk_path)',
+ '--output-apk-path=<(output_apk_path)',
+ ],
+}
diff --git a/build/android/developer_recommended_flags.gypi b/build/android/developer_recommended_flags.gypi
new file mode 100644
index 00000000000..79c201deccb
--- /dev/null
+++ b/build/android/developer_recommended_flags.gypi
@@ -0,0 +1,61 @@
+# 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.
+
+# This is the set of recommended gyp variable settings for Chrome for Android development.
+#
+# These can be used by copying this file to $CHROME_SRC/chrome/supplement.gypi.
+#
+# Even better, create chrome/supplement.gypi containing the following:
+# {
+# 'includes': [ '../build/android/developer_recommended_flags.gypi' ]
+# }
+# and you'll get new settings automatically.
+# When using this method, you can override individual settings by setting them unconditionally (with
+# no %) in chrome/supplement.gypi.
+# I.e. to disable gyp_managed_install but use everything else:
+# {
+# 'variables': {
+# 'gyp_managed_install': 0,
+# },
+# 'includes': [ '../build/android/developer_recommended_flags.gypi' ]
+# }
+
+{
+ 'variables': {
+ 'variables': {
+ # Set component to 'shared_library' to enable the component build. This builds native code as
+ # many small shared libraries instead of one monolithic library. This slightly reduces the time
+ # required for incremental builds.
+ 'component%': 'shared_library',
+ },
+ 'component%': '<(component)',
+
+ # When gyp_managed_install is set to 1, building an APK will install that APK on the connected
+ # device(/emulator). To install on multiple devices (or onto a new device), build the APK once
+ # with each device attached. This greatly reduces the time required for incremental builds.
+ #
+ # This comes with some caveats:
+ # Only works with a single device connected (it will print a warning if
+ # zero or multiple devices are attached).
+ # Device must be flashed with a user-debug unsigned Android build.
+ # Some actions are always run (i.e. ninja will never say "no work to do").
+ 'gyp_managed_install%': 1,
+
+ # With gyp_managed_install, we do not necessarily need a standalone APK.
+ # When create_standalone_apk is set to 1, we will build a standalone APK
+ # anyway. For even faster builds, you can set create_standalone_apk to 0.
+ 'create_standalone_apk%': 1,
+
+ # Set clang to 1 to use the clang compiler. Clang has much (much, much) better warning/error
+ # messages than gcc.
+ # TODO(cjhopman): Enable this when http://crbug.com/156420 is addressed. Until then, users can
+ # set clang to 1, but Android stack traces will sometimes be incomplete.
+ #'clang%': 1,
+
+ # Set fastbuild to 1 to build with less debugging information. This can greatly decrease linking
+ # time. The downside is that stack traces will be missing useful information (like line
+ # numbers).
+ #'fastbuild%': 1,
+ },
+}
diff --git a/build/android/devil_chromium.json b/build/android/devil_chromium.json
new file mode 100644
index 00000000000..c1157fae9ef
--- /dev/null
+++ b/build/android/devil_chromium.json
@@ -0,0 +1,69 @@
+{
+ "config_type": "BaseConfig",
+ "dependencies": {
+ "aapt": {
+ "file_info": {
+ "linux2_x86_64": {
+ "local_paths": [
+ "../../third_party/android_tools/sdk/build-tools/23.0.1/aapt"
+ ]
+ }
+ }
+ },
+ "adb": {
+ "file_info": {
+ "linux2_x86_64": {
+ "local_paths": [
+ "../../third_party/android_tools/sdk/platform-tools/adb"
+ ]
+ }
+ }
+ },
+ "android_sdk": {
+ "file_info": {
+ "linux2_x86_64": {
+ "local_paths": [
+ "../../third_party/android_tools/sdk"
+ ]
+ }
+ }
+ },
+ "dexdump": {
+ "file_info": {
+ "linux2_x86_64": {
+ "local_paths": [
+ "../../third_party/android_tools/sdk/build-tools/23.0.1/dexdump"
+ ]
+ }
+ }
+ },
+ "split-select": {
+ "file_info": {
+ "linux2_x86_64": {
+ "local_paths": [
+ "../../third_party/android_tools/sdk/build-tools/23.0.1/split-select"
+ ]
+ }
+ }
+ },
+ "pymock": {
+ "file_info": {
+ "darwin_x86_64": {
+ "local_paths": [
+ "../../third_party/pymock"
+ ]
+ },
+ "linux2_x86_64": {
+ "local_paths": [
+ "../../third_party/pymock"
+ ]
+ },
+ "win32_AMD64": {
+ "local_paths": [
+ "../../third_party/pymock"
+ ]
+ }
+ }
+ }
+ }
+}
diff --git a/build/android/devil_chromium.py b/build/android/devil_chromium.py
new file mode 100644
index 00000000000..0e085d8abdb
--- /dev/null
+++ b/build/android/devil_chromium.py
@@ -0,0 +1,163 @@
+# Copyright 2015 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.
+
+"""Configures devil for use in chromium."""
+
+import os
+import sys
+
+from pylib.constants import host_paths
+
+if host_paths.DEVIL_PATH not in sys.path:
+ sys.path.append(host_paths.DEVIL_PATH)
+
+from devil import devil_env
+
+_DEVIL_CONFIG = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), 'devil_chromium.json'))
+
+_DEVIL_BUILD_PRODUCT_DEPS = {
+ 'forwarder_device': [
+ {
+ 'platform': 'android',
+ 'arch': 'armeabi-v7a',
+ 'name': 'forwarder_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'arm64-v8a',
+ 'name': 'forwarder_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'mips',
+ 'name': 'forwarder_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'mips64',
+ 'name': 'forwarder_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'x86',
+ 'name': 'forwarder_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'x86_64',
+ 'name': 'forwarder_dist',
+ },
+ ],
+ 'forwarder_host': [
+ {
+ 'platform': 'linux2',
+ 'arch': 'x86_64',
+ 'name': 'host_forwarder',
+ },
+ ],
+ 'md5sum_device': [
+ {
+ 'platform': 'android',
+ 'arch': 'armeabi-v7a',
+ 'name': 'md5sum_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'arm64-v8a',
+ 'name': 'md5sum_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'mips',
+ 'name': 'md5sum_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'mips64',
+ 'name': 'md5sum_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'x86',
+ 'name': 'md5sum_dist',
+ },
+ {
+ 'platform': 'android',
+ 'arch': 'x86_64',
+ 'name': 'md5sum_dist',
+ },
+ ],
+ 'md5sum_host': [
+ {
+ 'platform': 'linux2',
+ 'arch': 'x86_64',
+ 'name': 'md5sum_bin_host',
+ },
+ ],
+}
+
+
+def Initialize(output_directory=None, custom_deps=None, adb_path=None):
+ """Initializes devil with chromium's binaries and third-party libraries.
+
+ This includes:
+ - Libraries:
+ - the android SDK ("android_sdk")
+ - pymock ("pymock")
+ - Build products:
+ - host & device forwarder binaries
+ ("forwarder_device" and "forwarder_host")
+ - host & device md5sum binaries ("md5sum_device" and "md5sum_host")
+
+ Args:
+ output_directory: An optional path to the output directory. If not set,
+ no built dependencies are configured.
+ custom_deps: An optional dictionary specifying custom dependencies.
+ This should be of the form:
+
+ {
+ 'dependency_name': {
+ 'platform': 'path',
+ ...
+ },
+ ...
+ }
+ """
+
+ devil_dynamic_config = {
+ 'config_type': 'BaseConfig',
+ 'dependencies': {},
+ }
+ if output_directory:
+ output_directory = os.path.abspath(output_directory)
+ devil_dynamic_config['dependencies'] = {
+ dep_name: {
+ 'file_info': {
+ '%s_%s' % (dep_config['platform'], dep_config['arch']): {
+ 'local_paths': [
+ os.path.join(output_directory, dep_config['name']),
+ ],
+ }
+ for dep_config in dep_configs
+ }
+ }
+ for dep_name, dep_configs in _DEVIL_BUILD_PRODUCT_DEPS.iteritems()
+ }
+ if custom_deps:
+ devil_dynamic_config['dependencies'].update(custom_deps)
+ if adb_path:
+ devil_dynamic_config['dependencies'].update({
+ 'adb': {
+ 'file_info': {
+ devil_env.GetPlatform(): {
+ 'local_paths': [adb_path]
+ }
+ }
+ }
+ })
+
+ devil_env.config.Initialize(
+ configs=[devil_dynamic_config], config_files=[_DEVIL_CONFIG])
+
diff --git a/build/android/dex_action.gypi b/build/android/dex_action.gypi
new file mode 100644
index 00000000000..7d9638e035c
--- /dev/null
+++ b/build/android/dex_action.gypi
@@ -0,0 +1,63 @@
+# 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.
+
+# This file is meant to be included into an action to provide a rule that dexes
+# compiled java files. If proguard_enabled == "true" and CONFIGURATION_NAME ==
+# "Release", then it will dex the proguard_enabled_input_path instead of the
+# normal dex_input_paths/dex_generated_input_paths.
+#
+# To use this, create a gyp target with the following form:
+# {
+# 'action_name': 'some name for the action'
+# 'actions': [
+# 'variables': {
+# 'dex_input_paths': [ 'files to dex (when proguard is not used) and add to input paths' ],
+# 'dex_generated_input_dirs': [ 'dirs that contain generated files to dex' ],
+#
+# # For targets that use proguard:
+# 'proguard_enabled': 'true',
+# 'proguard_enabled_input_path': 'path to dex when using proguard',
+# },
+# 'includes': [ 'relative/path/to/dex_action.gypi' ],
+# ],
+# },
+#
+
+{
+ 'message': 'Creating dex file: <(output_path)',
+ 'variables': {
+ 'dex_input_paths': [],
+ 'dex_generated_input_dirs': [],
+ 'proguard_enabled%': 'false',
+ # TODO(jbudorick): remove this once multidex is done.
+ 'debug_build_proguard_enabled%': 'false',
+ 'proguard_enabled_input_path%': '',
+ 'dex_no_locals%': 0,
+ 'dex_additional_options': [],
+ },
+ 'inputs': [
+ '<(DEPTH)/build/android/gyp/util/build_utils.py',
+ '<(DEPTH)/build/android/gyp/util/md5_check.py',
+ '<(DEPTH)/build/android/gyp/dex.py',
+ '>@(dex_input_paths)',
+ ],
+ 'outputs': [
+ '<(output_path)',
+ '<(output_path).inputs',
+ ],
+ 'action': [
+ 'python', '<(DEPTH)/build/android/gyp/dex.py',
+ '--dex-path=<(output_path)',
+ '--android-sdk-tools=<(android_sdk_tools)',
+ '--output-directory=<(PRODUCT_DIR)',
+ '--configuration-name=<(CONFIGURATION_NAME)',
+ '--proguard-enabled=>(proguard_enabled)',
+ '--debug-build-proguard-enabled=>(debug_build_proguard_enabled)',
+ '--proguard-enabled-input-path=<(proguard_enabled_input_path)',
+ '--no-locals=>(dex_no_locals)',
+ '>@(dex_additional_options)',
+ '>@(dex_input_paths)',
+ '>@(dex_generated_input_dirs)',
+ ]
+}
diff --git a/build/android/disable_gcc_lto.gypi b/build/android/disable_gcc_lto.gypi
new file mode 100644
index 00000000000..a733c7aa0fc
--- /dev/null
+++ b/build/android/disable_gcc_lto.gypi
@@ -0,0 +1,20 @@
+# Copyright (c) 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 file is meant to be included to disable GCC LTO on a target.
+
+{
+ 'target_conditions': [
+ ['_toolset=="target"', {
+ 'conditions': [
+ ['OS=="android" and clang==0 and (use_lto==1 or use_lto_o2==1)', {
+ 'cflags!': [
+ '-flto',
+ '-ffat-lto-objects',
+ ],
+ }],
+ ],
+ }],
+ ],
+}
diff --git a/build/android/docs/coverage.md b/build/android/docs/coverage.md
new file mode 100644
index 00000000000..0a179876c65
--- /dev/null
+++ b/build/android/docs/coverage.md
@@ -0,0 +1,32 @@
+# Android code coverage instructions
+
+This is instruction for code coverage for android instrumentation and junit tests.
+
+[TOC]
+
+## How EMMA coverage works
+
+In order to use EMMA code coverage, we need to create build time **.em** file and runtime
+**.ec** file. Then we need to process them using the
+build/android/generate_emma_html.py script.
+
+## How to collect EMMA coverage data
+
+1. Build your APK with the GN arg emma_coverage=true.
+ ```
+ gn args out-gn/Debug
+ > target_os = "android"
+ > emma_coverage = true
+ ```
+ By doing so, **.em** files will be created in out-gn/Debug.
+2. Run tests, with option `--coverage-dir `, to specify where to save
+ the .ec file. For example, you can run chrome junit tests:
+ `out-gn/Debug/bin/run_chrome_junit_tests --coverage-dir /tmp/coverage`.
+3. Now we have both .em and .ec files. We can merge them and create a html file,
+ using generate_emma_html.py. For example, generate_emma_html.py can be called
+ this way:
+ `build/android/generate_emma_html.py --coverage-dir /tmp/coverage/
+ --metadata-dir out-gn/Debug/ --output example.html`.
+ Then an example.html containing coverage info will be created:
+ `EMMA: writing [html] report to
+ [/example.html] …`
diff --git a/build/android/docs/lint.md b/build/android/docs/lint.md
new file mode 100644
index 00000000000..37f35502e5f
--- /dev/null
+++ b/build/android/docs/lint.md
@@ -0,0 +1,91 @@
+# Lint
+
+Android's [**lint**](http://developer.android.com/tools/help/lint.html) is a static
+analysis tool that Chromium uses to catch possible issues in Java code.
+
+[TOC]
+
+## How Chromium uses lint
+
+Chromium runs lint on a per-target basis for all targets using any of the
+following templates if they are marked as Chromium code (i.e.,
+`chromium_code = true`):
+
+ - `android_apk`
+ - `android_library`
+ - `instrumentation_test_apk`
+ - `unittest_apk`
+
+Chromium also runs lint on a per-target basis for all targets using any of the
+following templates if they are marked as Chromium code and they support
+Android (i.e., `supports_android = true`):
+
+ - `java_library`
+
+This is implemented in the
+[`android_lint`](https://code.google.com/p/chromium/codesearch#chromium/src/build/config/android/internal_rules.gni&q=android_lint%20file:internal_rules%5C.gni)
+gn template.
+
+## My code has a lint error
+
+If lint reports an issue in your code, there are several possible remedies.
+In descending order of preference:
+
+### Fix it
+
+While this isn't always the right response, fixing the lint error or warning
+should be the default.
+
+### Suppress it in code
+
+Android provides an annotation,
+[`@SuppressLint`](http://developer.android.com/reference/android/annotation/SuppressLint.html),
+that tells lint to ignore the annotated element. It can be used on classes,
+constructors, methods, parameters, fields, or local variables, though usage
+in Chromium is typically limited to the first three.
+
+Like many suppression annotations, `@SuppressLint` takes a value that tells **lint**
+what to ignore. It can be a single `String`:
+
+```java
+@SuppressLint("NewApi")
+public void foo() {
+ a.methodThatRequiresHighSdkLevel();
+}
+```
+
+It can also be a list of `String`s:
+
+```java
+@SuppressLint({
+ "NewApi",
+ "UseSparseArrays"
+ })
+public Map bar() {
+ Map shouldBeASparseArray = new HashMap();
+ another.methodThatRequiresHighSdkLevel(shouldBeASparseArray);
+ return shouldBeASparseArray;
+}
+```
+
+This is the preferred way of suppressing warnings in a limited scope.
+
+### Suppress it in the suppressions XML file
+
+**lint** can be given an XML configuration containing warnings or errors that
+should be ignored. Chromium's lint suppression XML file can be found in
+[`build/android/lint/suppressions.xml`](https://chromium.googlesource.com/chromium/src/+/master/build/android/lint/suppressions.xml).
+It can be updated to suppress current warnings by running:
+
+```bash
+$ python build/android/lint/suppress.py
+```
+
+e.g., to suppress lint errors found in `media_java`:
+
+```bash
+$ python build/android/lint/suppress.py out/Debug/gen/media/base/android/media_java__lint/result.xml
+```
+
+**This mechanism should only be used for disabling warnings across the entire code base; class-specific lint warnings should be disabled inline.**
+
diff --git a/build/android/download_doclava.py b/build/android/download_doclava.py
new file mode 100644
index 00000000000..f9f9ea2f4c4
--- /dev/null
+++ b/build/android/download_doclava.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# Copyright 2016 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.
+
+"""Minimal tool to download doclava from Google storage when building for
+Android."""
+
+import os
+import subprocess
+import sys
+
+
+# Its existence signifies an Android checkout.
+ANDROID_ONLY_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ os.pardir, os.pardir,
+ 'third_party', 'android_tools')
+
+
+def main():
+ # Some Windows bots inadvertently have third_party/android_tools installed,
+ # but are unable to run download_from_google_storage because depot_tools
+ # is not in their path, so avoid failure and bail.
+ if sys.platform == 'win32':
+ return 0
+ if not os.path.exists(ANDROID_ONLY_DIR):
+ return 0
+ subprocess.check_call([
+ 'download_from_google_storage',
+ '--no_resume',
+ '--no_auth',
+ '--bucket', 'chromium-doclava',
+ '--extract',
+ '-s',
+ os.path.join('src', 'buildtools', 'android', 'doclava.tar.gz.sha1')])
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/build/android/emma_coverage_stats.py b/build/android/emma_coverage_stats.py
new file mode 100644
index 00000000000..20ec8eae468
--- /dev/null
+++ b/build/android/emma_coverage_stats.py
@@ -0,0 +1,479 @@
+#!/usr/bin/python
+# Copyright 2015 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.
+
+"""Generates incremental code coverage reports for Java code in Chromium.
+
+Usage:
+
+ build/android/emma_coverage_stats.py -v --out