Merge changes I50e27f46,I0b7cd983,I3639fdee
* changes:
Remove lock_bootloader/unlock_bootloader.
Remove `reboot emergency`.
Clean up fastboot help:
diff --git a/adb/adb.bash b/adb/adb.bash
new file mode 100644
index 0000000..d36bec3
--- /dev/null
+++ b/adb/adb.bash
@@ -0,0 +1,499 @@
+# /* vim: set ai ts=4 ft=sh: */
+#
+# Copyright 2011, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+_adb() {
+ if ! type -t "$1" >/dev/null; then
+ return
+ fi
+
+ if type -t _init_completion >/dev/null; then
+ _init_completion || return
+ fi
+
+ local where i cur serial
+ COMPREPLY=()
+
+ serial="${ANDROID_SERIAL:-none}"
+ where=OPTIONS
+ for ((i=1; i <= COMP_CWORD; i++)); do
+ cur="${COMP_WORDS[i]}"
+ case "${cur}" in
+ -s)
+ where=OPT_SERIAL
+ ;;
+ -p)
+ where=OPT_PATH
+ ;;
+ -*)
+ where=OPTIONS
+ ;;
+ *)
+ if [[ $where == OPT_SERIAL ]]; then
+ where=OPT_SERIAL_ARG
+ serial=${cur}
+ else
+ where=COMMAND
+ break
+ fi
+ ;;
+ esac
+ done
+
+ if [[ $where == COMMAND && $i -ge $COMP_CWORD ]]; then
+ where=OPTIONS
+ fi
+
+ OPTIONS="-d -e -s -p"
+ COMMAND="devices connect disconnect push pull sync shell emu logcat lolcat forward jdwp install uninstall bugreport help version start-server kill-server get-state get-serialno status-window remount reboot reboot-bootloader root usb tcpip disable-verity"
+
+ case $where in
+ OPTIONS|OPT_SERIAL|OPT_PATH)
+ COMPREPLY=( $(compgen -W "$OPTIONS $COMMAND" -- "$cur") )
+ ;;
+ OPT_SERIAL_ARG)
+ local devices=$(command adb devices 2> /dev/null | grep -v "List of devices" | awk '{ print $1 }')
+ COMPREPLY=( $(compgen -W "${devices}" -- ${cur}) )
+ ;;
+ COMMAND)
+ if [[ $i -eq $COMP_CWORD ]]; then
+ COMPREPLY=( $(compgen -W "$COMMAND" -- "$cur") )
+ else
+ i=$((i+1))
+ case "${cur}" in
+ install)
+ _adb_cmd_install "$serial" $i
+ ;;
+ sideload)
+ _adb_cmd_sideload "$serial" $i
+ ;;
+ pull)
+ _adb_cmd_pull "$serial" $i
+ ;;
+ push)
+ _adb_cmd_push "$serial" $i
+ ;;
+ reboot)
+ if [[ $COMP_CWORD == $i ]]; then
+ args="bootloader recovery"
+ COMPREPLY=( $(compgen -W "${args}" -- "${COMP_WORDS[i]}") )
+ fi
+ ;;
+ shell)
+ _adb_cmd_shell "$serial" $i
+ ;;
+ uninstall)
+ _adb_cmd_uninstall "$serial" $i
+ ;;
+ esac
+ fi
+ ;;
+ esac
+
+ return 0
+}
+
+_adb_cmd_install() {
+ local serial i cur where
+
+ serial=$1
+ i=$2
+
+ where=OPTIONS
+ for ((; i <= COMP_CWORD; i++)); do
+ cur="${COMP_WORDS[i]}"
+ case "${cur}" in
+ -*)
+ where=OPTIONS
+ ;;
+ *)
+ where=FILE
+ break
+ ;;
+ esac
+ done
+
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ if [[ $where == OPTIONS ]]; then
+ COMPREPLY=( $(compgen -W "-d -l -r -s" -- "${cur}") )
+ return
+ fi
+
+ _adb_util_complete_local_file "${cur}" '!*.apk'
+}
+
+_adb_cmd_sideload() {
+ local serial i cur
+
+ serial=$1
+ i=$2
+
+ cur="${COMP_WORDS[COMP_CWORD]}"
+
+ _adb_util_complete_local_file "${cur}" '!*.zip'
+}
+
+_adb_cmd_push() {
+ local serial IFS=$'\n' i cur
+
+ serial=$1
+ i=$2
+
+ cur="${COMP_WORDS[COMP_CWORD]}"
+
+ if [[ $COMP_CWORD == $i ]]; then
+ _adb_util_complete_local_file "${cur}"
+ elif [[ $COMP_CWORD == $(($i+1)) ]]; then
+ if [ "${cur}" == "" ]; then
+ cur="/"
+ fi
+ _adb_util_list_files $serial "${cur}"
+ fi
+}
+
+_adb_cmd_pull() {
+ local serial IFS=$'\n' i cur
+
+ serial=$1
+ i=$2
+
+ cur="${COMP_WORDS[COMP_CWORD]}"
+
+ if [[ $COMP_CWORD == $i ]]; then
+ if [ "${cur}" == "" ]; then
+ cur="/"
+ fi
+ _adb_util_list_files $serial "${cur}"
+ elif [[ $COMP_CWORD == $(($i+1)) ]]; then
+ _adb_util_complete_local_file "${cur}"
+ fi
+}
+
+_adb_cmd_shell() {
+ local serial IFS=$'\n' i cur
+ local -a args
+
+ serial=$1
+ i=$2
+
+ cur="${COMP_WORDS[i]}"
+ if [ "$serial" != "none" ]; then
+ args=(-s $serial)
+ fi
+
+ if [[ $i -eq $COMP_CWORD && ${cur:0:1} != "/" ]]; then
+ paths=$(command adb ${args[@]} shell echo '$'PATH 2> /dev/null | tr -d '\r' | tr : '\n')
+ COMMAND=$(command adb ${args[@]} shell ls $paths '2>' /dev/null | tr -d '\r' | {
+ while read -r tmp; do
+ command=${tmp##*/}
+ printf '%s\n' "$command"
+ done
+ })
+ COMPREPLY=( $(compgen -W "$COMMAND" -- "$cur") )
+ return 0
+ fi
+
+ i=$((i+1))
+ case "$cur" in
+ ls)
+ _adb_shell_file_command $serial $i "--color -A -C -F -H -L -R -S -Z -a -c -d -f -h -i -k -l -m -n -p -q -r -s -t -u -x -1"
+ ;;
+ cat)
+ _adb_shell_file_command $serial $i "-h -e -t -u -v"
+ ;;
+ dumpsys)
+ _adb_cmd_shell_dumpsys "$serial" $i
+ ;;
+ am)
+ _adb_cmd_shell_am "$serial" $i
+ ;;
+ pm)
+ _adb_cmd_shell_pm "$serial" $i
+ ;;
+ /*)
+ _adb_util_list_files $serial "$cur"
+ ;;
+ *)
+ COMPREPLY=( )
+ ;;
+ esac
+
+ return 0
+}
+
+_adb_cmd_shell_dumpsys() {
+ local serial i cur
+ local -a args
+ local candidates
+
+ unset IFS
+
+ serial=$1
+ i=$2
+
+ if [ "$serial" != "none" ]; then
+ args=(-s $serial)
+ fi
+
+ if (( $i == $COMP_CWORD )) ; then
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ # First line is a header, so need "1d".
+ candidates=$(command adb ${args[@]} shell dumpsys -l 2> /dev/null | sed -e '1d;s/^ *//' | tr -d '\r')
+ candidates="-l $candidates"
+ COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )
+ return 0
+ fi
+
+ COMPREPLY=( )
+ return 0
+}
+
+_adb_cmd_shell_am() {
+ local serial i cur
+ local candidates
+
+ unset IFS
+
+ serial=$1
+ i=$2
+
+ if (( $i == $COMP_CWORD )) ; then
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ candidates="broadcast clear-debug-app clear-watch-heap dumpheap force-stop get-config get-inactive hang idle-maintenance instrument kill kill-all monitor package-importance profile restart screen-compat send-trim-memory set-debug-app set-inactive set-watch-heap stack start startservice start-user stopservice stop-user suppress-resize-config-changes switch-user task to-app-uri to-intent-uri to-uri"
+ COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )
+ return 0
+ fi
+
+ COMPREPLY=( )
+ return 0
+}
+
+
+_adb_cmd_shell_pm() {
+ local serial i cur
+ local candidates
+
+ unset IFS
+
+ serial=$1
+ i=$2
+
+ if (( $i == $COMP_CWORD )) ; then
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ candidates="-l -lf -p clear create-user default-state disable"
+ candidates+=" disable-until-used disable-user dump enable"
+ candidates+=" get-app-link get-install-location get-max-users"
+ candidates+=" get-max-running-users grant hide install"
+ candidates+=" install-abandon install-commit install-create"
+ candidates+=" install-write list move-package"
+ candidates+=" move-primary-storage path remove-user"
+ candidates+=" reset-permissions revoke set-app-link"
+ candidates+=" set-installer set-install-location"
+ candidates+=" set-permission-enforced trim-caches unhide"
+ candidates+=" uninstall"
+ COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )
+ return 0
+ fi
+
+ if (( $i + 1 == $COMP_CWORD )) && [[ "${COMP_WORDS[COMP_CWORD -1]}" == "list" ]] ; then
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ candidates="packages permission-groups permissions instrumentation features libraries users"
+ COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )
+ return 0
+ fi
+
+ COMPREPLY=( )
+ return 0
+}
+
+_adb_cmd_uninstall() {
+ local serial i where cur packages
+
+ serial=$1
+ i=$2
+ if [ "$serial" != "none" ]; then
+ args=(-s $serial)
+ fi
+
+ where=OPTIONS
+ for ((; i <= COMP_CWORD; i++)); do
+ cur="${COMP_WORDS[i]}"
+ case "${cur}" in
+ -*)
+ where=OPTIONS
+ ;;
+ *)
+ where=FILE
+ break
+ ;;
+ esac
+ done
+
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ if [[ $where == OPTIONS ]]; then
+ COMPREPLY=( $(compgen -W "-k" -- "${cur}") )
+ fi
+
+ packages="$(
+ command adb ${args[@]} shell pm list packages '2>' /dev/null 2> /dev/null | tr -d '\r' | {
+ while read -r tmp; do
+ local package=${tmp#package:}
+ echo -n "${package} "
+ done
+ }
+ )"
+
+ COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "${packages}" -- "${cur}") )
+}
+
+_adb_shell_file_command() {
+ local serial i cur file options
+ local -a args
+
+ serial=$1
+ i=$2
+ if [ "$serial" != "none" ]; then
+ args=(-s $serial)
+ fi
+ options=$3
+
+ where=OPTIONS
+ for ((; i <= COMP_CWORD; i++)); do
+ cur="${COMP_WORDS[i]}"
+ case "${cur}" in
+ -*)
+ where=OPTIONS
+ ;;
+ *)
+ where=FILE
+ break
+ ;;
+ esac
+ done
+
+ file="${COMP_WORDS[COMP_CWORD]}"
+ if [[ ${file} == "" ]]; then
+ file="/"
+ fi
+
+ case $where in
+ OPTIONS)
+ unset IFS
+ COMPREPLY=( $(compgen -W "$options" -- "$cur") )
+ ;;
+ FILE)
+ _adb_util_list_files $serial "$file"
+ ;;
+ esac
+
+ return 0
+}
+
+_adb_util_list_files() {
+ local serial dir IFS=$'\n'
+ local -a toks
+ local -a args
+
+ serial="$1"
+ file="$2"
+
+ if [ "$serial" != "none" ]; then
+ args=(-s $serial)
+ fi
+
+ if [[ $( command adb ${args[@]} shell ls -dF / '2>/dev/null' | tr -d '\r' ) == "d /" ]] ; then
+ toks=( ${toks[@]-} $(
+ command adb ${args[@]} shell ls -dF ${file}"*" '2>' /dev/null 2> /dev/null | tr -d '\r' | {
+ while read -r tmp; do
+ filetype=${tmp%% *}
+ filename=${tmp:${#filetype}+1}
+ if [[ ${filetype:${#filetype}-1:1} == d ]]; then
+ printf '%s/\n' "$filename"
+ else
+ printf '%s\n' "$filename"
+ fi
+ done
+ }
+ ))
+ else
+ toks=( ${toks[@]-} $(
+ command adb ${args[@]} shell ls -dp ${file}"*" '2>/dev/null' 2> /dev/null | tr -d '\r'
+ ))
+ fi
+
+ # Since we're probably doing file completion here, don't add a space after.
+ if [[ $(type -t compopt) = "builtin" ]]; then
+ compopt -o nospace
+ fi
+
+ COMPREPLY=( ${COMPREPLY[@]:-} "${toks[@]}" )
+}
+
+_adb_util_complete_local_file()
+{
+ local file xspec i j IFS=$'\n'
+ local -a dirs files
+
+ file=$1
+ xspec=$2
+
+ # Since we're probably doing file completion here, don't add a space after.
+ if [[ $(type -t compopt) = "builtin" ]]; then
+ compopt -o plusdirs
+ if [[ "${xspec}" == "" ]]; then
+ COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
+ else
+ compopt +o filenames
+ COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -X "${xspec}" -- "${cur}") )
+ fi
+ else
+ # Work-around for shells with no compopt
+
+ dirs=( $(compgen -d -- "${cur}" ) )
+
+ if [[ "${xspec}" == "" ]]; then
+ files=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
+ else
+ files=( ${COMPREPLY[@]:-} $(compgen -f -X "${xspec}" -- "${cur}") )
+ fi
+
+ COMPREPLY=( $(
+ for i in "${files[@]}"; do
+ local skip=
+ for j in "${dirs[@]}"; do
+ if [[ $i == $j ]]; then
+ skip=1
+ break
+ fi
+ done
+ [[ -n $skip ]] || printf "%s\n" "$i"
+ done
+ ))
+
+ COMPREPLY=( ${COMPREPLY[@]:-} $(
+ for i in "${dirs[@]}"; do
+ printf "%s/\n" "$i"
+ done
+ ))
+ fi
+}
+
+
+if [[ $(type -t compopt) = "builtin" ]]; then
+ complete -F _adb adb
+else
+ complete -o nospace -F _adb adb
+fi
diff --git a/adb/test_adb.py b/adb/test_adb.py
index e771106..3bb433d 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -162,15 +162,14 @@
Bug: https://code.google.com/p/android/issues/detail?id=21021
"""
- port = 12345
-
with contextlib.closing(
socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
# Use SO_REUSEADDR so subsequent runs of the test can grab the port
# even if it is in TIME_WAIT.
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- listener.bind(('127.0.0.1', port))
+ listener.bind(('127.0.0.1', 0))
listener.listen(4)
+ port = listener.getsockname()[1]
# Now that listening has started, start adb emu kill, telling it to
# connect to our mock emulator.
@@ -233,6 +232,24 @@
output.strip(), 'connected to localhost:{}'.format(port))
s.close()
+ def test_already_connected(self):
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.bind(('127.0.0.1', 0))
+ s.listen(2)
+
+ port = s.getsockname()[1]
+ output = subprocess.check_output(
+ ['adb', 'connect', 'localhost:{}'.format(port)])
+
+ self.assertEqual(
+ output.strip(), 'connected to localhost:{}'.format(port))
+
+ # b/31250450: this always returns 0 but probably shouldn't.
+ output = subprocess.check_output(
+ ['adb', 'connect', 'localhost:{}'.format(port)])
+
+ self.assertEqual(
+ output.strip(), 'already connected to localhost:{}'.format(port))
def main():
random.seed(0)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 37b56e2..2867d38 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -77,7 +77,15 @@
Stop();
}
+static void AssumeLocked(std::mutex& mutex) ASSERT_CAPABILITY(mutex) {}
+
void BlockingConnectionAdapter::Start() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (started_) {
+ LOG(FATAL) << "BlockingConnectionAdapter(" << this->transport_name_
+ << "): started multiple times";
+ }
+
read_thread_ = std::thread([this]() {
LOG(INFO) << this->transport_name_ << ": read thread spawning";
while (true) {
@@ -95,7 +103,11 @@
LOG(INFO) << this->transport_name_ << ": write thread spawning";
while (true) {
std::unique_lock<std::mutex> lock(mutex_);
- cv_.wait(lock, [this]() { return this->stopped_ || !this->write_queue_.empty(); });
+ cv_.wait(lock, [this]() REQUIRES(mutex_) {
+ return this->stopped_ || !this->write_queue_.empty();
+ });
+
+ AssumeLocked(mutex_);
if (this->stopped_) {
return;
@@ -111,25 +123,44 @@
}
std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "write failed"); });
});
+
+ started_ = true;
}
void BlockingConnectionAdapter::Stop() {
- std::unique_lock<std::mutex> lock(mutex_);
- if (stopped_) {
- LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): already stopped";
- return;
- }
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (!started_) {
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): not started";
+ return;
+ }
- stopped_ = true;
- lock.unlock();
+ if (stopped_) {
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_
+ << "): already stopped";
+ return;
+ }
+
+ stopped_ = true;
+ }
LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): stopping";
this->underlying_->Close();
-
this->cv_.notify_one();
- read_thread_.join();
- write_thread_.join();
+
+ // Move the threads out into locals with the lock taken, and then unlock to let them exit.
+ std::thread read_thread;
+ std::thread write_thread;
+
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ read_thread = std::move(read_thread_);
+ write_thread = std::move(write_thread_);
+ }
+
+ read_thread.join();
+ write_thread.join();
LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): stopped";
std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "requested stop"); });
@@ -137,7 +168,7 @@
bool BlockingConnectionAdapter::Write(std::unique_ptr<apacket> packet) {
{
- std::unique_lock<std::mutex> lock(this->mutex_);
+ std::lock_guard<std::mutex> lock(this->mutex_);
write_queue_.emplace_back(std::move(packet));
}
diff --git a/adb/transport.h b/adb/transport.h
index 8b71e34..d18c362 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -30,6 +30,7 @@
#include <thread>
#include <unordered_set>
+#include <android-base/thread_annotations.h>
#include <openssl/rsa.h>
#include "adb.h"
@@ -121,13 +122,14 @@
virtual void Start() override final;
virtual void Stop() override final;
- bool stopped_ = false;
+ bool started_ GUARDED_BY(mutex_) = false;
+ bool stopped_ GUARDED_BY(mutex_) = false;
std::unique_ptr<BlockingConnection> underlying_;
- std::thread read_thread_;
- std::thread write_thread_;
+ std::thread read_thread_ GUARDED_BY(mutex_);
+ std::thread write_thread_ GUARDED_BY(mutex_);
- std::deque<std::unique_ptr<apacket>> write_queue_;
+ std::deque<std::unique_ptr<apacket>> write_queue_ GUARDED_BY(mutex_);
std::mutex mutex_;
std::condition_variable cv_;
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
new file mode 100644
index 0000000..b91ec72
--- /dev/null
+++ b/fastboot/fastboot.bash
@@ -0,0 +1,182 @@
+# /* vim: set ai ts=4 ft=sh: */
+#
+# Copyright 2017, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+_fastboot() {
+ if ! type -t "$1" >/dev/null; then
+ return
+ fi
+
+ if type -t _init_completion >/dev/null; then
+ _init_completion || return
+ fi
+
+ local where i cur serial
+ COMPREPLY=()
+
+ serial="${ANDROID_SERIAL:-none}"
+ where=OPTIONS
+ for ((i=1; i <= COMP_CWORD; i++)); do
+ cur="${COMP_WORDS[i]}"
+ case "${cur}" in
+ -s)
+ where=OPT_SERIAL
+ ;;
+ --slot)
+ where=OPT_SLOT
+ ;;
+ -*)
+ where=OPTIONS
+ ;;
+ *)
+ if [[ $where == OPT_SERIAL ]]; then
+ where=OPT_SERIAL_ARG
+ serial=${cur}
+ elif [[ $where == OPT_SLOT ]]; then
+ where=OPT_SLOT_ARG
+ else
+ where=COMMAND
+ break
+ fi
+ ;;
+ esac
+ done
+
+ if [[ $where == COMMAND && $i -ge $COMP_CWORD ]]; then
+ where=OPTIONS
+ fi
+
+ OPTIONS="-a -c --disable-verification --disable-verity -h --help -s --set_active --skip-secondary --skip-reboot --slot -u --version -w"
+ COMMAND="continue devices erase flash flashall flashing format getvar get_staged help oem reboot stage update"
+
+ case $where in
+ OPTIONS|OPT_SERIAL)
+ COMPREPLY=( $(compgen -W "$OPTIONS $COMMAND" -- "$cur") )
+ ;;
+ OPT_SERIAL_ARG)
+ local devices=$(command fastboot devices 2> /dev/null | awk '{ print $1 }')
+ COMPREPLY=( $(compgen -W "${devices}" -- ${cur}) )
+ ;;
+ OPT_SLOT_ARG)
+ local slots="a all b other"
+ COMPREPLY=( $(compgen -W "${slots}" -- ${cur}) )
+ ;;
+ COMMAND)
+ if [[ $i -eq $COMP_CWORD ]]; then
+ COMPREPLY=( $(compgen -W "$COMMAND" -- "$cur") )
+ else
+ i=$((i+1))
+ case "${cur}" in
+ flash)
+ _fastboot_cmd_flash "$serial" $i
+ ;;
+ reboot)
+ if [[ $COMP_CWORD == $i ]]; then
+ args="bootloader emergency"
+ COMPREPLY=( $(compgen -W "${args}" -- "${COMP_WORDS[i]}") )
+ fi
+ ;;
+ update)
+ _fastboot_cmd_update "$serial" $i
+ ;;
+ esac
+ fi
+ ;;
+ esac
+
+ return 0
+}
+
+_fastboot_cmd_flash() {
+ local serial i cur
+ local partitions
+
+ serial=$1
+ i=$2
+
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ if [[ $i -eq $COMP_CWORD ]]; then
+ partitions="boot bootloader dtbo modem radio recovery system vbmeta vendor"
+ COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
+ else
+ _fastboot_util_complete_local_file "${cur}" '!*.img'
+ fi
+}
+
+_fastboot_cmd_update() {
+ local serial i cur
+
+ serial=$1
+ i=$2
+
+ cur="${COMP_WORDS[COMP_CWORD]}"
+
+ _fastboot_util_complete_local_file "${cur}" '!*.zip'
+}
+
+_fastboot_util_complete_local_file() {
+ local file xspec i j IFS=$'\n'
+ local -a dirs files
+
+ file=$1
+ xspec=$2
+
+ # Since we're probably doing file completion here, don't add a space after.
+ if [[ $(type -t compopt) = "builtin" ]]; then
+ compopt -o plusdirs
+ if [[ "${xspec}" == "" ]]; then
+ COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
+ else
+ compopt +o filenames
+ COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -X "${xspec}" -- "${cur}") )
+ fi
+ else
+ # Work-around for shells with no compopt
+
+ dirs=( $(compgen -d -- "${cur}" ) )
+
+ if [[ "${xspec}" == "" ]]; then
+ files=( ${COMPREPLY[@]:-} $(compgen -f -- "${cur}") )
+ else
+ files=( ${COMPREPLY[@]:-} $(compgen -f -X "${xspec}" -- "${cur}") )
+ fi
+
+ COMPREPLY=( $(
+ for i in "${files[@]}"; do
+ local skip=
+ for j in "${dirs[@]}"; do
+ if [[ $i == $j ]]; then
+ skip=1
+ break
+ fi
+ done
+ [[ -n $skip ]] || printf "%s\n" "$i"
+ done
+ ))
+
+ COMPREPLY=( ${COMPREPLY[@]:-} $(
+ for i in "${dirs[@]}"; do
+ printf "%s/\n" "$i"
+ done
+ ))
+ fi
+}
+
+if [[ $(type -t compopt) = "builtin" ]]; then
+ complete -F _fastboot fastboot
+else
+ complete -o nospace -F _fastboot fastboot
+fi
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 85a593f..63a6839 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -114,8 +114,19 @@
}
std::string size_str = std::to_string(dev_sz / 4096);
+ // clang-format off
const char* const args[] = {
- "/system/bin/make_f2fs", "-f", "-O", "encrypt", fs_blkdev, size_str.c_str(), nullptr};
+ "/system/bin/make_f2fs",
+ "-d1",
+ "-f",
+ "-O", "encrypt",
+ "-O", "quota",
+ "-w", "4096",
+ fs_blkdev,
+ size_str.c_str(),
+ nullptr
+ };
+ // clang-format on
return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
LOG_KLOG, true, nullptr, nullptr, 0);
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 9c6fed4..d2d6ab8 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -149,13 +149,12 @@
}
// Create the process memory from the stack data.
- uint64_t size = stack.end - stack.start;
- unwindstack::MemoryBuffer* memory = new unwindstack::MemoryBuffer;
- memory->Resize(size);
- memcpy(memory->GetPtr(0), stack.data, size);
- std::shared_ptr<unwindstack::Memory> shared_memory(memory);
-
- process_memory_.reset(new unwindstack::MemoryRange(shared_memory, 0, size, stack.start));
+ if (memory_ == nullptr) {
+ memory_ = new unwindstack::MemoryOfflineBuffer(stack.data, stack.start, stack.end);
+ process_memory_.reset(memory_);
+ } else {
+ memory_->Reset(stack.data, stack.start, stack.end);
+ }
return true;
}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index ec0d9c1..039f4a2 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -79,6 +79,9 @@
bool Build(const std::vector<backtrace_map_t>& maps);
bool CreateProcessMemory(const backtrace_stackinfo_t& stack);
+
+ private:
+ unwindstack::MemoryOfflineBuffer* memory_ = nullptr;
};
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 0d43f87..6868f18 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -155,6 +155,7 @@
"tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
"tests/MemoryLocalTest.cpp",
+ "tests/MemoryOfflineBufferTest.cpp",
"tests/MemoryOfflineTest.cpp",
"tests/MemoryRangeTest.cpp",
"tests/MemoryRemoteTest.cpp",
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index d4ba680..beb2aad 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -345,6 +345,25 @@
return memory_->Read(addr, dst, size);
}
+MemoryOfflineBuffer::MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end)
+ : data_(data), start_(start), end_(end) {}
+
+void MemoryOfflineBuffer::Reset(const uint8_t* data, uint64_t start, uint64_t end) {
+ data_ = data;
+ start_ = start;
+ end_ = end;
+}
+
+size_t MemoryOfflineBuffer::Read(uint64_t addr, void* dst, size_t size) {
+ if (addr < start_ || addr >= end_) {
+ return 0;
+ }
+
+ size_t read_length = std::min(size, static_cast<size_t>(end_ - addr));
+ memcpy(dst, &data_[addr - start_], read_length);
+ return read_length;
+}
+
MemoryOfflineParts::~MemoryOfflineParts() {
for (auto memory : memories_) {
delete memory;
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 19bce04..c0c07f4 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -151,6 +151,21 @@
std::unique_ptr<MemoryRange> memory_;
};
+class MemoryOfflineBuffer : public Memory {
+ public:
+ MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end);
+ virtual ~MemoryOfflineBuffer() = default;
+
+ void Reset(const uint8_t* data, uint64_t start, uint64_t end);
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ const uint8_t* data_;
+ uint64_t start_;
+ uint64_t end_;
+};
+
class MemoryOfflineParts : public Memory {
public:
MemoryOfflineParts() = default;
diff --git a/libunwindstack/tests/MemoryOfflineBufferTest.cpp b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
new file mode 100644
index 0000000..f022884
--- /dev/null
+++ b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "LogFake.h"
+
+namespace unwindstack {
+
+class MemoryOfflineBufferTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.reset(new MemoryOfflineBuffer(buffer_.data(), kStart, kEnd));
+ }
+
+ static void SetUpTestCase() {
+ buffer_.resize(kLength);
+ for (size_t i = 0; i < kLength; i++) {
+ buffer_[i] = i % 189;
+ }
+ }
+
+ std::unique_ptr<MemoryOfflineBuffer> memory_;
+
+ static constexpr size_t kLength = 0x2000;
+ static constexpr uint64_t kStart = 0x1000;
+ static constexpr uint64_t kEnd = kStart + kLength;
+ static std::vector<uint8_t> buffer_;
+};
+
+std::vector<uint8_t> MemoryOfflineBufferTest::buffer_;
+
+static void VerifyBuffer(uint8_t* buffer, size_t start_value, size_t length) {
+ for (size_t i = 0; i < length; i++) {
+ ASSERT_EQ((start_value + i) % 189, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryOfflineBufferTest, read_out_of_bounds) {
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_FALSE(memory_->ReadFully(0, buffer.data(), 1));
+ ASSERT_FALSE(memory_->ReadFully(0xfff, buffer.data(), 1));
+ ASSERT_FALSE(memory_->ReadFully(0xfff, buffer.data(), 2));
+ ASSERT_FALSE(memory_->ReadFully(0x3000, buffer.data(), 1));
+ ASSERT_FALSE(memory_->ReadFully(0x3001, buffer.data(), 1));
+}
+
+TEST_F(MemoryOfflineBufferTest, read) {
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_TRUE(memory_->ReadFully(kStart, buffer.data(), 10));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0, 10));
+
+ ASSERT_TRUE(memory_->ReadFully(kStart + 555, buffer.data(), 40));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 555, 40));
+
+ ASSERT_TRUE(memory_->ReadFully(kStart + kLength - 105, buffer.data(), 105));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), kLength - 105, 105));
+}
+
+TEST_F(MemoryOfflineBufferTest, read_past_end) {
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_EQ(100U, memory_->Read(kStart + kLength - 100, buffer.data(), buffer.size()));
+ VerifyBuffer(buffer.data(), kLength - 100, 100);
+}
+
+TEST_F(MemoryOfflineBufferTest, read_after_reset) {
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_TRUE(memory_->ReadFully(kStart, buffer.data(), 100));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0, 100));
+
+ memory_->Reset(&buffer_[10], 0x12000, 0x13000);
+ ASSERT_TRUE(memory_->ReadFully(0x12000, buffer.data(), 100));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 10, 100));
+
+ ASSERT_EQ(50U, memory_->Read(0x13000 - 50, buffer.data(), buffer.size()));
+ ASSERT_NO_FATAL_FAILURE(VerifyBuffer(buffer.data(), 0x1000 - 50 + 10, 50));
+}
+
+} // namespace unwindstack